How to render flat text in three.js - javascript

I have a library I'm working on that's supposed to display graphs. It'd be nice to include a way to label the vertices with a parameterized text, but unfortunately, I'm stuck.
My scene consists of boxes and lines. Below is how I render the box, and I'd like to know how to render a short text beneath it.
Graph.prototype.add_vertex = function(options){
var v = new Vertex(this.vertex_id_spawn++, options);
v.paint(this.scene);
this.V[v.id] = v;
return v;
};
As you can see, Vertex relies on paint to draw itself to the screen.
Vertex.prototype.paint = function(scene){
this.object = new Cube(scene, this.options.cube);
scene.add(this.object);
};
and the Cube function:
var Cube = function(scene, options){
if(options === undefined){
options = {};
}
if(options.width === undefined){
options.width = 3;
}
if(options.height === undefined){
options.height = 3;
}
if(options.depth === undefined){
options.depth = 3;
}
if(options.color === undefined){
options.color = 0x00ccaa;
}
if(options.wireframe === undefined){
options.wireframe = false;
}
var geometry, material, material_args;
geometry = new THREE.BoxGeometry(
options.width,
options.height,
options.depth
);
geometry.dynamic = true;
if(options.texture !== undefined){
material_args = {
map: THREE.ImageUtils.loadTexture( options.texture )
};
}else{
material_args = {
color: options.color,
wireframe: true
};
}
material = new THREE.MeshBasicMaterial( material_args );
var cube = new THREE.Mesh( geometry, material );
var scale = 2;
cube.position = new THREE.Vector3(
Math.random() * scale, Math.random() * scale,
Math.random() * scale
);
cube.matrixAutoUpdate = true;
scene.add(cube);
return cube;
};
The full code is available at https://github.com/thwee-alchemist/fourd.js/blob/gh-pages/lib/fourd.js
I've been stuck on the label problem forever, and would welcome any suggestions.
Edit In the meantime, I've tried creating a canvas context filled with text, to get the dataURL, but to no avail. I passed the dataURL to the ImageLoader, to the map parameter of the texture which is what I do with regular vertices.

Related

Raycasting not working on imported OBJ model,but working on object created with the program

