基于斥力-张力模型的网络拓扑布局算法(php代码)

根据之前的C++代码改写:

<?php
	//**********************************************************//
	//date:2014.11.07 @ bupt
	//本PHP文件包括顶点类Point、图类Graph、以及相关函数
	//Graph对象的构造函数的输入为(顶点个数:$_Nv,x轴最小值:$_xmin,x轴最大值:$_xmax,y轴最小值:$_ymin,y轴最大值:$_ymax,邻接矩阵$_mat)
	//Graph对象的initLayoutProc()方法是初始化布局过程
	//Graph对象的layOut()方法是进行一次布局
	//当Graph对象的成员变量温度T小于1时,停止布局,根据画布大小进行平移和缩放
	//**********************************************************//
	//
	//示例程序如下:
	//$tNv = 4;//顶点的个数
	//$txmin = 0;//x轴最小值
	//$txmax = 400;//x轴最大值
	//$tymin = 0;//y轴最小值
	//$tymax = 400;//y轴最大值
	//
	////邻接矩阵
	//$tmat = array(
	//	0=>array(0,1,1,0),
	//	1=>array(1,0,1,1),
	//	2=>array(1,1,0,1),
	//	3=>array(0,1,1,0)
	//	);
	//
	//
	//$g = new Graph($tNv,$txmin,$txmax,$tymin,$tymax,$tmat);//初始化拓扑图
	//$g->initLayoutProc();//初始化布局过程
	//$g->outPut();//输出图
	//while ($g->T >= 1) {
	//	//$g->layOut();//进行一次布局
	//}	
	//$g->layOut();//$this->T<1时,此时布局部已完成,进行平移和缩放
	//$g->outPut();//
	//**********************************************************//
	//
	//
	//
	//需要用到的函数
	function init3($v,$x,$y){//初始化
		$v[0]=$x;
		$v[1]=$y;
		return $v;
	}
	function len3($v){//计算模
		$len=sqrt($v[0]*$v[0]+$v[1]*$v[1]);
		if($len==0)$len=0.001;
		return $len;
	}
	function len3_2($v){//计算平方和
		return $v[0]*$v[0]+$v[1]*$v[1];
	}
	function sub3($a,$b,$rs){//计算向量的差
		$rs[0]=$a[0]-$b[0];
		$rs[1]=$a[1]-$b[1];
		return $rs;
	}
	function add3($a,$b,$rs){//计算向量的和
		$rs[0]=$a[0]+$b[0];
		$rs[1]=$a[1]+$b[1];
		return $rs;
	}
	function mul3($k,$v,$rs){//计算向量与常数的乘积
		$rs[0]=$k*$v[0];
		$rs[1]=$k*$v[1];
		return $rs;
	}
	function fa($d,$K){//引力位移
		return $d*$d/$K;
	}
	function fr($d,$K){//斥力位移
		return $K*$K/$d;
	}
	////////////////////////////////////////////////

	//顶点类
	class Point
	{
		var $pos;//点的位置
		var $offset;//点的位移
		var $weight;//点的重量
		var $degree;//点的度数
		var $force;//合力

		function __construct()//构造函数
		{
			$this->pos = array(0,0);
			$this->offset = array(0,0);
			$this->weight=0;
			$this->degree=0;
			$this->force=array(0,0);
		}

	}

	//图类
	class Graph
	{
		
		var $Nv;//顶点的个数
		var $xmin;//x轴最小值
		var $xmax;//x轴最大值
		var $ymin;//y轴最小值
		var $ymax;//y轴最大值
		var $mat;//邻接矩阵vector<vector<bool> > mat;
		var $vlist;//顶点列表vector<Cvertex> vlist;
		
		//引力-斥力模型
		var $K;//引力常数
		var $T;//当前温度
		var $Jf;//前一次的目标函数值
		var $decay;//衰减常数

		function __construct($_Nv,$_xmin,$_xmax,$_ymin,$_ymax,$_mat)//复杂构造函数
		{
			$this->Nv=$_Nv;
			$this->xmin=$_xmin;
			$this->xmax=$_xmax;
			$this->ymin=$_ymin;
			$this->ymax=$_ymax;
			$this->mat=$_mat;
			$this->decay=0.85;

			for($i=0;$i<$_Nv;$i++){
				$this->vlist[$i]=new Point();
				$this->vlist[$i]->pos[0] = rand($this->xmin,$this->xmax);
				$this->vlist[$i]->pos[1] = rand($this->ymin,$this->ymax);

			}


		}

		function shutLayoutProc(){//关闭布局过程
			$this->T=0;
		}

		function initLayoutProc(){//初始化布局过程
		//初始化引力常数
			$INF=100000000;
			$area=($this->xmax-$this->xmin)*($this->ymax-$this->ymin);
			$this->K=pow($area/$this->Nv,0.5);		
			//初始化温度
			$this->T=($this->xmax-$this->xmin);
			//初始化目标函数值
			$this->Jf=$INF;
			//初始化衰减系数
			$decay=0.9;
		}

		function layOut(){//进行一次布局
			$INF=100000000;
			if($this->T<=1){	//T为当前温度
				//此时布局部已完成,进行平移和缩放
				//求外接矩形
					$left=$INF;
					$right=-$INF;
					$up=-$INF;
					$down=$INF;
					for($i=0;$i<=$this->Nv-1;$i++){
						if($this->vlist[$i]->pos[0]<$left)$left=$this->vlist[$i]->pos[0];
						if($this->vlist[$i]->pos[0]>$right)$right=$this->vlist[$i]->pos[0];
						if($this->vlist[$i]->pos[1]>$up)$up=$this->vlist[$i]->pos[1];
						if($this->vlist[$i]->pos[1]<$down)$down=$this->vlist[$i]->pos[1];
					}//得到left,right,up,down

					echo "</br>left:".$left.",right:".$right.",up:".$up.",down:".$down."</br></br>";

					//求外接矩形中心
					$_cx=($left+$right)/2;
					$_cy=($up+$down)/2;
					//求画板中心
					$cx=($this->xmin+$this->xmax)/2;
					$cy=($this->ymin+$this->ymax)/2;
					//由外接矩形中心向画板中心的平移向量
					$vec = array($cx-$_cx,$cy-$_cy);
					//按平移向量对图形进行平移,但只移若干分之一,而不一步到位,为的是动画效果
					for($i=0;$i<=$this->Nv-1;$i++){
						$this->vlist[$i]->pos[0]+=$vec[0];
						$this->vlist[$i]->pos[1]+=$vec[1];
					}
					//求外接矩形的最大边心距
					$r=max(($this->xmax-$this->xmin)/2,($this->ymax-$this->ymin)/2);
					//求边心距的最大可增加量
					$dr=min(($this->xmax-$this->xmin)/2-($right-$left)/2,($this->ymax-$this->ymin)/2-($up-$down)/2);
					$dr=$dr-($this->xmax-$this->xmin)/15;//防止顶天
					//$_dr=$dr/1000;//只取最大可增加量的若干分之一,为的是动画效果
					//求缩放系数
					$k=($r+$dr)/$r;
					//按k对图形进行缩放,
					for($i=0;$i<=$this->Nv-1;$i++){
						$this->vlist[$i]->pos[0]=($this->vlist[$i]->pos[0]-$_cx)*$k+$_cx;
						$this->vlist[$i]->pos[1]=($this->vlist[$i]->pos[1]-$_cy)*$k+$_cy;
					}				
				return;
			}
			//将各顶点的offset恢复为0
			for($i=0;$i<=$this->Nv-1;$i++){
				$this->vlist[$i]->offset = init3($this->vlist[$i]->offset,0,0);
			}
			//在斥力作用下位移
			for($i=0;$i<=$this->Nv-1;$i++){
				for($j=0;$j<$i;$j++){
					//计算位移向量offset和_offset
					$diff=array(0,0);

					/*
					echo "i:".$i.",j:".$j;

					echo "</br>vlist_i_pos:";
					echo $this->vlist[$i]->pos[0];
					echo ",";
					echo $this->vlist[$i]->pos[1];
					echo "</br>";

					echo "</br>vlist_j_pos:";
					echo $this->vlist[$j]->pos[0];
					echo ",";
					echo $this->vlist[$j]->pos[1];
					echo "</br>";//*/

					$diff = sub3($this->vlist[$i]->pos,$this->vlist[$j]->pos,$diff);
					$len_diff = len3($diff);//|diff|

					/*
					echo "</br>diff:";
					echo $diff[0];
					echo ",";
					echo $diff[1];
					echo "</br>";

					echo "</br>len_diff:";
					echo $len_diff;
					echo "</br>";//*/

					$e_diff = array(0,0);//diff/|diff|
					$e_diff = mul3(1.0/$len_diff,$diff,$e_diff);
					$offset = array(0,0);//(diff/|diff|)*fr(|diff|)
					$offset = mul3(fr($len_diff,$this->K),$e_diff,$offset);
					$_offset = array(0,0);//-offset
					$_offset = mul3(-1,$offset,$_offset);
					//累计位移量
					$this->vlist[$i]->offset = add3($this->vlist[$i]->offset,$offset,$this->vlist[$i]->offset);
					$this->vlist[$j]->offset = add3($this->vlist[$j]->offset,$_offset,$this->vlist[$j]->offset);
				}
			}
			//在引力作用下位移
			for($i=0;$i<=$this->Nv-1;$i++){
				for($j=0;$j<$i;$j++){
					if($this->mat[$i][$j]){
						//计算位移向量offset和_offset
						$diff=array(0,0);
						$diff=sub3($this->vlist[$i]->pos,$this->vlist[$j]->pos,$diff);
						$len_diff=len3($diff);//|diff|
						$e_diff=array(0,0);//diff/|diff|
						$e_diff=mul3(1.0/$len_diff,$diff,$e_diff);
						$offset=array(0,0);//(diff/|diff|)*fr(|diff|)
						$offset=mul3(fa($len_diff,$this->K),$e_diff,$offset);
						$_offset=array(0,0);//-offset
						$_offset=mul3(-1,$offset,$_offset);
						//累计位移量
						$this->vlist[$i]->offset=add3($this->vlist[$i]->offset,$_offset,$this->vlist[$i]->offset);
						$this->vlist[$j]->offset=add3($this->vlist[$j]->offset,$offset,$this->vlist[$j]->offset);
					}
				}
			}
			//进行位移(限制移动距离不超过T)
			for($i=0;$i<=$this->Nv-1;$i++){
				$len_offset=array(0,0);

				/*
				echo "</br>vlist_offset";
				echo $this->vlist[$i]->offset[0];
				echo ",";
				echo $this->vlist[$i]->offset[1];
				echo "</br>";//*/

				$len_offset=len3($this->vlist[$i]->offset);//位移量的模
				$e_offset=array(0,0);//位移量的方向
				$e_offset=mul3(1.0/$len_offset,$this->vlist[$i]->offset,$e_offset);//得到e_offset
				$len_offset=min($len_offset,$this->T);//得到len_offset
				$offset=array(0,0);//位移量
				$offset=mul3($len_offset,$e_offset,$offset);//得到offset
				/*
				echo "</br>len_offset:";
				echo $len_offset;
				echo "</br>";

				echo "</br>e_offset:";
				echo $e_offset[0];
				echo ",";
				echo $e_offset[1];
				echo "</br>";

				echo "</br>offset:";
				echo $offset[0];
				echo ",";
				echo $offset[1];
				echo "</br>";//*/
				$this->vlist[$i]->pos=add3($this->vlist[$i]->pos,$offset,$this->vlist[$i]->pos);
			}
			//降温
	    	$this->T=$this->decay*$this->T;	
		}

		function outPut(){
			echo "Point Number:".$this->Nv;
			echo "</br>";
			echo "</br>";
			echo "Adjacent Matrix:";
			echo "</br>";
			for($i=0;$i<$this->Nv;$i++){
				for($j=0;$j<$this->Nv;$j++){
					echo $this->mat[$i][$j];
					echo " ";
				}
				echo "</br>";
			}
			echo "</br>";

			echo "x:".$this->xmin.",".$this->xmax;
			echo "</br>";
			echo "y:".$this->ymin.",".$this->ymax;
			echo "</br>";
			echo "</br>";

			echo "Repulsive Const:".$this->K;
			echo "</br>";
			echo "Now Temperature:".$this->T;
			echo "</br>";
			echo "Prev Function Value:".$this->Jf;
			echo "</br>";
			echo "Attenuation Const:".$this->decay;
			echo "</br>";

			echo "</br>";
			echo "Point Pos:";
			echo "</br>";
			for($i=0;$i<$this->Nv;$i++){
				echo $i;
				echo ":";
				echo $this->vlist[$i]->pos[0];
				echo ",";
				echo $this->vlist[$i]->pos[1];
				echo "</br>";
			}

		}

	}

	//echo $INF;
	$tNv = 4;//顶点的个数
	$txmin = 0;//x轴最小值
	$txmax = 400;//x轴最大值
	$tymin = 0;//y轴最小值
	$tymax = 400;//y轴最大值

	//邻接矩阵
	$tmat = array(
		0=>array(0,1,1,0),
		1=>array(1,0,1,1),
		2=>array(1,1,0,1),
		3=>array(0,1,1,0)
		);

	$g = new Graph($tNv,$txmin,$txmax,$tymin,$tymax,$tmat);//初始化拓扑图
	$g->initLayoutProc();//初始化布局过程
	$g->outPut();//输出图
	while ($g->T >= 1) {
		$g->layOut();//进行一次布局
	}	
	$g->outPut();//
	$g->layOut();//$this->T<1时,此时布局部已完成,进行平移和缩放
	$g->outPut();//
?>

 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。