How to make a placementment tool for a model? - autodesk-forge

I followed tutorial from this blog and want to create a tool like TransformationExtension.
I found that the model I selected was not immediately centered on the cursor and there was a gap when dragging it.
document.onmousemove = (event) => {
if (!event.ctrlKey) return;
let res = this.viewer.impl.hitTest(
event.clientX,
event.clientY,
true,
null,
[this.viewer.model.getModelId()]
);
let pt = null;
if (res) {
pt = res.intersectPoint;
} else {
pt = viewer.impl.intersectGround(event.clientX, event.clientY);
}
let tr = this.selectedModel.getPlacementTransform();
tr.elements[12] = pt.x;
tr.elements[13] = pt.y;
tr.elements[14] = pt.z;
this.selectedModel.setPlacementTransform(tr);
this.viewer.impl.invalidate(true, true, true);
};

As #AlexAR mentioned, when finding the cursor position relative to a specific DOM element using the clientX and clientY values, you have to remember subtracting the element's client bounding rect: Find mouse position relative to element.
Btw. I've recently built another sample Forge app that can be used to drag&drop new models into the viewer that you might find helpful: https://github.com/petrbroz/forge-assembly-configurator. The main drag&drop logic is implemented in the setupDragDrop function.

Related

Implementing Three.js SSAOPass in AFrame