I am new to three.js, and i am trying to use raycasting, i am quite confused with how raycasting would work on imported 3d models. Here i imported an obj model, and when i try to detect whether i touch with the imported 3D model, the funciton is not working as i expect. But when i change it to detect a box object i create with three.js, is working as i expected, can someone help with this problem?
import * as THREE from 'https://unpkg.com/three#0.126.1/build/three.module.js';
import { OrbitControls } from 'https://unpkg.com/three#0.126.1/examples/jsm/controls/OrbitControls.js';
import {OBJLoader} from "https://unpkg.com/three#0.126.1/examples/jsm/loaders/OBJLoader.js";
import {MTLLoader} from "https://unpkg.com/three#0.126.1/examples/jsm/loaders/MTLLoader.js";
import * as GUI from "https://unpkg.com/dat.gui#0.7.7/build/dat.gui.module.js";
//Set Up Enviroment for 3D model
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth,window.innerHeight);
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
//Set up Lighting
var keyLight = new THREE.DirectionalLight(new THREE.Color('hsl(30,100%,75%)'),1.0);
var fillLight = new THREE.DirectionalLight(new THREE.Color('hsl(240,100%,75%)'),0.75);
var backLight = new THREE.DirectionalLight(0xffffff,1.0);
keyLight.position.set(-100,0,100);
fillLight.position.set(100,0,100);
backLight.position.set(100,0,-100).normalize();
scene.add(keyLight);
scene.add(fillLight);
scene.add(backLight);
//Set up Camera
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth/window.innerHeight,
0.1,
1000
);
const orbit = new OrbitControls(camera,renderer.domElement);
camera.position.set(0,2,7.5);
orbit.update();
//Set up base
const planeGeometry = new THREE.PlaneGeometry(100,100);
const planeMaterial = new THREE.MeshBasicMaterial({
color: 0xffffff,
wireframe: false,
side: THREE.DoubleSide
});
var plane = new THREE.Mesh(planeGeometry,planeMaterial);
scene.add(plane);
plane.rotation.x = -0.5 * Math.PI;
//Model for testing
const boxGeometry = new THREE.BoxGeometry(10,10,10);
const boxMaterial = new THREE.MeshBasicMaterial({color: 0x0000ff})
var box = new THREE.Mesh(boxGeometry,boxMaterial);
scene.add(box);
box.position.x += 20;
//Set up GUI for controlling base
const gridHelper = new THREE.GridHelper(100);
scene.add(gridHelper);
const gui = new GUI.GUI();
const options = {
planeColor: '#ffea00',
wireframe: false
};
gui.addColor(options,'planeColor').onChange(function(e){
plane.material.color.set(e);
});
gui.add(options,'wireframe').onChange(function(e){
plane.material.wireframe = e;
});
//Set up Background image
const textureLoader = new THREE.TextureLoader();
textureLoader.load(
'../img/doge.jpg',
function ( texture ) {
scene.background = texture;
},
undefined,
function ( err ) {
console.error( 'An error happened.' );
}
);
//Import obj and mtl file to build 3D model
var building;
const mtlLoader = new MTLLoader();
mtlLoader.load(
'../source/building_04.mtl',
(materials)=>{
materials.preload();
console.log(materials);
const objLoader = new OBJLoader()
objLoader.setMaterials(materials)
objLoader.load(
'../source/building_04.obj',
(object)=>{
scene.add(object)
building = object;
object.position.y +=1;
},
(xhr) => {
console.log((xhr.loaded / xhr.total) * 100 + '% loaded')
},
(error) => {
console.log("Object error")
}
)
},
(xhr) => {
console.log((xhr.loaded / xhr.total) * 100 + '% loaded')
},
(error) => {
console.log("Material Eror")
}
)
//Set up raycasting environment
const raycaster = new THREE.Raycaster();
const mousePosition = new THREE.Vector2();
window.addEventListener('mousemove', function(e){
mousePosition.x = (e.clientX/this.window.innerWidth)*2 - 1;
mousePosition.y = -(e.clientY/this.window.innerHeight)*2 + 1;
});
//Function making sure everything to be up to date
function animate(){
//Raycasting
raycaster.setFromCamera( mousePosition, camera );
const intersects = raycaster.intersectObjects( scene.children,true);
if(intersects.length > 0){
for ( let i = 0; i < intersects.length; i ++ ) {
if(building != undefined){
if(intersects[0].object.id === building.id){
console.log("Touched!");
}
else{
console.log("did not touch!");
}
}
else{
console.log("Not reeady!");
}
console.log(intersects[i].object.id);
}
}
else{
console.log("did not touched")
}
console.log("finished")
renderer.render(scene,camera);
}
renderer.setAnimationLoop(animate);
//Make the screen to resize following the browser size
window.addEventListener('resize',function(){
camera.aspect = window.innerWidth/this.window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth,window.innerHeight);
});
The problem is that the object you get from OBJLoader and assign to your building variable is an instance of THREE.Group. Groups do not have an own geometry or material. They are more or less containers holding other 3D objects (like meshes, point clouds, lines or other groups) and representing them as a logical group.
That means they are never the result of a raycasting evaluation which means if(intersects[0].object.id === building.id) always fails.
I suggest you do this after the loading process which tags your objects:
building.traverse( function( object ) {
object.userData.tag = 'building';
} );
You can then use the tag property in your raycasting routine for detecting the building.

How can I pick up text mesh with the RayCaster in Three.js?

