I need to have more than ~400 text objects on the scene. Each text object is one sentence of a decimal number with two digit. Echa text is reading from a JSON file. Each text object have a position (x,y,z) in the scene.
Using a basic scene i'm having, it takes a lot of times to load each text.
The code is here:
function setText(text, textColor, textSize, positionX, positionY, positionZ) {
var textGeo = new THREE.TextGeometry(text, {
height: 0,
curveSegments: 4,
font: "helvetiker",
//font: "optimer",
weight: "normal",
style: "normal",
size: textSize,
//
//bevelThickness: bevelThickness,
//bevelSize: bevelSize,
//bevelEnabled: bevelEnabled,
material: 0,
extrudeMaterial: 1
});
textGeo.computeBoundingBox();
textGeo.computeVertexNormals();
var textMaterial = new THREE.MeshBasicMaterial({
shading: THREE.FlatShading,
transparent: true,
depthWrite: false,
transparent: true,
needsUpdate: true,
color: textColor,
side:THREE.DoubleSide
});
var text = new THREE.Mesh(textGeo, textMaterial);
text.position.x = positionX;
text.position.y = positionY;
text.position.z = positionZ;
return text;
}
Examples of texts:
"model", "metamodel", "syntax", "sentence" ...
Examples of numbers:
7.32,
7.81,
8.30,
8.78,
Could you please help me? Thanks!
EDIT: The new code is here :
function createTextGeo(textSize, text) {
var tGeo = new THREE.TextGeometry(text, {
height: 0,
curveSegments: 4,
font: "helvetiker",
weight: "normal",
style: "normal",
size: textSize,
material: 0,
extrudeMaterial: 1
});
tGeo.computeBoundingBox();
tGeo.computeVertexNormals();
return tGeo;
}
function createGeoMaterial(textColor) {
var tMat = new THREE.MeshBasicMaterial({
color: textColor,
});
return tMat;
}
function setText(text, textColor, tMat, tGeo, X, Y, Z) {
tMat.color = textColor;
tGeo.text = text;
var text = new THREE.Mesh(tGeo, tMat);
text.position.set( X, Y, Z );
return text;
}
function createTexts(value) {
var text;
var mesh;
var x = 5;
var y = 8;
var z = 10;
var keyColor = new THREE.Color('#0B0B61');
var tMat = createGeoMaterial(keyColor);
var tGeo = createTextGeo(5, "");
for (var i = 0; i < value; i++) {
mesh = setText(i, keyColor, tMat, tGeo, x * i, y * i, z * i);
scene.add(mesh);
}
}
Related
I'm new to three.js and I'm using Tubegeometry of three.js in externalRenders of ArcGIS recently. I encountered a problem that the tube will have Strange jitter and distortion when I move the perspective, the situation will be exacerbated as the radius and length of the tube decrease. The gif below should be a good explanation of what happened:
I have been stuck with this for a few days,Can someone help me solve this?
Here's the complete javascript code:
let path=[
[116.533079,39.352741],
[116.533080,39.352753],
];
var map = new Map({
basemap: "satellite",
ground:'world-elevation'
});
var view = new SceneView({
container: "mapContainer",
map: map,
viewingMode: "local",
camera: {
position: [116.533079, 39.352741, 13154.641086300715],
fov: 55,
heading: 318.70623732061983,
tilt: 42.34234113203692
}
});
//create the reference line
view.graphics.add(new Graphic({
geometry:{
type:'polyline',
paths:path,
},
symbol:{
type: "simple-line", // autocasts as SimpleLineSymbol()
color: [226, 119, 40],
width: 2
}
}))
const myRenderer = {
view:view,
renderer:null,
camera:null,
scene:null,
height:100,
offset:0,
map:null,
setup:function (context){
this.renderer= new THREE.WebGLRenderer({
context:context.gl,
premultipliedAlpha:false,
});
this.renderer.setPixelRatio(window.devicePixelRatio);
this.renderer.setViewport(0,0,view.width,view.height);
this.renderer.autoClearDepth = false;
this.renderer.autoClearStencil = false;
this.renderer.autoClearColor = false;
const originalSetRenderTarget = this.renderer.setRenderTarget.bind(
this.renderer
);
this.renderer.setRenderTarget = function (target) {
originalSetRenderTarget(target);
if (target == null) {
context.bindRenderTarget();
}
};
this.scene = new THREE.Scene();
this.camera = new THREE.PerspectiveCamera();
const axesHelper = new THREE.AxesHelper(10000000);
this.scene.add(axesHelper);
// create the Tubegeometry
let v3List=[];
path.forEach((item)=>{
var renderPos = [0, 0, 0];
externalRenderers.toRenderCoordinates(view, [item[0], item[1], 100], 0, SpatialReference.WGS84, renderPos, 0, 1);
v3List.push(new THREE.Vector3(renderPos[0], renderPos[1], renderPos[2]));
})
console.log(v3List)
let curve = new THREE.CatmullRomCurve3(v3List, false);
let tubeGeometry = new THREE.TubeBufferGeometry(curve, 1, 10, 20, false);
var textureLoader = new THREE.TextureLoader();
this.map = textureLoader.load('img/point2.png')
this.map.wrapS = THREE.RepeatWrapping;
this.map.wrapT = THREE.RepeatWrapping;
this.map.repeat.set(2,20);
let material = new THREE.MeshBasicMaterial({
color: 0x85A9A9,
side:THREE.BackSide,
//map:this.map,
transparent: false,
depthWrite: false,
opacity: 0,
aoMapIntensity:0,
});
let tube1 = new THREE.Mesh(tubeGeometry, material);
this.scene.add(tube1);
let tubeGeometry2 = new THREE.TubeBufferGeometry(curve, 1, 10, 20, false);
let tubeMaterial2 = new THREE.MeshPhongMaterial({
color: 0x85A9A9,
side:THREE.FrontSide,
//map:this.map,
transparent: false,
depthWrite: false,
opacity: 0,
aoMapIntensity:0,
});
let tube2 = new THREE.Mesh(tubeGeometry2, tubeMaterial2);
console.log(tube2);
this.scene.add(tube2);
var ambient = new THREE.AmbientLight(0xffffff, 1);
ambient.position.set(0, 100, 0);
this.scene.add(ambient);
context.resetWebGLState();
},
render: function (context) {
const cam = context.camera;
this.camera.position.set(cam.eye[0], cam.eye[1], cam.eye[2]);
this.camera.up.set(cam.up[0], cam.up[1], cam.up[2]);
this.camera.lookAt(
new THREE.Vector3(cam.center[0], cam.center[1], cam.center[2])
);
this.camera.projectionMatrix.fromArray(cam.projectionMatrix);
this.map.offset.x += 0.001;
this.map.needsUpdate = true;
this.renderer.state.reset();
this.renderer.render(this.scene, this.camera);
externalRenderers.requestRender(view);
// cleanup
context.resetWebGLState();
}
}
externalRenderers.add(view, myRenderer);
})
This looks very much like the Precision Issues detailed in https://developers.arcgis.com/javascript/latest/api-reference/esri-views-3d-externalRenderers.html
I have LineString features styled by a single stroke. Some of these features represent two things, so in addition to the initial stroke color, I would like to have a parallel line next to it in a different color. This would be a no-brainer if ol/style/Stroke had an offset property like ol/style/Image does, but sadly, it does not. If I created offset geometries for those features, they wouldn't be visible from a zoomed out level. What are my options?
You can offset the geometries based on resolution
var style = function(feature, resolution) {
var colors = ['green', 'yellow', 'red'];
var width = 4;
var styles = [];
for (var line = 0; line < colors.length; line++) {
var dist = width * resolution * (line - (colors.length-1)/2);
var geom = feature.getGeometry();
var coords = [];
var counter = 0;
geom.forEachSegment(function(from, to) {
var angle = Math.atan2(to[1] - from[1], to[0] - from[0]);
var newFrom = [
Math.sin(angle) * dist + from[0],
-Math.cos(angle) * dist + from[1]
];
var newTo = [
Math.sin(angle) * dist + to[0],
-Math.cos(angle) * dist + to[1]
];
coords.push(newFrom);
coords.push(newTo);
if (coords.length > 2) {
var intersection = math.intersect(coords[counter], coords[counter+1], coords[counter+2], coords[counter+3]);
coords[counter+1] = (intersection) ? intersection : coords[counter+1];
coords[counter+2] = (intersection) ? intersection : coords[counter+2];
counter += 2;
}
});
styles.push(
new ol.style.Style({
geometry: new ol.geom.LineString(coords),
stroke: new ol.style.Stroke({
color: colors[line],
width: width
})
})
);
}
return styles;
};
var raster = new ol.layer.Tile({
source: new ol.source.OSM()
});
var source = new ol.source.Vector();
var vector = new ol.layer.Vector({
source: source,
style: style
});
var map = new ol.Map({
layers: [raster, vector],
target: 'map',
view: new ol.View({
center: [-11000000, 4600000],
zoom: 4
})
});
map.addInteraction(new ol.interaction.Draw({
source: source,
type: 'LineString'
}));
html, body, .map {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
<link href="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/css/ol.css" rel="stylesheet" />
<script src="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/build/ol.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjs/5.4.1/math.min.js"></script>
<div id="map" class="map"></div>
The intersect test prevents effects like this on sharp angles
For each each segment of the feature geometry the style function calculates parallel segments set to be 4 pixels apart regardless of the resolution (in this case line width and spacing are the same) for the styling geometries centered around the original segment (e.g. if three colors the center style line would follow the original and the outer style lines are offset by +/- 4, if two colors each lines would be offset by +/- 2) Initially the parallel segments are the same length as the original but need to be increased at outside angles or reduced at inside angles so math.intersect is used to find the point where they should join to form a continuous line. Finally for each color the completed linestring is used as the geometry for the color's style, and an array containing each style is returned.
Here is a second example where instead of simple colors and fixed width each offset line has its own width and stroke pattern.
var white = [255, 255, 255, 1];
var blue = [0, 153, 255, 1];
var width = 3;
var pointStyle = new ol.style.Style({
image: new ol.style.Circle({
radius: width * 2,
fill: new ol.style.Fill({
color: blue
}),
stroke: new ol.style.Stroke({
color: white,
width: width / 2
})
}),
zIndex: Infinity
});
var style = function(feature, resolution) {
var widths = [10, 10];
var strokes = [
[
{
width: 4,
color: '#7af500'
}, {
width: 4,
color: '#55aa00',
lineDash: [7,9]
}
], [
{
width: 2,
color: 'black'
}, {
width: 8,
color: 'black',
lineDash: [2,14],
lineCap: 'butt'
}
]
];
var styles = [pointStyle];
var totalWidth = 0
for (var line = 0; line < widths.length; line++) {
totalWidth += widths[line];
}
var width = 0;
for (var line = 0; line < widths.length; line++) {
var dist = (width + widths[line]/2 - totalWidth/2) * resolution;
width += widths[line];
var geom = feature.getGeometry();
if (geom.forEachSegment) {
var coords = [];
var counter = 0;
geom.forEachSegment(function(from, to) {
var angle = Math.atan2(to[1] - from[1], to[0] - from[0]);
var newFrom = [
Math.sin(angle) * dist + from[0],
-Math.cos(angle) * dist + from[1]
];
var newTo = [
Math.sin(angle) * dist + to[0],
-Math.cos(angle) * dist + to[1]
];
coords.push(newFrom);
coords.push(newTo);
if (coords.length > 2) {
var intersection = math.intersect(coords[counter], coords[counter+1], coords[counter+2], coords[counter+3]);
coords[counter+1] = (intersection) ? intersection : coords[counter+1];
coords[counter+2] = (intersection) ? intersection : coords[counter+2];
counter += 2;
}
});
strokes[line].forEach( function(stroke) {
styles.push(
new ol.style.Style({
geometry: new ol.geom.LineString(coords),
stroke: new ol.style.Stroke(stroke)
})
);
});
}
}
return styles;
};
var raster = new ol.layer.Tile({
source: new ol.source.OSM()
});
var source = new ol.source.Vector();
var vector = new ol.layer.Vector({
source: source,
style: style
});
var map = new ol.Map({
layers: [raster, vector],
target: 'map',
view: new ol.View({
center: [-11000000, 4600000],
zoom: 4
})
});
map.addInteraction(new ol.interaction.Draw({
source: source,
style: style,
type: 'LineString'
}));
html, body, .map {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
<link href="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/css/ol.css" rel="stylesheet" />
<script src="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/build/ol.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjs/5.4.1/math.min.js"></script>
<div id="map" class="map"></div>
I have a version of this that works - but not when I zoom the chart.
Lines separates on zoom
I plot lines on the chart (from the database) and use our function to
draw the lines.
I tried to insert code here but my functions in the javascript
are not run. How do I use functions in code snipplets?
function print_my_string( my_string )
{
console.log( my_string );
}
html, body, .map {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
<link href="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/css/ol.css" rel="stylesheet" />
<script src="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/build/ol.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjs/5.4.1/math.min.js"></script>
<div id="map" class="map"></div>
// How do I use the java-script (Include before called...
<script>print_my_string( "Hello" );</script>
I have the following code that generates some meshes. I want to add 3d text to the scene but when i do this i get the following error:
TypeError: Cannot read property 'generateShapes' of undefined
this is the code i have to generate the meshes and the 3d text:
var x = 0;
var y = 0;
var finalSize = 450;
var textureLoader = new THREE.TextureLoader();
var fontLoader = new THREE.FontLoader();
the var font is undefined why?
var font = fontLoader.load( 'css/arial_bold.json');
var fontColor = textMaterial = new THREE.MeshBasicMaterial({ color: 0x000000, overdraw: 0.5 });
for (var i = javascriptProjects.length - 1; i >= 0; i--) {
var object = {};
object.scale = javascriptProjects[i].baseImageData["0"] / finalSize;
var geometry = new THREE.PlaneGeometry(javascriptProjects[i].baseImageData["0"]/object.scale,javascriptProjects[i].baseImageData["1"]/object.scale, 10, 10);
object.texture = textureLoader.load( "data/"+ javascriptProjects[i].base_image );
object.material = new THREE.MeshBasicMaterial( { map: object.texture, overdraw: 0.5, transparent: true } );
object.material.opacity = 0.5;
object.mesh = new THREE.Mesh(geometry, object.material);
//the line below generates the error
object.FontGeo = new THREE.TextGeometry( javascriptProjects[i].project_name , {
font: font,
size: 50,
height: 2,
curveSegments: 12,
bevelThickness: 1,
bevelSize: 1,
bevelEnabled: false
});
object.textMesh = new THREE.Mesh( object.FontGeo, fontColor );
object.textMesh.position.x = object.location.x;
object.textMesh.position.y = object.location.y;
object.textMesh.position.z = object.location.z - 100;
scene.add(object.textMesh);
object.location = new THREE.Vector3(x, 120, y);
object.id = "" + javascriptProjects[i].project_id;
object.mesh.position.x = object.location.x;
object.mesh.position.y = object.location.y;
object.mesh.position.z = object.location.z;
scene.add(object.mesh);
meshes.push(object.mesh);
objects.push(object);
x += 600;
if(i % 3 == 0){
y += 600;
x = 0;
};
};
apparently font is undefined. why i don't see what i am doing wrong. Any suggestions would be great. If something is not clear let me know.
Thanks in advance
FontLoader.load() is an asynchronous function call. This is why there is an onLoad call back function.
The library is calling font.generateShapes() before the font is loaded.
Use a pattern like this one, instead:
var loader = new THREE.FontLoader();
loader.load( 'myFontFile.json', function ( font ) {
// insert your code here
} );
See, for example, this three.js example, and the three.js documentation .
three.js r.84
i have list of polylines. i want to make it more organized. how can i make my polyline list into the GeoJSON?
var point1 = new coordsToLatLngArray(0, 100);
var point2 = new coordsToLatLngArray(200, 150);
var point3 = new coordsToLatLngArray(400, 70);
var point4 = new coordsToLatLngArray(10, 70);
var point5 = new coordsToLatLngArray(90, 50);
var pointList = [point1, point2, point3, point4, point5];
var firstpolyline = new L.polyline(pointList, {
color: 'red',
weight: 3,
opacity: 0.5,
smoothFactor: 1
});
firstpolyline.addTo(map);
i have trying to work with: http://leafletjs.com/examples/geojson.html
but it didn't work. my line were outside the map:
var geojsonFeature = [{
"type": "LineString",
"coordinates":
[
[0,0],
[-100,100]
]
}];
var myStyle = {
"color": "#ff7800",
"weight": 5,
"opacity": 0.65
};
var myLayer = L.geoJson(coordsToLatLngArray(), {style: myStyle }).addTo(l);
myLayer.addData(geojsonFeature);
Demo of the problem:
http://jsfiddle.net/n4k1psuh/
There will only be passed a single argument to coordsToLatLngArray, an array:
[longitude,latitude]
The function must look like this:
var coordsToLatLngArray = function(c) {
var e = fixLat(c[1]),
t = c[0],
n = e / latScale + latScaleVal / 2 / latScale,
r = t / longScale + longScaleVal / 2 / longScale;
return [-n, r]
};
Note: in geoJSON a coordinate has a reversed order, [longitude,latitude], so the coordinates must be:
[
[100, 0],
[150, 200],
[70,400],
[70,10],
[50,90]
]
to draw the feature call:
var myLayer = L.geoJson(geojsonFeature, {
style: myStyle,
coordsToLatLng:coordsToLatLngArray
}).addTo(l);
Demo: http://jsfiddle.net/Ltaw5teo/
I'm trying to use Dat GUI + Three.js to get user inputted text to create the 3d text geometry that is updated in real time. So far I have been able to get the positions x, y & z to be controlled and the text input box to show up.
I can't figure out what I'm doing wrong with the text input so any help would be appreciated
here is a link to what i've got so far: http://nuevil.com/index3.html here is the code that deals with the 3d text geometry + dat GUI:
var theText = "FEED ME";
var hash = document.location.hash.substr( 1 );
if ( hash.length !== 0 ) { theText = hash; }
var text3d = new THREE.TextGeometry( theText, {
size: 80,
height: 80,
curveSegments: 2,
font: "helvetiker",
weight: "bold"
});
text3d.computeBoundingBox();
var centerOffset = -0.5 * ( text3d.boundingBox.max.x - text3d.boundingBox.min.x );
var textMaterial = new THREE.MeshNormalMaterial;
text = new THREE.Mesh( text3d, textMaterial );
text.position.x = centerOffset;
text.position.y = 0;
text.position.z = 0;
// text.position.z = Math.tan( Date.now() * 2 ) * 20;
text.rotation.x = 0;
text.rotation.y = Math.PI * 2;
parent = new THREE.Object3D();
parent.add( text );
scene.add( parent );
var axes = new THREE.AxisHelper();
scene.add(axes);
gui = new dat.GUI();
parameters =
{
x: 0, y: 30, z: 0,
color: "#ff0000", // color (change "#" to "0x")
theText: "",
opacity: 1,
visible: true,
material: "Phong",
reset: function() { resetText() }
};
var folder1 = gui.addFolder('text');
var line1 = folder1.add( parameters, 'theText');
// var line2 = folder1.add( text, '').step(1).listen();
// var line3 = folder1.add( text, '' ).step(1).listen();
// folder1.open();
line1.onChange(function(newValue)
{ theText = newValue});
var folder2 = gui.addFolder('position');
var textX = folder2.add( parameters, 'x' ).min(-400).max(200).step(1).listen();
var textY = folder2.add( parameters, 'y' ).min(0).max(100).step(1).listen();
var textZ = folder2.add( parameters, 'z' ).min(-200).max(200).step(1).listen();
// folder2.open();
var folder3 = gui.addFolder('size');
textX.onChange(function(value)
{ text.position.x = value; });
textY.onChange(function(value)
{ text.position.y = value; });
textZ.onChange(function(value)
{ text.position.z = value; });
Your code :
line1.onChange(function(newValue)
{ theText = newValue});
has no effect on the mesh displaying the text.
You need to redefine the mesh with the new text :
line1.onChange(function(newValue)
{
var text3d = new THREE.TextGeometry( newValue, {
size: 80,
height: 80,
curveSegments: 2,
font: "helvetiker",
weight: "bold"
});
text3d.computeBoundingBox();
text = new THREE.Mesh( text3d, textMaterial );
});
the text variable is passed via canvas -- via the font libraries-- not generated within THREE.TextGeometry so I created a function so that when values are entered they are fed to a new text mesh each time. i'm thinking of adding an update function or refresh function in the gui for users to choose to erase old meshes.
line1.onFinishChange(function(newValue)
{
function createtext(){
var hash = document.location.hash.substr( 1 );
if ( hash.length !== 0 ) { newValue = hash; }
var text3d = new THREE.TextGeometry( newValue, {
size: 80,
height: 80,
curveSegments: 2,
font: "helvetiker",
weight: "bold"
});
text3d.computeBoundingBox();
var centerOffset = -0.5 * ( text3d.boundingBox.max.x - text3d.boundingBox.min.x );
var textMaterial = new THREE.MeshNormalMaterial;
text = new THREE.Mesh( text3d, textMaterial );
text.position.x = centerOffset;
text.position.y = 0;
text.position.z = 0;
// text.position.z = Math.tan( Date.now() * 2 ) * 20;
text.rotation.x = 0;
text.rotation.y = Math.PI * 2;
parent = new THREE.Object3D();
parent.add( text );
scene.add( parent );
var axes = new THREE.AxisHelper();
scene.add(axes);
}
createtext();
});