pdbファイルを読み込んで分子構造を表示
原子の色はセンスが悪いです。
データは「フタル酸ジ-2-エチルヘキシル」です。
http://www3.u-toyama.ac.jp/kihara/cc/index.html
にあったので使用しています。
(HP上に「データの利用については制限を設けません」とあったので利用しています)
たぶん他のPDB(Protein Data Bank)データも読むことができます。
ここにWEB素材として使用できる形のものがあります。(原子の画像等)
http://programmingatelier.net/flex4_51/flex4_5107.html
/**
* Copyright hi.kurosawa ( http://wonderfl.net/user/hi.kurosawa )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/3Bn2
*/
//================================================
//分子構造の表示
//
// http://programmingatelier.net/
//
package
{
import flash.display.Sprite;
import flash.display.Bitmap;
import flash.display.Loader;
import flash.net.URLRequest;
import flash.net.URLLoader;
import flash.net.URLLoaderDataFormat;
import flash.system.LoaderContext;
import flash.display.BitmapData;
import flash.events.Event;
import flash.geom.ColorTransform;
import mx.utils.StringUtil;
import org.papervision3d.materials.*;
import org.papervision3d.objects.primitives.*;
import org.papervision3d.objects.*;
import org.papervision3d.lights.*;
import org.papervision3d.materials.shadematerials.*;
import org.papervision3d.materials.utils.MaterialsList;
import org.papervision3d.materials.special.CompositeMaterial;
import org.papervision3d.view.*;
import org.papervision3d.core.geom.Lines3D;
import org.papervision3d.core.geom.renderables.Line3D;
import org.papervision3d.core.geom.renderables.Vertex3D;
import org.papervision3d.materials.special.LineMaterial;
import org.papervision3d.typography.fonts.HelveticaMedium;
import org.papervision3d.materials.special.Letter3DMaterial;
import org.papervision3d.typography.Text3D;
import org.papervision3d.typography.fonts.HelveticaBold;
import org.papervision3d.events.InteractiveScene3DEvent;
import org.papervision3d.core.math.Number3D;
import flash.system.Security;
import flash.utils.ByteArray;
import flash.geom.Point;
import flash.geom.Rectangle;
public class Molecule extends BasicView
{
//使用画像定義(黒白の原子のイメージ)
private var bmAtom:Bitmap;
private var bmdAtom:BitmapData;
private var loader:Loader;
private static var filePath:String = "http://programmingatelier.net/publicSwf/data/Atom.png";
//3D形状の配置領域
private var rootNode:DisplayObject3D;
//データのあるパス
private var strDatUrl:String;
//表示Atom配列、カメラ移動時の向きの調整用
private var arrObj:Array;
//PDB読み込み原子と結合線データ
private var arrAtom:Array;
//Typ:ATOM/HETATM,nam:名称,id:NO,x,y,z:原子の座標値*100
private var arrConect:Array;
//StrCon:開始点側原子ID, EndCon:終点側原子ID
private var kakudo0:Number;
private var kakudo1:Number;
private var kakudo2:Number;
public function Molecule():void {
arrObj = new Array();
arrAtom = new Array();
arrConect = new Array();
Security.loadPolicyFile("http://programmingatelier.net/publicSwf/crossdomain.xml");
loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, complete, false, 0, true);
loader.load(new URLRequest(filePath), new LoaderContext(true));
}
private function complete(evt:Event):void {
loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, complete);
bmAtom = Bitmap(loader.content);
bmdAtom = bmAtom.bitmapData;
var urlLoader:URLLoader = new URLLoader();
urlLoader.dataFormat = URLLoaderDataFormat.BINARY;
urlLoader.addEventListener(Event.COMPLETE, onCompUrlLoad);
//http://www3.u-toyama.ac.jp/kihara/cc/index.html
//http://www3.u-toyama.ac.jp/kihara/cc/pdb/c1-15-3/index.html
//フタル酸ジ-2-エチルヘキシル
//HP上に「データの利用については制限を設けません」とあったので利用しています
urlLoader.load(new URLRequest("http://programmingatelier.net/publicSwf/data/c0013.pdb"));
}
//読み込み完了
private function onCompUrlLoad(event:Event):void {
var urlLoader:URLLoader = event.currentTarget as URLLoader;
var bytArra:ByteArray = urlLoader.data as ByteArray;
fncChkData(bytArra); //データ処理、表示処理
}
//読み込みデータ処理、表示処理==============================================
private function fncChkData(bytArra:ByteArray):void {
var minX:Number = 0, maxX:Number = 0, minY:Number = 0;
var maxY:Number = 0, minZ:Number = 0, maxZ:Number = 0;
arrConect = new Array();
arrAtom = new Array();
arrObj = new Array();
try {
//改行の変更
var strData:String = bytArra.toString();
var arrDat:Array = strData.split("\r\n"); //Windowsの改行対応
strData = arrDat.join("\n");
arrDat = strData.split("\n"); //行の分解
for (var i:int = 0; i < arrDat.length; i++) { //1行ごとの処理
//原子
if (arrDat[i].indexOf("ATOM") == 0 || arrDat[i].indexOf("HETATM") == 0) {
arrAtom.push( {
Typ:StringUtil.trim(arrDat[i].substr( 0, 6 )),
nam:StringUtil.trim(arrDat[i].substr( 11, 5 )),
id:int(arrDat[i].substr( 6, 5 )),
x:Number(arrDat[i].substr( 30, 8 ))*100.0,
y:Number(arrDat[i].substr( 38, 8 ))*100.0,
z:Number(arrDat[i].substr( 46 ,8 ))*100.0
});
//原子の配置領域を求める
var ii:Number = arrAtom.length - 1;
if (arrAtom.length == 1) {
minX = arrAtom[ii].x;
maxX = arrAtom[ii].x;
minY = arrAtom[ii].y;
maxY = arrAtom[ii].y;
minZ = arrAtom[ii].z;
maxZ = arrAtom[ii].z;
} else {
if(minX > arrAtom[ii].x) {minX = arrAtom[ii].x;}
if(maxX < arrAtom[ii].x) {maxX = arrAtom[ii].x;}
if(minY > arrAtom[ii].y) {minY = arrAtom[ii].y;}
if(maxY < arrAtom[ii].y) {maxY = arrAtom[ii].y;}
if(minZ > arrAtom[ii].z) {minZ = arrAtom[ii].z;}
if(maxZ < arrAtom[ii].z) {maxZ = arrAtom[ii].z;}
}
}
//結合線
else if (arrDat[i].indexOf("CONECT") == 0) {
var s:String = arrDat[i].substr( 6, 5 );
var s0:String = arrDat[i];
var iStrCon:int = int(arrDat[i].substr( 6, 5 ));
for (var j:int = 11; j < arrDat[i].length; j += 5) {
var s2:String = arrDat[i].substr( j, 5 );
var iEndCon:int = int(arrDat[i].substr( j, 5 ));
//結合のデータは始点・終点を逆にして2本あるので片方をセット
if(iStrCon<iEndCon) {
arrConect.push( { StrCon:iStrCon, EndCon:iEndCon } );
}
}
}
}
}
catch(e:*) {
return;
}
//中心座標
var dx:Number = (minX + maxX) / 2.0;
var dy:Number = (minY + maxY) / 2.0;
var dz:Number = (minZ + maxZ) / 2.0;
//表示
fncDispAtom(dx,dy,dz);
}
//原子結合線表示
//dx,dy,dz:中心座標
private function fncDispAtom(dx:Number, dy:Number, dz:Number):void {
//3D形状の配置領域作成
rootNode = new DisplayObject3D();
this.scene.addChild(rootNode);
rootNode.x = 0;
//原子の表示
for (var k:int = 0; k < arrAtom.length; k++) {
var r:Number=30;
var ic:uint = 0x0000ff;
var s:String = arrAtom[k].nam.substr(0, 1);
if (s == "H") {
r = 15;
ic = 0x00ffff;
} else if (s == "C") {
ic = 0x008800;
}
rootNode.addChild(mkAtom(arrAtom[k].nam,
(arrAtom[k].x-dx), (arrAtom[k].y-dy), (arrAtom[k].z-dz), r, ic,0xff00ff));
}
//結合線の表示
for (var l:int = 0; l < arrConect.length; l++) {
var id1:int = getAtomID(arrConect[l].StrCon);
var id2:int = getAtomID(arrConect[l].EndCon);
if (id1 >= 0 && id2 >= 0) {
var r1:Number = 30;
var r2:Number = 30;
var s1:String = arrAtom[id1].nam.substr(0, 1);
if (s1 == "H") {
r1 = 15;
}
var s2:String = arrAtom[id2].nam.substr(0, 1);
if (s2 == "H") {
r2 = 15;
}
rootNode.addChild(mkLine(
(arrAtom[id1].x-dx) , (arrAtom[id1].y-dy) , (arrAtom[id1].z-dz) ,
(arrAtom[id2].x - dx) , (arrAtom[id2].y - dy) , (arrAtom[id2].z - dz),
r1, r2, 0x888888));
}
}
//onSlChmg();
kakudo0 = 0;
kakudo1 = 0;
kakudo2 = 0;
addEventListener(Event.ENTER_FRAME, onFrame);
}
private function onFrame(e:Event):void
{
kakudo0 += 0.3;
kakudo1++;
if (kakudo1 >= 360) { kakudo1 = 0; }
if (kakudo0 >= 360) { kakudo0 -= 360; }
kakudo2 = Math.sin(kakudo0 * Math.PI / 180) * 80;
onSlChmg();
}
//結合線の両端に付く原子を求める
// iCon:原子のID
// 戻り値:arrAtomの位置(-1:対応する原子なし)
private function getAtomID(iCon:int):int {
for (var i:int; i < arrAtom.length; i++) {
if (iCon == arrAtom[i].id) { return i;}
}
return -1;
}
//原子の表示
// nam:表示する名称
// x,y,z:表示位置
// r:半径
// uiAtomCol:原子の色
// uiNameCol:表示名称の色
// 戻り値:オブジェクト
private function mkAtom(nam:String, x:Number, y:Number, z:Number, r:Number,
uiAtomCol:uint,uiNameCol:uint):DisplayObject3D {
var objNode:DisplayObject3D=new DisplayObject3D();
//原子
var objPlane:Plane;
//イメージをコピーする:色の変更のため
var bw:Number = bmAtom.width;
var bh:Number = bmAtom.height;
var bmd:BitmapData = new BitmapData( bw, bh, true, 0 );
bmd.copyPixels( bmdAtom, new Rectangle( 0, 0, bw, bh ), new Point( 0, 0 ) );
var bmp:Bitmap = new Bitmap( bmd );
var matrAtom:BitmapMaterial = new BitmapMaterial(bmp.bitmapData,true);
matrAtom.smooth = true;
matrAtom.oneSide = true;
//matrAtom.doubleSided = true; //裏表示
objPlane = new Plane(matrAtom, r * 2, r * 2, 1, 1);
objNode.addChild(objPlane);
var ir:uint = uiAtomCol / (256 * 256); //カラーをRGBに分解
var ig:uint = (uiAtomCol / (256))-ir*256;
var ib:uint = uiAtomCol % 256;
//イメージの黒色部分を指定カラーに変更
objPlane.material.bitmap.colorTransform(objPlane.material.bitmap.rect,
new ColorTransform(1.0,1.0,1.0,1.0, ir, ig, ib,0));
//名称の表示
var LMaterial:Letter3DMaterial = new Letter3DMaterial(uiNameCol , 1);
//LMaterial.doubleSided = true; //裏表示
var fnt:HelveticaBold = new HelveticaBold();
var word:Text3D = new Text3D(nam, fnt , LMaterial);
word.z = -10.0; //手前に表示
word.x = 10.0+nam.length*10.0;
word.y = 20.0;
word.scale = 0.5;
objNode.addChild(word);
//(原子+名称)の位置
objNode.x = x;
objNode.y = y;
objNode.z = z;
//表示処理用の配列にセット
arrObj.push(objNode);
return objNode;
}
//結合線
// x1,y1,z1,x2,y2,z3:結合線の始終点
// r1,r2:付く原子の半径(半径分表示線分を短くする)
// ic:カラー
private function mkLine(x1:Number, y1:Number, z1:Number,
x2:Number, y2:Number, z2:Number,
r1:Number, r2:Number, ic:uint):Lines3D {
//ラインの単位ベクトルを求める
var dx:Number = x2 - x1;
var dy:Number = y2 - y1;
var dz:Number = z2 - z1;
var dd:Number = Math.sqrt(dx * dx + dy * dy + dz * dz);
if (dd > 0.0) {
dx = dx / dd;
dy = dy / dd;
dz = dz / dd;
}
//線を引く
var lm:LineMaterial = new LineMaterial(ic);
var lines3D:Lines3D = new Lines3D();
var startV:Vertex3D = new Vertex3D(x1+r1*dx, y1+r1*dy, z1+r1*dz);
var endV:Vertex3D = new Vertex3D(x2-r2*dx, y2-r2*dy, z2-r2*dz);
var line:Line3D = new Line3D(lines3D, lm, 5, startV, endV);
lines3D.addLine(line);
return lines3D;
}
//Sliderの値変更:カメラの位置セット、形状表示
private function onSlChmg():void {
//カメラ位置
var kyori:Number = 800;//原点・カメラの距離
//高さ方向の角度(ラジアン)
var ang1:Number = kakudo2 * Math.PI / 180.0;
//方向(ラジアン)
var ang2:Number = (kakudo1-180) * Math.PI / 180.0;
var x:Number=kyori*Math.cos(ang1)*Math.sin(ang2);
var y:Number=kyori*Math.cos(ang1)*Math.cos(ang2);
var z:Number = kyori*Math.sin(ang1);
this.camera.x = x;
this.camera.z = y;
this.camera.y = z;
//原子(Plenで表示)を正面を向かせる。
for (var i:int = 0; i < arrObj.length; i++) {
//lookAtで原子をカメラに向けると文字等がうまく表示されない
//arrObjAtom[i].lookAt( bv.camera );
//arrObjAtom[i].yaw(180);
arrObj[i].rotationX = kakudo2;
arrObj[i].rotationY = kakudo1;
}
//描画処理
this.renderer.renderScene( this.scene , this.camera , this.viewport );
}
}
}