So, I have run into an issue using RayCaster to pick up TextGeometry. I am using the function createText() below to render this text onto the canvas. This works fine, it shows up, and it even adds it to the array of meshes for the RayCaster it check for intersection, however, the function HandleSingleClick() is not firing on TextGeometries for some reason.
I have tested using cubes, and it does work. The intention is that if you click on the TextGeometry, it changes color to denote that you clicked on it. However, for some reason, the following script doesn't work when it comes to Text. I can confirm that the meshes are being added to the array the RayCaster checks in, but it doesn't pick them up for some reason.
import {
Font,
TextGeometry
} from '../js/libs/Three.es.js';
import FontJson from '../fonts/helvetiker_bold.typeface.json';
export default class TextExtension extends Autodesk.Viewing.Extension {
constructor(viewer, options) {
super()
this.viewer = viewer
}
load() {
console.log('Viewing.Extension.MeshSelection loaded')
this.viewer.addEventListener(
Autodesk.Viewing.OBJECT_TREE_CREATED_EVENT, () => {
this.dbIds = this.getAllDbIds()
console.log(this.dbIds);
});
this.viewer.toolController.registerTool(this)
this.viewer.toolController.activateTool(
'MeshSelection')
this.intersectMeshes = [];
return true
}
/////////////////////////////////////////////////////////
// Tool Interface
//
/////////////////////////////////////////////////////////
getNames() {
return ['MeshSelection']
}
activate() {
}
deactivate() {
}
/////////////////////////////////////////////////////////
// Unload callback
//
/////////////////////////////////////////////////////////
unload() {
console.log('MeshSelection unloaded')
this.viewer.toolController.deactivateTool(
'MeshSelection')
this.viewer.toolController.unregisterTool(this)
return true
}
/////////////////////////////////////////////////////////
// Adds a box mesh with random size and position
// to the scene
//
/////////////////////////////////////////////////////////
addMesh() {
const geometry = new THREE.BoxGeometry(
Math.random() * 10 + 5.0,
Math.random() * 10 + 5.0,
Math.random() * 10 + 5.0)
const color = Math.floor(Math.random() * 16777215)
const material = this.createColorMaterial(color)
const mesh = new THREE.Mesh(geometry, material)
mesh.position.x = -50 + Math.random() * 25
mesh.position.y = -50 + Math.random() * 25
mesh.position.z = 1 + Math.random() * 1
this.viewer.impl.scene.add(mesh)
this.viewer.impl.sceneUpdated(true)
return mesh
}
/////////////////////////////////////////////////////////
// Creates color material from int
//
/////////////////////////////////////////////////////////
createColorMaterial(color) {
const material = new THREE.MeshPhongMaterial({
specular: new THREE.Color(color),
side: THREE.DoubleSide,
reflectivity: 0.0,
color
})
const materials = this.viewer.impl.getMaterials()
materials.addMaterial(
color.toString(16),
material,
true)
return material
}
/////////////////////////////////////////////////////////
// Creates Raycaster object from the pointer
//
/////////////////////////////////////////////////////////
pointerToRaycaster(domElement, camera, pointer) {
const pointerVector = new THREE.Vector3()
const pointerDir = new THREE.Vector3()
const ray = new THREE.Raycaster()
const rect = domElement.getBoundingClientRect()
const x = ((pointer.clientX - rect.left) / rect.width) * 2 - 1
const y = -((pointer.clientY - rect.top) / rect.height) * 2 + 1
if (camera.isPerspective) {
pointerVector.set(x, y, 0.5)
pointerVector.unproject(camera)
ray.set(camera.position,
pointerVector.sub(
camera.position).normalize())
} else {
pointerVector.set(x, y, -1)
pointerVector.unproject(camera)
pointerDir.set(0, 0, -1)
ray.set(pointerVector,
pointerDir.transformDirection(
camera.matrixWorld))
}
return ray
}
/////////////////////////////////////////////////////////
// Click handler
//
/////////////////////////////////////////////////////////
handleSingleClick(event) {
console.log(this.intersectMeshes);
const pointer = event.pointers ?
event.pointers[0] :
event
console.log(pointer);
const rayCaster = this.pointerToRaycaster(
this.viewer.impl.canvas,
this.viewer.impl.camera,
pointer)
const intersectResults = rayCaster.intersectObjects(
this.intersectMeshes, true)
console.log(intersectResults);
const hitTest = this.viewer.model.rayIntersect(
rayCaster, true, this.dbIds)
const selections = intersectResults.filter((res) =>
(!hitTest || (hitTest.distance > res.distance))
)
if (selections.length) {
console.log('Custom meshes selected:')
console.log(selections)
selections[0].object.material.color = this.createColorMaterial(Math.floor(Math.random() * 16777215));
viewer.impl.sceneUpdated(true);
return true
}
return false
}
/////////////////////////////////////////////////////////
// Get list of all dbIds in the model
//
/////////////////////////////////////////////////////////
getAllDbIds() {
const {
instanceTree
} = this.viewer.model.getData()
const {
dbIdToIndex
} = instanceTree.nodeAccess
return Object.keys(dbIdToIndex).map((dbId) => {
return parseInt(dbId)
})
}
createColorMaterial(color) {
const material = new THREE.MeshPhongMaterial({
specular: new THREE.Color(color),
side: THREE.DoubleSide,
reflectivity: 0.0,
color
})
const materials = this.viewer.impl.getMaterials()
materials.addMaterial(
color.toString(),
material,
true)
return material
}
/////////////////////////////////////////////////////////
// Wraps TextGeometry object and adds a new mesh to
// the scene
/////////////////////////////////////////////////////////
createText(params, index) {
const geometry = new TextGeometry(params.text,
Object.assign({}, {
font: new Font(FontJson),
params
}))
geometry.computeBoundingBox();
const material = this.createColorMaterial(
params.color)
const text = new THREE.Mesh(
geometry, material)
text.scale.set(params.scale, params.scale, params.scale);
text.position.set(
params.position.x,
params.position.y,
10)
this.intersectMeshes[index] = text;
this.viewer.impl.scene.add(text);
this.viewer.impl.sceneUpdated(true)
}
}
Autodesk.Viewing.theExtensionManager.registerExtension(
'TextSpawner', TextExtension);
You need to debug the ray casting logic to find out what is going on. In the Three.js version that the viewer is using there is a chech to see if the geometry object from the tested mesh is of instance THREE.BufferGeometry or THREE.Geometry, if none the intersection logic is not computed.
In order to make it compatible with it while adding as little code as possible, I used the following approach:
/////////////////////////////////////////////////////////
// Wraps TextGeometry object and adds a new mesh to
// the scene
/////////////////////////////////////////////////////////
createText (params) {
const textGeometry = new TextGeometry(params.text,
Object.assign({}, {
font: new Font(FontJson),
params
}))
// use a geometry recognized by the viewer
// THREE.js version
const geometry = new THREE.BufferGeometry
geometry.fromGeometry(textGeometry)
const material = this.createColorMaterial(
params.color)
const text = new THREE.Mesh(
geometry , material)
text.position.set(
params.position.x,
params.position.y,
params.position.z)
this.viewer.impl.scene.add(text)
this.viewer.impl.sceneUpdated(true)
return text
}

