ArcGIS Runtime : How to create 3d cube in SceneView with Z? - arcgis-runtime

I want to draw 3d cube in SceneView by interactive operation.
The functions I hope to achieve are:
Click multiple points (x1, y1,0), (x2, y2, 0), (x3, y3, 0), (x4, y4, 0) on SceneView through interaction.
Enter the height h through the pop-up window.
Draw a cube with these points:
(x1,y1,0),(x2,y2,0),(x3,y3,0),(x4,y4,0),(x1,y1,h),(x2,y2,h),(x3 ,y3,h),(x4,y4,h)
I tried this:
var centerPoint = new MapPoint(vm.X, vm.Y, vm.Z, onMapLocation.SpatialReference);
SimpleMarkerSceneSymbol symbol = SimpleMarkerSceneSymbol.CreateCube(System.Drawing.Color.DarkSeaGreen, 1, SceneSymbolAnchorPosition.Center);
symbol.Heading = vm.Heading;
symbol.Height = vm.Height;
symbol.Width = vm.Width;
symbol.Depth = vm.Depth;
// Create the graphic from the geometry and the symbol.
Graphic item = new Graphic(centerPoint, symbol);
// Add the graphic to the overlay.
graphicOverlay.Graphics.Add(item);
It works,but I can only get one point from Geometry. I want to get all vertices of my cube.
Then I tried to draw a polygon without Z , and set Renderer to display:
// Create a new simple line symbol for the feature layer
SimpleLineSymbol mySimpleLineSymbol = new SimpleLineSymbol(SimpleLineSymbolStyle.Solid, System.Drawing.Color.Black, 1);
// Create a new simple fill symbol for the feature layer
SimpleFillSymbol mysimpleFillSymbol = new SimpleFillSymbol(SimpleFillSymbolStyle.Solid, System.Drawing.Color.WhiteSmoke, mySimpleLineSymbol);
// Create a new simple renderer for the feature layer
SimpleRenderer mySimpleRenderer = new SimpleRenderer(mysimpleFillSymbol);
// Get the scene properties from the simple renderer
RendererSceneProperties myRendererSceneProperties = mySimpleRenderer.SceneProperties;
// Set the extrusion mode for the scene properties
myRendererSceneProperties.ExtrusionMode = ExtrusionMode.AbsoluteHeight;
// Set the initial extrusion expression
myRendererSceneProperties.ExtrusionExpression = "[Z]";
// Set the feature layer's renderer to the define simple renderer
featureLayer.Renderer = mySimpleRenderer;
In my featureLayer, it has a feature Z, so it works.
But this does not meet my requirements, I want to draw the cube in real time instead of reading the data.
I don't know how to achieve...
A .NET solution is the best, and other languages are also acceptable.
Finally, please forgive my poor English :(

Finally, I used Render to solve the problem, but I was not satisfied with this solution.
This is my completed result:
https://www.cnblogs.com/Lulus/p/13948464.html

Related

ThreeJS apply properties from one camera to another camera

In my webapp I'm using ThreeJS scenes in different modals/popups/dialogs with different width/height ratios.
Furthermore, I want to use multiple user defined camera settings (rotation, position, lookAt etc.) among these different scenes.
Therefore, I save the camera object via camera.toJSON() when the user clicks a capture camera settings button.
(Before I did this, I saved just the object camera, but unfortunately these objects are quite big and slow down the performance while multiple camera objects get stored. Nevertheless, this approach worked, since I was able to copy all the desired values between the saved camera object and the currently used camera [e.g. current_camera.position.x=saved_camera.position.x and so on])
In every scene I want now to use the saved properties I tried the following:
let m = new THREE.Matrix4();
m.fromArray(saved_camera.object.matrix);
current_camera.applyMatrix(m)
current_camera.updateMatrix();
Unfortunately this doesn't work.
"normal" camera object
camera.toJSON() object
If you're comfortable using matrices, then you can turn off the matrix auto-update that three.js does during the render process, and keep the world matrix up-to-date yourself. (This includes any time you change the camera's orientation, so keep that in mind if you're using some form of mouse interaction to control the camera angle.)
First, turn off automatic matrix updating for your camera by setting the autoUpdateMatrix property to false. You can still use the convenience properties (position, rotation, scale), but you'll have to manually update the world matrix by calling camera.updateMatrixWorld(true);.
Finally, when you're ready to restore a particular camera orientation, simply copy the matrix values using the matrixWorld's copy method.
var origin = new THREE.Vector3();
var theCamera = new THREE.PerspectiveCamera(35, 1, 1, 1000);
theCamera.autoUpdateMatrix = false; // turn off auto-update
theCamera.position.set(10, 10, 10);
theCamera.lookAt(origin);
theCamera.updateMatrixWorld(true); // manually update the matrix!
console.log("Camera original matrix: ", theCamera.matrixWorld.elements.toString());
var saveMatrix = new THREE.Matrix4();
saveMatrix.copy(theCamera.matrixWorld);
// saveMatrix now contains the current value of theCamera.matrixWorld
theCamera.position.set(50, -50, 75);
theCamera.lookAt(origin);
theCamera.updateMatrixWorld(true); // manually update the matrix!
console.log("Camera moved matrix: ", theCamera.matrixWorld.elements.toString());
// theCamera.matrixWorld now holds a value that's different from saveMatrix.
theCamera.matrixWorld.copy(saveMatrix);
// Don't upate the matrix, because you just SET it.
console.log("Camera moved matrix: ", theCamera.matrixWorld.elements.toString());
// theCamera.matrixWorld once again contains the saved value.
<script src="https://threejs.org/build/three.js"></script>
Edit to address OrbitControls:
It looks like OrbitControls uses the convenience properties, rather than gathering the information from the matrix. As such, when you restore a camera position, you'll also need to restore those properties. This is easily done by using decompose on the matrix, and copying the resulting values into the appropriate properties:
var d = new THREE.Vector3(),
q = new THREE.Quaternion(),
s = new THREE.Vector3();
camera.matrixWorld.decompose( d, q, s );
camera.position.copy( d );
camera.quaternion.copy( q );
camera.scale.copy( s );

Is there a way I can create a Path or Curve to use for TubeGeomety(path,...) from an existing geometry's points/vertices array?

I'm very new to both three.js & to js in general.
1st I select a polyHedron geometry with a dat.gui checkbox
which renders say a tetrahedron. these selections work.
I also have a dat.gui checkbox to either phongfill or wireframe render.
I initially wanted just a wireframe type mesh but not with all of the internal triangles. I found the edgesgeometry() function which draws pretty much what I want(hard edges only). there is however a known issue with linewidth not working in windows anymore. all lines drawn as strokeweight/width 1.
I'd like to use tubeGeometry() to draw tubes of whatever radius as opposed to 1weight lines. I know I'll have to draw something such as a sphere at/over the connection vertices for it to not look ridiculous.
geo = new THREE.TetrahedronBufferGeometry(controls0.Radius,controls0.Detail);
...
egeo = new THREE.EdgesGeometry( geo );
lmat = new THREE.LineBasicMaterial({ color: 0x0099ff, linewidth: 4 });
ph = new THREE.LineSegments( egeo, lmat );
scene.add(ph);
....
playing around in the console I found some geometry/bufferGeomery arrays that are likely the vertices/indices of my selected X-hedron as their sizes change with type(tetra/icosa etc) selection & detail increase/decrease:
//p = dome.geometry.attributes.uv.array;
p = egeo.attributes.position.array
//p = geo.attributes.uv.array
...
var path = new THREE.Curve();
path.getPoint = function (t) {
// trace the arc as t ranges from 0 to 1
var segment = (0 - Math.PI*2) *t;
return new THREE.Vector3( Math.cos(segment), Math.sin(segment), 0);
};
var geomet = new THREE.TubeBufferGeometry( path, 10, 0.2, 12, false );
var mesh = new THREE.Mesh( geomet, mat );
scene.add( mesh );
from above the tubeGeometry() draws fine separately as well but with the "path" made by that curve example. How can I use the vertices from my tetrahedron for example to create that "path" to pass to tubegeometry() ?
maybe a function that creates "segment vectors" from the vertices ?
I think it needs other properties of curve/path as well ?
I'm quite stuck at this point.
ANY Help, suggestions or examples would be greatly appreciated !
thanks.
You can try to create a TubeGeometry for each edge. Generate a LineCurve3 as the input path. Use the vertices of the edge as the start and end vector for the line.
Consider to use something like "triangulated lines" as an alternative in order to visualize the wireframe of a mesh with a linewidth greater than 1. With the next release of three.js(R91) there are new line primitives for this. Demo:
https://rawgit.com/mrdoob/three.js/dev/examples/webgl_lines_fat.html
This approach is much more performant than drawing a bunch of meshes with a TubeGeometry.

Create a CubeGrid in Three.js

In Three.js there is a GridHelper that allows you to create a plane grid easily, just like this:
var grid = new THREE.GridHelper(size, divisions, colorCenterLine, colorGrid);
You can see a result example here.
What I want to do is to create a box/cube grid, exactly like this:
I need to have a cube, because later I'll want to know, for example, if an object it's inside the cube (dynamically).
I wasn't able to find a helper that do what I need, so my idea is to take the GridHelper source code and use a BoxBufferGeometry instead of a BufferGeometry, but I don't even know if that's possible. I want to add that I do not have much knowledge in the field of 3D graphics, I'm just starting.
I'd love to hear you thoughts about this: I'm going in the right direction? How would you approach this problem?
I've finally used the approach that #WestLangley proposed:
var createBoxGrid = function (base, height, translateY, divisions, color) {
boxGrid = new THREE.Group();
boxGrid.name = "BoxGrid";
var box3 = new THREE.Box3(new THREE.Vector3(-base / 2, 0, -base / 2), new THREE.Vector3(base / 2, height, base / 2));
var box3Helper = new THREE.Box3Helper(box3, color);
var gridHelper = new THREE.GridHelper(base, divisions, color, color);
boxGrid.add(box3Helper);
boxGrid.add(gridHelper);
boxGrid.translateY(translateY);
return boxGrid;
};

How to set camera coordinates to object in three.js? Using example "webgl obj + mtl loader"

I have a 3D model in .obj format. However the coordinates for this 3D model are not (0,0,0). This a 3D render of drone imagery so the coordinates are actual georeferenced coordinates.
I'm following the example in Three.js on how to load an obj with its mtl on webgl. I use the original HTML except that I simply replace the obj listed as male02 by CerroPelaoLow and the files are placed in the obj directory. Firefox displays the model correctly but the position is the problem.
Note that this render is generated by a program this way and even though I can manipulate the model with a program such as Meshlab I'd still prefer the minimum manipulation possible.
So how can I use local coordinates of my object or focus the camera and then use a different set of controls?
You can use the boundingSphere or boundingBox of your object's geometry to determine the position and of your camera. I have already implemented a functionality to focus an object or a set objects. So, here I share some code:
// assuming following variables:
// object -> your obj model (THREE.Mesh)
// camera -> PerspectiveCamera
// controls -> I'm also using OrbitControls
// if boundingSphere isn't set yet
object.computeBoundingSphere();
var sphere = object.geometry.boundingSphere.clone();
sphere.applyMatrix4( object.matrixWorld );
// vector from current center to camera position (shouldn't be zero in length)
var s = new THREE.Vector3().subVectors( camera.position, controls.center );
var h = sphere.radius / Math.tan( camera.fov / 2 * Math.PI / 180 );
var newPos = new THREE.Vector3().addVectors( sphere.center, s.setLength(h) );
camera.position.copy( newPos );
controls.center.copy( sphere.center );

How to hide a html overlay label on a sphere object

I have a sphere model along with html text labels overlayed onto it at fixed positions. How can I hide them when they are behind the object when i am rotating my sphere using orbit controls?
Anyone knows about any references on stackoverflow or any examples which can solve my issue?
See this example how you could do it: http://jsfiddle.net/L0rdzbej/194/
I have put the relevant code in the updateLabelVisibility()-function, it looks like this:
var meshPosition = mesh.getWorldPosition();
var eye = camera.position.clone().sub( meshPosition );
var dot = eye.clone().normalize().dot( meshPosition.normalize() );
//IS TRUE WHEN THE MESH IS OCCLUDED BY THE SPHERE = dot value below 0.0
var ocluded = dot < -0.2;
if ( ocluded ) {
// HIDE LABEL IF CAMERA CANT SEE IT (I.E. WHEN IT IS BEHIND THE GLOBE)
label.style.visibility = "hidden";
}
else {
// DISPLAY LABEL IF MESH IS VISIBLE TO THE CAMERA
label.style.visibility = "visible";
}
Where mesh is the marker where your label is attached to. The dot is basically telling you how far around the sphere it is, within a certain distance of 90 degree. Simply said by values below zero the label is longer visible by going further around the back.
To be fair I based this off some code from: https://zeitgeist-globe.appspot.com/

Resources