I was able to successfully integrate Threejs Effect composer in aframe as a component by exporting everything as THREE.Effectcomposer, THREE.SSAOPass etc. and adding the effect inside a aframe component and i tweaked the AFrame renderer to update the effects in the scene. OutlinePass from threejs worked fine in this code but SSAO is not working and i don't get any errors. Please someone help me figure out the problem. the code for SSAOPass looks like this
AFRAME.registerComponent('ssao', {
init: function () {
this.el.addEventListener('that', evt => this.onEnter());
this.el.addEventListener('mouseleave', evt => this.onLeave());
setTimeout(() => this.el.emit("that"), 2000);
},
onEnter: function () {
const scene = this.el.sceneEl.object3D;
const camera = this.el.sceneEl.camera;
const renderer = this.el.sceneEl.renderer;
const render = renderer.render;
const composer = new THREE.EffectComposer(renderer);
//let renderPass = new THREE.RenderPass(scene, camera);
//let outlinePass = new THREE.OutlinePass(new THREE.Vector2(window.innerWidth, window.innerHeight), scene, camera);
const ssaoPass = new THREE.SSAOPass( scene, camera, window.innerWidth, window.innerHeight );
//composer.addPass(renderPass);
//composer.addPass(outlinePass);
ssaoPass.kernelRadius = 16;
composer.addPass( ssaoPass );
// let objects = [];
// this.el.object3D.traverse(node => {
// if (!node.isMesh) return;
// objects.push(node);
// });
// outlinePass.selectedObjects = objects;
// outlinePass.renderToScreen = true;
// outlinePass.edgeStrength = this.data.strength;
// outlinePass.edgeGlow = this.data.glow;
// outlinePass.visibleEdgeColor.set(this.data.color);
// HACK the AFRAME render method (a bit ugly)
const clock = new THREE.Clock();
this.originalRenderMethod = render;
let calledByComposer = false;
renderer.render = function () {
if (calledByComposer) {
render.apply(renderer, arguments);
} else {
calledByComposer = true;
composer.render(clock.getDelta());
calledByComposer = false;
}
};
},
onLeave: function () {
this.el.sceneEl.renderer.render = this.originalRenderMethod;
},
remove: function () {
this.onLeave();
}
});
I have also created a glitch project which i am sharing here. Please feel free to join and collaborate in my project
Edit link: https://glitch.com/edit/#!/accessible-torpid-partridge
Site link:https://accessible-torpid-partridge.glitch.me
Thanks in advance
The code is correct, all you need is to tweak the exposed SSAOShader uniforms: SSAOPass.kernelRadius, SSAOPass.minDistance, SSAOPass.maxDistance - like in the Three.js example.
Keep in mind - the scale in the example is huge, so the values will need to be different in a default aframe scene.
It's a good idea to be able to dynamically update a component (via setAttribute() if you properly handle updates), so you can see what's going on in realtime. Something like I did here - SSAO in a-frame (also based on Don McCurdys gist.
I've used some basic HTML elements, most threejs examples use dat.GUI - it is made for demo / debug tweaks.

Flutter update element create by ui.platformViewRegistry.registerViewFactory class

I am working with Google Map for flutter Website. As most tutorial said that, the map is displayed through ui.platformViewRegistry.registerViewFactory
below is the code example. As you see, i create the widget with the variable and tried to give the _lat and _lng from outside. But after i tried to give the value, the ui.platformViewRegistry.registerViewFactory is not triggered.
I just realize that the ui.platformViewRegistry.registerViewFactory objected to create the element so when the element was created, it will not executed again, but I cannot access the element via document.getElementById('map-canvas') either.
Anyone have idea about this?
Widget getMap(double _lat, double _lng) {
String htmlId = "map-canvas";
ui.platformViewRegistry.registerViewFactory(htmlId, (int viewId) {
// final myLatLng = LatLng(-25.363882, 131.044922);
final mapOptions = new MapOptions()
..zoom = 8
..center = new LatLng(_lat, _lng)
..mapTypeControl = false;
final elem = DivElement()
..id = htmlId
..style.width = "100%"
..style.height = "100%"
..style.border = "none";
final map = new GMap(elem, mapOptions);
Marker(MarkerOptions()
..position = LatLng(_lat, _lng)
..map = map
..title = 'Green Energy');
return elem;
});
return HtmlElementView(
viewType: htmlId,
);
}
I found two ways to solve this problem.
It will update the whole view when you change the htmlId and reload.
Either determine how the htmlId should change or give it a new random number with every reload. String htmlId = Random().nextInt(1000).toString();
The disadvantage here is that you always reload the whole map.
This is the better solution as you dont have to reload the complete map. I also had the problem that i could change values inside the ui.platformViewRegistry.registerViewFactory function with data i give to the getMap widget.
So the first part of this solution is to create a global variable that you use inside the ui.platformViewRegistry.registerViewFactory function but update outside.
The second part is to use listener functions inside the ui.platformViewRegistry.registerViewFactory. For example map.onClick.listen((mapsMouseEvent) {}); or map.onZoomChanged.listen((_) {}); or a Stream ANY_STREAM_CONTROLLER.stream.listen((data) {}).
If you would like to add new markers when you zoom out you could do it something like this
ui.platformViewRegistry.registerViewFactory(htmlId, (int viewId) {
map.onZoomChanged.listen((_) {
List<LatLng> marker_list = list_of_markers(map.zoom, map.center);
//Function that gives you a list of markers depending on your zoom level and center
marker_list.forEach((latlng){
Marker marker = Marker(MarkerOptions()
..position = latlng
..map = map
..title = myLatlng.toString()
..label = myLatlng.toString());
});
});
}
I hope the solution helps you

Cocos2d-x JS getting variables from scene or layer

I'm currently having issues with the following problem.
I'm trying to access the layer inside the scene and thereby the elements that I set in that layer. In this case I want access to the conv_label in the Layer to set the text.
I'm doing this via a ConversationClass which extends cc.Class.
When I try to access the layer via the variable it doesn't work or with getChildByName or Tag it doesn't work (value is always null).
This is a method inside the ConversationClass, I can console log the currentscene without any problem, but any variable I set doesn't appear in the current scene. in this case the name was "conv_layer", I can access the children by just using array calls, but that's not really a good way it seems and quite confusing.
This I tried:
currentscene.children[0].children[3] will give me the right element.
currentscene.conv_layer.getChildByName("text") says conv_layer does not exist
currentscene.children[0].getChildByName("text") returns null
Does anyone know how to solve this issue or can tell me where my thinking went wrong?
Not sure of it matters, but I call the scene (for now) in the following way.
cc.LoaderScene.preload(conversation_load, function() {
cc.director.runScene(new ConversationScene());
this.startGame();
}, this);
This is where I want access
startConversation: function(conversation) {
this._conversationObject = conversation;
this._currentScene = cc.director.getRunningScene();
console.log(this._currentScene); // Shows current scene object (doesn't have conv_layer property)
if(scene !== null)
this._currentConversationLayer = scene.conv_layer; // Returns null
},
This is my scene:
var ConversationScene = cc.Scene.extend({
conv_layer: null,
onEnter: function() {
this._super();
this.conv_layer = new ConversationLayer();
this.conv_layer.setName('conversation');
this.conv_layer.setTag(1);
this.addChild(this.conv_layer);
}
});
and this is my layer:
var ConversationLayer = cc.Layer.extend({
ctor: function() {
this._super();
this.init();
},
init: function() {
this._super();
var winSize = cc.director.getWinSize();
GD.current_conversation = conversation1;
this.background = new cc.Sprite();
this.background.anchorX = 0.5;
this.background.anchorY = 0.5;
this.background.setPositionX(winSize.width / 2);
this.background.setPositionY(winSize.height / 2);
this.addChild(this.background);
this.girl = new cc.Sprite();
this.girl.anchorX = 0;
this.girl.anchorY = 0;
this.addChild(this.girl);
this.text_background = new cc.Sprite(resources.conversation_interactive_assets, cc.rect(0,0,1920/GD.options.scale,320/GD.options.scale));
this.text_background.anchorX = 0.5;
this.text_background.anchorY = 0;
this.text_background.setPositionX(winSize.width / 2);
this.text_background.setPositionY(0);
this.addChild(this.text_background);
// Left
this.conv_label = new cc.LabelBMFont("", resources.font);
this.conv_label.anchorX = 0;
this.conv_label.anchorY = 0;
this.conv_label.setPositionX((winSize.width - this.text_background.width) / 2 + 20);
this.conv_label.setPositionY(this.text_background.height - 30);
this.conv_label.color = cc.color.BLACK;
this.conv_label.setName('text');
this.addChild(this.conv_label);
}
});
The issue was the loading order of everything.
It seems scenes are loaded in async, so the next function would be called but no layer would exist at that point.
Solved it by doing creation inside the class itself and calling onSceneEnter.

Update d3 graph from dynamic JSON (onStateChange)

I am working with the Vaadin framework (https://vaadin.com/home) integrating d3.js to my java code (analogous to http://www.rapidpm.org/2013/10/using-javascript-libraries-d3-in-vaadin.html). Basically, this Javascript code is bound to a Java class and reacts to a change of state in said Java class.
==================================================================================
So, when the state of the java diagram-class changes, this.conStateChange-method is called: javascript fetches a new String from the java class (var string = this.getState().string;), parses it to JSON and renders a graph.
A button click will trigger the state change by changing the String in the java class (!), thereby triggering the described method and rendering the new graph from the new JSON-parsed-String.
...THEORETICALLY... however, in reality, if I click that button once the graph disappears. Only if I click the button again, the new graph is rendered.
/*not so interesting code
var diagramElement = this.getElement();
var width = 960, height = 500;
var force = d3.layout.force().size([ width, height ])
.charge(-400)
.linkDistance(40)
.on("tick", tick);
var drag = force.drag()
.on("dragstart", dragstart);
var svg = d3.select(diagramElement)
.append("svg")
.attr("width", width)
.attr("height", height);
var link = svg.selectAll(".link"),
node = svg.selectAll(".node");*/
/*interesting code*/
this.onStateChange = function() {
link.remove(); //delete old graph
node.remove(); //delete old graph
var string = this.getState().string;
graph = JSON.parse(string);
force.nodes(graph.nodes).links(graph.links).start();
link = link.data(graph.links)
.enter().append("line")
.attr("class", "link");
node = node.data(graph.nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", 12)
.on("dblclick", dblclick)
.call(drag);
};
So whats wrong with this (indeed not very elegant) code, and how can I achieve that a single click will [1] remove the graph displayed and [2] render a new graph?
Thanks in advance...
I'm not sure if you have found a solution and I'm still a noob in d3 but I would like to suggest as this solution helped me. However, my force layout derived its data from a csv and not JSON not sure if it makes much difference.
I included " graph = { "nodes": [], "links": [] }; " in the "this.onStateChange = function() {" as this will help fill up the arrays with new data. Hope it helps!

Disable zoom with Openlayers

i'm using OpenLayers to display custom OSM maps on my website.
I've some points to respect: the map have to be fix (meaning that we can't drag it or zoom it).
I have a problem with the zoom, i can't manage to disable zoom with the mouse. Does anyone has a tip?
map = new OpenLayers.Map('map');
map.events.remove("move");
map.events.remove("movestart");
map.events.remove("moveend");
map.events.remove("zoomend");
map.events.remove("mouseover");
map.events.remove("mouseout");
map.events.remove("mousemove");
map.events.remove("zoomstart");
var nav = new OpenLayers.Control.Navigation({
defaultDblClick: function(event) { return ; }
});
map[index].addControl(nav);
Also, if someone has a tip to remove all Navigation events easier than that, it would be greatly appreciated.
Disable the default controls on your map by passing an empty array:
var map = new OpenLayers.Map('map', { controls: [] });
For OpenLayers3 the interaction array also needs to be empty.
var map = new ol.Map({
controls: [],
interactions: []
});
Simplifying approach of Mahdi results in
var i, l, c = map.getControlsBy( "zoomWheelEnabled", true );
for ( i = 0, l = c.length; i < l; i++ ) {
c[i].disableZoomWheel();
}
This way disabling zoom on mouse wheel doesn't require to customize options on constructing map e.g. by creating map without any control (though this was somewhat requested by Lght). In addition re-enabling zoom works equivalently.
In addition, by searching controls matching enabled property zoomWheelEnabled rather than class name it's supporting custom controls derived from OpenLayers.Control.Navigation.
You can do the following also:
map = new OpenLayers.Map({
// options here ...
}
var Navigation = new OpenLayers.Control.Navigation({
'zoomWheelEnabled': false,
'defaultDblClick': function ( event ) {
return;
}
});
map.addControl(Navigation);
var NavigationControls = map.getControlsByClass('OpenLayers.Control.Navigation')
, i;
for ( i = 0; i < NavigationControls.length; i++ ) {
NavigationControls[i].disableZoomWheel();
}
Found here.
For other options like disable dragging, you can take a look at the documentation and customize the above code.
Here is another easy way to restrict the zoom event based on some logic. Because OpenLayers doesnt provide a 'beforezoom'
map.zoomToProxy = map.zoomTo;
map.zoomTo = function (zoom,xy){
// if you want zoom to go through call
map.zoomToProxy(zoom,xy);
//else do nothing and map wont zoom
};
How this works:
For any kind of zooming activity, OpenLayers API ultimately calls the function called zoomTo. So before overriding it, we copy that function to a new function called 'zoomToProxy'. The we override it and add our conditional zoom logic. If we want the zoom to happen we just call new proxy function :)
You could reset the controls array and then add the Zoom and TouchNavigation to it.
var map_controls = [];
map_controls.push( new OpenLayers.Control.Zoom() );
map_controls.push( new OpenLayers.Control.TouchNavigation() );
var map = new OpenLayers.Map({
div: "map",
controls: map_controls
});
Hope it helps ! :)