Three.js move as group

It seems like this should be easy, but I have spent nearly a week on this trying every possible combination of how to drag multiple items as a group in Three.js. It started out simple, I used this example https://jsfiddle.net/mz7Lv9dt/1/ to get the ball working. I thought I could just add some TextGeometry, which of course had some major API changes in the last couple releases rendering most examples obsolete.
After finally getting it to work as a single line, I wanted to add in wordwrap and move it as a group, but I can't seem to do so.
Here you can see it working just fine with the ball, but you can't drag the text https://jsfiddle.net/ajhalls/h05v48wd/
By swapping around three lines of code (location line 93-99), I can get it to where you can drag the individual lines around, which you can see here: https://jsfiddle.net/ajhalls/t0e2se3x/
function addText(text, fontSize, boundingWidth) {
var wrapArray;
wrapArray = text.wordwrap(10,2);
var loader = new THREE.FontLoader();
loader.load( 'https://cdn.coursesaver.com/three.js-74/examples/fonts/helvetiker_bold.typeface.js',
function ( font ) {
group = new THREE.Group();
group.name = "infoTag";
for (var i = 0; i < wrapArray.length; i++) {
var objectID=i;
var line = wrapArray[objectID];
var textGeo = new THREE.TextGeometry( line, {font: font,size: fontSize,height: 10,curveSegments: 12,bevelThickness: 0.02,bevelSize: 0.05,bevelEnabled: true});
textGeo.computeBoundingBox();
var centerOffset = -0.5 * ( textGeo.boundingBox.max.x - textGeo.boundingBox.min.x );
var textMaterial = new THREE.MeshPhongMaterial( { color: 0xff0000, specular: 0xffffff } );
var mesh = new THREE.Mesh( textGeo, textMaterial );
mesh.position.x = centerOffset +200;
mesh.position.y = i*fontSize*-1+11;
mesh.position.z = 280;
mesh.castShadow = true;
mesh.receiveShadow = true;
mesh.geometry.center();
mesh.lookAt(camera.position);
mesh.name = i;
group.add( mesh ); //this should work - comment out and swap for other two lines to see
scene.add(mesh); // this should work- comment out and swap for other two lines to see
//objects.push(mesh);//works on individual lines if you uncomment this
//scene.add(mesh); //works on individual lines if you uncomment this
}
objects.push( group ); // this should work- comment out and swap for other two lines to see
});
}
That "should" be working according to everything I had researched over the last week. I had one moment where it was "working" but because of the size of the group object, the pivot points were wrong, the setLength function didn't work, and it flipped the object away from the camera. All in all it was a mess.
I did try using 2d objects such as canvases and sprites, but for reasons detailed here Three.js TextGeometry Wordwrap - drag as group couldn't get it working.
Please, someone help me!
The issue ended up being with the group. Previously I was creating it, adding objects to it with a position.z which increased the size of the box around the group, then after doing that I moved the box to in front of the camera and did a group.lookAt which meant that when I was dragging it everything including pivot point and looking at it from the back was wrong. The right way was to create the group, position it, face the camera, then add the text.
function addText(text, fontSize, wrapWidth, tagColor, positionX, positionY, positionZ) {
var wrapArray;
wrapArray = text.wordwrap(wrapWidth,2);
var loader = new THREE.FontLoader();
loader.load( '/js/fonts/helvetiker_bold.typeface.js', function ( font ) {
group = new THREE.Group();
group.position.x=positionX;
group.position.y=positionY;
group.position.z=positionZ;
group.lookAt(camera.position);
group.tourType = "infoTag";
group.name = "infoTag-" + objects.length;
group.dataID=objects.length;
group.textData=text;
for (var i = 0; i < wrapArray.length; i++) {
var objectID=i;
var line = wrapArray[objectID];
var textGeo = new THREE.TextGeometry( line, {
font: font,
size: fontSize,
height: 1,
curveSegments: 12,
bevelThickness: 0.02,
bevelSize: 0.05,
bevelEnabled: true
});
textGeo.computeBoundingBox();
var centerOffset = -0.5 * ( textGeo.boundingBox.max.x - textGeo.boundingBox.min.x );
var textMaterial = new THREE.MeshPhongMaterial( { color: tagColor, specular: 0xffffff } );
var mesh = new THREE.Mesh( textGeo, textMaterial );
mesh.dataID=objects.length;
mesh.position.x = 0;
mesh.position.y = (i*mesh.geometry.boundingBox.max.y*-1)*1.15;
mesh.position.z = 0;
mesh.castShadow = true;
mesh.receiveShadow = true;
mesh.geometry.center();
//mesh.lookAt(camera.position);
mesh.name = "infoTag-mesh-" + objects.length;
group.add( mesh );
}
scene.add(group);
objects.push( group );
});
}
Of course there were some changes to be made in the mouse events to take into account that you want to move the parent of the object, which looks something like this:
function onDocumentMouseDown(event) {
event.preventDefault();
raycaster.setFromCamera(mouse, camera);
var intersects = raycaster.intersectObjects(objects, true);
if (intersects.length > 0) {
if (intersects[0].object.parent.tourType == 'infoTag') {
var manipulatingInfoTag = true;
SELECTED = intersects[0].object.parent;
}else{
SELECTED = intersects[0].object;
}
var intersects = raycaster.intersectObject(plane);
if (intersects.length > 0) {
offset.copy(intersects[0].point).sub(plane.position);
}
container.style.cursor = 'move';
}
isUserInteracting = true;
onPointerDownPointerX = event.clientX; onPointerDownPointerY = event.clientY; onPointerDownLon = lon; onPointerDownLat = lat;
}

