papervision3dで地形と等高線を表示
* papervision3dで地形と等高線を表示。
* 地形データは「標高データ 立山カルデラ周辺](http://www.vector.co.jp/soft/data/home/se021130.html)
* を利用させていただいている。
* シフトキーを押しながら、画面をクリックすると、等高線が表示される。
* ドラッグで、カメラ位置を変更できる。
/**
* Copyright termat ( http://wonderfl.net/user/termat )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/z5Nc
*/
/*
* papervision3dで地形と等高線を表示。
* 地形データは「標高データ 立山カルデラ周辺](http://www.vector.co.jp/soft/data/home/se021130.html)
* を利用させていただいている。
* シフトキーを押しながら、画面をクリックすると、等高線が表示される。
* ドラッグで、カメラ位置を変更できる。
*/
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.geom.ColorTransform;
import flash.system.Security;
import org.papervision3d.core.geom.Lines3D;
import org.papervision3d.core.geom.renderables.Line3D;
import org.papervision3d.materials.ColorMaterial;
import org.papervision3d.materials.special.LineMaterial;
import org.papervision3d.view.BasicView;
import org.papervision3d.core.geom.TriangleMesh3D;
import org.papervision3d.core.geom.renderables.Vertex3D;
import org.papervision3d.core.geom.renderables.Triangle3D;
import org.papervision3d.objects.DisplayObject3D;
import org.papervision3d.core.math.NumberUV;
import org.papervision3d.materials.utils.MaterialsList;
[SWF(width = "500", height = "500", backgroundColor = "0x000000", fps = "30")]
public class Mountain extends BasicView
{
private var loader:URLLoader;
private var node:Array;
private var elem:Vector.<Array>;
private var map:Vector.<Array>;
private var mx:int;
private var my:int;
private var rangeZ:Number;
private var minZ:Number;
private var mesh:TriangleMesh3D;
private var rootNode:DisplayObject3D;
private var contour:DisplayObject3D;
private var isPress:Boolean=false;
private var cameraPitch:Number = 61.75;
private var cameraYaw:Number = 21;
private var cameraTarget:DisplayObject3D = DisplayObject3D.ZERO;
private var preX:Number;
private var preY:Number;
public function Mountain():void{
super(500, 500, false, false);
rootNode = new DisplayObject3D();
scene.addChild(rootNode);
stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
stage.addEventListener(Event.ENTER_FRAME, update);
/*5iVESTAR.ORGさん(http://5ivestar.org/blog/)のプロキシを利用させていただいている。*/
Security.loadPolicyFile("http://termat.sakura.ne.jp/crossdomain.xml");
loader = new URLLoader();
loader.addEventListener(Event.COMPLETE,onCompleted);
loader.load(new URLRequest("http://termat.sakura.ne.jp/air/data.csv"));
}
private function update(e:Event):void {
if (!isPress) {
cameraYaw += 2;
cameraYaw %= 360;
camera.orbit(cameraPitch, cameraYaw, true, cameraTarget);
}
}
private function onMouseDown(e:MouseEvent):void{
isPress = true;
preX = e.stageX;
preY = e.stageY;
if (e.shiftKey) {
if (contour == null) {
contour = new DisplayObject3D();
rootNode.addChild(contour);
var num:Number = Math.round(minZ / 10) * 10;
for (; num < minZ + rangeZ; num += 10) {
if (num % 200 == 0) {
drawContour(num);
}
}
}else {
if (contour.parent == rootNode) {
rootNode.removeChild(contour);
}else {
rootNode.addChild(contour);
}
}
}
}
private function onMouseUp(e:MouseEvent):void{
isPress = false;
}
private function onMouseMove(e:MouseEvent):void{
if(isPress){
cameraPitch += (e.stageY - preY) * 0.25;
cameraYaw += (e.stageX - preX) * 0.25;
cameraPitch %= 360;
cameraYaw %= 360;
cameraPitch = cameraPitch > 0 ? cameraPitch : 0.0001;
cameraPitch = cameraPitch < 180 ? cameraPitch : 179.9999;
preX = e.stageX;
preY = e.stageY;
camera.orbit(cameraPitch, cameraYaw, true, cameraTarget);
}
}
private function onCompleted(e:Event):void {
node = new Array();
var data:Array = parseCSV(loader.data);
my = (data[0] as Array).length;
mx = data.length;
var maxZ:Number = -1e12;
minZ = 1e12;
var centerX:Number = mx * 100 / 2;
var centerY:Number = my * 100 / 2;
for (var i:int = 0; i < data.length; i++) {
for (var j:int = 0; j < data[i].length; j++) {
var z:Number = parseFloat(data[i][j]);
var p:Vertex3D = new Vertex3D(i * 100 - centerX, z, (my-j-1) * 100 - centerY);
node.push(p);
minZ = Math.min(minZ, z);
maxZ = Math.max(maxZ, z);
}
}
camera.z = -my * 100 * 1.2;
rangeZ = maxZ - minZ;
createMap();
mesh = new TriangleMesh3D(new ColorMaterial( 0x006699, 1 ), node, new Array(), null );
rootNode.addChild(mesh);
for (i = 0; i < elem.length; i++) {
createTriMesh(node[elem[i][0]], node[elem[i][1]], node[elem[i][2]]);
}
startRendering();
}
private function parseCSV(str:String):Array {
str = (str.split("\r\n")).join("\n");
str = (str.split("\r")).join("\n");
var data:Array = new Array();
var tmp:Array = str.split("\n");
var l:Number = tmp.length;
for(var i:Number=0; i<l; i++){
data.push(tmp[i].split(","));
}
if(data[data.length-1].length == 1 && data[data.length-1][0] == ""){data.pop();}
return data;
}
private function createTriMesh(p0:Vertex3D, p1:Vertex3D, p2:Vertex3D):void {
var col:Number = getColor( ((p0.y + p1.y + p2.y) / 3.0 - minZ) / rangeZ);
var mat:ColorMaterial = new ColorMaterial(col, 1.0);
var tri:TriangleMesh3D = new TriangleMesh3D( null, [ p0, p1, p2 ], [] );
mesh.geometry.faces.push( new Triangle3D( tri, [ p0, p1, p2 ], mat, [new NumberUV( 0.5, 1 ), new NumberUV( 0, 0 ), new NumberUV( 1, 0 )] ));
tri.geometry.ready = true;
}
private function getColor(v:Number):Number {
var r:Number = Math.round( -238.0 * v * v + 493.0 * v);
var g:Number = Math.round(480.0 * v * v - 480.0 * v + 255.0);
var b:Number = Math.round(462*v*v-207*v);
if (r > 255) r = 255;
if (g > 255) g = 255;
if (b > 255) b = 255;
if (b < 0) b = 0;
return (r<<16)+(g<<8)+b;
}
private function drawContour(val:Number):void {
var lm:LineMaterial = new LineMaterial(0xff0000,0.9);
var lines:Lines3D = new Lines3D(lm);
var count:int = 0;
for (var i:int = 0; i < elem.length; i++) {
var d:Array = new Array(node[elem[i][0]], node[elem[i][1]], node[elem[i][2]]);
var p:Array = sort(new Array(0, 1, 2), d);
if (val >= p[0].y) {
if (val > p[2].y) {
continue;
}else {
var a:Vertex3D;
var b:Vertex3D;
var line:Line3D;
if(val>=p[1].y){
a = getPoint(p[0], p[2], val);
b = getPoint(p[1], p[2], val);
if (a == null || b == null) continue;
line = new Line3D(lines, lm, 0.5, a, b);
lines.addLine(line);
count++;
}else{
a = getPoint(p[0], p[2], val);
b = getPoint(p[0], p[1], val);
if(a==null||b==null)continue;
line = new Line3D(lines, lm, 0.5, a, b);
lines.addLine(line);
count++;
}
}
}else{
continue;
}
}
if (count > 0) contour.addChild(lines);
}
private function sort(it:Array,d:Array):Array{
for (var i:int = 1; i < it.length; i++) {
if (d[it[i]].y < d[it[i - 1]].y) {
var t:int=it[i-1];
it[i-1]=it[i];
it[i]=t;
return sort(it,d);
}
}
return new Array(d[it[0]],d[it[1]],d[it[2]]);
}
private function getPoint(small:Vertex3D,large:Vertex3D,val:Number):Vertex3D{
if(small.y==large.y)return null;
var rr:Number=(val-small.y)/(large.y-small.y);
var x:Number=small.x;
var z:Number=small.z;
var xx:Number=(large.x-x)*rr+x;
var zz:Number=(large.z-z)*rr+z;
var l10:Number=Math.sqrt(Math.pow(xx-x, 2)+Math.pow(zz-z, 2));
var l11:Number=Math.sqrt(Math.pow(xx-large.x, 2)+Math.pow(zz-large.z, 2));
var ret:Vertex3D = new Vertex3D();
var i:int;
if (l10 == 0) {
ret.x = small.x;
ret.y = small.y;
ret.z = small.z;
}else if (l10 == 1) {
ret.x = large.x;
ret.y = large.y;
ret.z = large.z;
}else{
var r0:Number=1/l10;
var r1:Number=1/l11;
ret.x = xx;
ret.z = zz;
ret.y = val;
}
return ret;
}
private function createMap():void {
elem = new Vector.<Array>();
map = new Vector.<Array>();
var id:int = 0;
for (var i:int = 0; i < mx; i++) {
for (var j:int = 0; j < my; j++) {
if (i < mx - 1 && j < my - 1) {
var i0:Array=new Array(i*my+j,(i+1)*my+j+1,(i+1)*my+j);
var i1:Array=new Array(i*my+j,i*my+j+1,(i+1)*my+j+1);
elem.push(i0);
elem.push(i1);
}else{
continue;
}
if (i == mx - 1 || j == my - 1) continue;
var j0:Array;
var j1:Array;
if(i==0){
if(j==0){
j0=new Array(id+1,id+my*2-1,-1);
id++;
map.push(j0);
j1=new Array(-1,id+1,id-1);
map.push(j1);
id++;
}else if(j==my-2){
j0=new Array(id+1,id+my*2-1,id-1);
id++;
map.push(j0);
j1=new Array(-1,-1,id-1);
map.push(j1);
id++;
}else{
j0=new Array(id+1,id+my*2-1,id-1);
id++;
map.push(j0);
j1=new Array(-1,id+1,id-1);
map.push(j1);
id++;
}
}else if(i==mx-2){
if(j==0){
j0 = new Array(id + 1, -1, -1);
id++;
map.push(j0);
j1=new Array(id-(my*2-1),id+1,id-1);
map.push(j1);
id++;
}else if(j==my-2){
j0=new Array(id+1,-1,id-1);
id++;
map.push(j0);
j1=new Array(id-(my*2-1),-1,id-1);
map.push(j1);
id++;
}else{
j0=new Array(id+1,-1,id-1);
id++;
map.push(j0);
j1=new Array(id-(my*2-1),id+1,id-1);
map.push(j1);
id++;
}
}else{
if(j==0){
j0=new Array(id+1,id+my*2-1,-1);
id++;
map.push(j0);
j1=new Array(id-(my*2-1),id+1,id-1);
map.push(j1);
id++;
}else if(j==my-2){
j0=new Array(id+1,id+my*2-1,id-1);
id++;
map.push(j0);
j1=new Array(id-(my*2-1),-1,id-1);
map.push(j1);
id++;
}else{
j0=new Array(id+1,id+my*2-1,id-1);
id++;
map.push(j0);
j1=new Array(id-(my*2-1),id+1,id-1);
map.push(j1);
id++;
}
}
}
}
}
}
}