我刚刚开始使用three.js,并且在挤出一些二维形状时遇到了一些问题。
我有一个包含美国所有县的 GeoJSON 文件。我正在使用 d3.js 和d3.geo.albersUSa()
投影将每个纬度/经度转换为 X/Y 坐标列表,THREE.Shape
然后制作一个我正在挤压和绘图的列表。这似乎对大多数县都有效。
我看到的问题是,某些县的子集要么无法挤出,要么挤出不正确,并出现以下警告序列:
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.
我不确定我是否完全理解问题所在——据我所知,这些特殊形状并没有什么特别之处。我做错了什么,还是这与three.js中的挤压代码有关?
例如,这里有一些缺失的县:
还要注意德克萨斯州丢失的三角形“沙漏”部分:这些看起来像一些只渲染了一半的县(它们最终变成三角形而不是矩形或正方形?)
为巨大的代码转储道歉,我试图尽可能地减少它。
设置:
/* 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);
};
渲染:
/* 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));
}