Three.js GridHelper: How to hide/show the grid?

I am adding a plane and a grid using Gridhelper to my 3d scene:
// PLANE XY static
var gridplaneSize = 20;
var color = 0xFFDCBB;
var plGeometry = new THREE.PlaneGeometry(gridplaneSize, gridplaneSize);
var plMaterial = new THREE.MeshBasicMaterial( {color:color, ambient:color, side:THREE.DoubleSide, opacity:0.5, transparent:true, depthWrite: false } );
var planeMesh_xy = new THREE.Mesh(plGeometry, plMaterial);
planeMesh_xy.rotation.x = -Math.PI/2;
scene.add(planeMesh_xy);
planeMesh_xy.receiveShadow = true;
// GRID XY static
var gridstep = 1;
var gridcolor = 0xCCCCCC;
var gridHelper_xy = new THREE.GridHelper(gridplaneSize/2, gridstep);
gridHelper_xy.position = new THREE.Vector3(0, 0, 0);
gridHelper_xy.setColors( new THREE.Color(gridcolor), new THREE.Color(gridcolor) );
scene.add(gridHelper_xy);
Then I would like to decide if the plane or the grid is visible.
This works for the plane:
planeMesh_xy.visible = false;
But not for the grid:
gridHelper_xy.visible = false; // not working
I also tried workarounds, not working:
gridHelper_xy.material.transparent = true;
gridHelper_xy.material.opacity = 0;
gridHelper_xy.parent.visible = false;
Can anyone tell me how to hide the grid then?
What should happen when you set the visible flag of an object is that all of its children should inherit the flag.
In r71 this should work out of the box (look at issue https://github.com/mrdoob/three.js/issues/1524)
In r65 you would have to traverse your hierarchy setting the flag on all the sub-parts:
object.traverse ( function (child) {
child.visible = false;
}

errors extruding shapes with three.js

I'm just getting started with three.js, and having some issues extruding some 2d shapes.
I have a GeoJSON file containing all the counties in the US. I'm using d3.js and a d3.geo.albersUSa() projection to convert each latitude/longitude into a list of X/Y coordinates to make a THREE.Shape that I'm then extruding and drawing. This seems to work OK for most counties.
The issue I'm seeing is that some subset of counties either fail to extrude or extrude incorrectly with the following sequences of warnings:
Warning, unable to triangulate polygon!
Duplicate point 653.4789181355854:204.0166729191409
Either infinite or no solutions!
Its finite solutions.
Either infinite or no solutions!
Too bad, no solutions.
I'm not sure I understand exactly what the issue is -- as far as I can tell, there's nothing special about these particular shapes. Am I doing something wrong, or is this an issue with the extrusion code in three.js?
For example here are some missing counties:
Also notice the triangular 'hourglass' missing pieces in Texas: these look like some counties which were only half rendered (they ended up as triangles instead of rectangles or squares?)
Larger
Apologies for the huge code dump, I tried to pare it down as much as possible.
setup:
/* initialize the scene, camera, light, and background plane */
var Map = function(params) {
this.width = params.width;
this.height = params.height;
this.container = params.target || document.body;
this.renderer = new THREE.WebGLRenderer({antialias: true});
this.renderer.setSize(this.width, this.height);
this.renderer.setClearColorHex(0x303030, 1.0);
this.container.appendChild(this.renderer.domElement);
this.camera = new THREE.PerspectiveCamera(45, this.width / this.height,
1, 10000);
this.scene = new THREE.Scene();
this.scene.add(this.camera);
this.camera.position.z = 550;
this.camera.position.x = 0;
this.camera.position.y = 550;
this.camera.lookAt(this.scene.position);
this.projection = d3.geo.albersUsa()
.scale(1000)
.translate([250, 0]);
var pointLight = new THREE.PointLight(0xFFFFFF);
pointLight.position.x = 800;
pointLight.position.y = 800;
pointLight.position.z = 800;
var plane = new THREE.Mesh(
new THREE.PlaneGeometry(10000, 10000, 10, 10),
new THREE.MeshLambertMaterial({color: 0xffffff})
);
plane.rotation.x = -Math.PI/2;
this.scene.add(pointLight);
this.scene.add(plane);
};
rendering:
/* given a GeoJSON Feature, return a list of Vector2s
* describing where to draw the feature, using the provided projection. */
function path(proj, feature) {
if (feature.geometry.type == 'Polygon') {
return polygonPath(proj, feature.geometry.coordinates);
} else if (feature.geometry.type == 'MultiPolygon') {
return multiPolygonPath(proj, feature.geometry.coordinates);
}
}
/* a GeoJSON Polygon is a set of 'rings'. The first ring is the shape of the polygon.
* each subsequent ring is a hole inside that polygon. */
function polygonPath(proj, rings) {
var list = [];
var cur = [];
$.each(rings, function(i, ring) {
cur = [];
$.each(ring, function(i, coord) {
var pts = proj(coord);
cur.push(new THREE.Vector2(pts[0], pts[1]));
});
list.push(cur);
});
return list;
}
/* a GeoJSON MultiPolgyon is just a series of Polygons. */
function multiPolygonPath(proj, polys) {
var list = [];
$.each(polys, function(i, poly) {
list.push(polygonPath(proj, poly));
});
return list;
}
/* for each feature, find it's X/Y Path, create shape(s) with the required holes,
* and extrude the shape */
function renderFeatures(proj, features, scene, isState) {
var color = 0x33ccff;
$.each(features, function(i, feature) {
var polygons = path(proj, feature);
if (feature.geometry.type != 'MultiPolygon') {
polygons = [polygons];
}
$.each(polygons, function(i, poly) {
var shape = new THREE.Shape(poly[0]);
if (poly.length > 1) {
shape.holes = poly.slice(1).map(function(item) { return new THREE.Shape(item); });
}
var geom = new THREE.ExtrudeGeometry(shape, { amount: 20, bevelEnabled: false });
var c = new THREE.Mesh(geom, new THREE.MeshLambertMaterial({color: color}) );
c.rotation.x = Math.PI/2;
c.translateX(-290);
c.translateZ(50);
c.translateY(5);
scene.add(c);
});
});
}
Map.prototype.renderCounties = function() {
$.getJSON('/data/us-counties.json', function(json) {
renderFeatures(this.projection, json.features, this.scene, false);
this.renderer.render(this.scene, this.camera);
}.bind(this));
}
It looks like the points of the polygon are in the wrong order.

Categories