由于上次lab2几乎把整个报告都放上去了,导致最后很多盆友的写报告的时候直接照样复制了,有点不太好。因此这次我只写了lab3自己的部分实现。其实我ADT的设计挺一般的,而且ddl都到了,我想这个时候大家的实验应该都写完了吧。
由于最近太忙,文章内容我也没怎么认真排版了,大家见谅。
首先请列出你要完成的具体应用场景(至少3个,1和2中选一,3必选,4和5中选一,鼓励完成更多的应用场景)。
分析你所选定的多个应用场景的异同,理解需求:它们在哪些方面有共性、哪些方面有差异。
共性:
1.都具备轨道系统中基本存在的对象,包括轨道、中心点物体、轨道物体.
2.都需要具备很多相同的操作:添加/删除轨道,在某一条轨道山添加/删除物体,从文件中读取轨道(读取方式不同)。
3.获得轨道系统的熵值,获得逻辑距离,比较两个同类型轨道的difference,检查轨道系统的合法化,GUI可视化。
差异:
1.StellarSystem的轨道物体考虑绝对位置,而其他两个系统无需考虑。
2.StellarSystem的轨道需要按照轨道半径进行排序,SocialNetworkCircle则是根据关系的级数进行轨道排序。
3.SocialNetworkCircle需要考虑复杂的映射关系,包括中心点物体映射轨道物体,和轨道物体之间的映射。并且在GUI可视化时,需要根据这些逻辑关系进行连线。
4.每个轨道系统都有着其特殊的功能,包括电子跃迁,计算行星物理距离和增加社交关系等。
分别是在三个应用的读文件中设计正则表达式。然后对正则表达式进行解析匹配。
Pattern pattern = Pattern.compile(args[i]); // 构造正则表达式
Matcher mentioned = pattern.matcher(newFlieStr);//匹配正则表达式
1.StellarSystem:
模拟行星系统的正则表达式比较简单,括号捕获的顺序是从左到右,从外到内(嵌套)。
第一个括号内\\w+是匹配多个字母,即一个单词,读取文件时每行先是一个单词,一个空格,一个'=',一个空格,后加一个max型的匹配,得到'='后所有信息项。
当mention.find时true时,
String[] args = mentioned.group(2).split("[\\s+ ,]");
将尖括号内的信息提取出来,如果后面的信息数目(字符串数组长度)为3,则读取到的是中心点的信息,否则读取的是行星的信息。
2.AtomStructure
对于原子轨道,我换了一种方式进行读取。
首先读入整篇txt,再去掉空格等空白符,使用如上的正则表达式进行全篇的匹配。
第一个正则表示式为了匹配原子核,其中group(2)获取后需要将多匹配到的NumberOfTracks字符串进行截去。
第二个正则表达式是为了获取group(3)中轨道的数目。
第三个正则表达式则是获取每级轨道的电子数目。
3.SocialNetworkCircle
对于SocialNetworkCircle,因为读取SocialTie之前可能还没有读取Friend,因此我没有选择按行读取文件的方式,我依旧选择读取整个文件,去掉空格等空白符后全篇匹配。
按照字符串数组的顺序进行全篇匹配,保证在匹配SocialTie之前,系统中已经读取了Friend,获得mentioned.group(2)之后,再将其中的逗号分开,即可读取用户的信息,和建立用户之间的关系。
1. public double getObjectDistributionEntropy(CircularOrbit c)
计算多轨道系统中各轨道上物体分布的熵值。
计算方法如图:
2. public int getLogicalDistance(CircularOrbit c, E e1, E e2)
计算任意两个物体之间的最短逻辑距离。
和之前实验类似。因为这里的逻辑距离是指最少通过多少边可以连在一起,因此单位都是1,可以使用BFS对EE_IntimacyMap进行搜索。其中获取某个朋友相邻的朋友的时候,可以调用EE_IntimacyMap.getAdjacentObjects()方法返回相邻的物体。
3. public double getPhysicalDistance(CircularOrbit c, E e1, E e2)
计算任意两个物体之间的物理距离。针对StellarSystem。
将半径和角度的极坐标系转化为直角坐标系,计算两点之间的距离。
4. public Difference getDifference(CircularOrbit c1, CircularOrbit c2)
计算两个多轨道系统之间的差异。
Difference类的构造函数如下。Difference类中的方法设计为将构造器传入的参数打印出来,
即输出轨道数、轨道物体等不同。
在获取两个同类型轨道系统difference的时候,还是需要分三种情况进行讨论。
4.1.AtomStructure
轨道数的差异很易得,但是对于特定轨道物体数差异时,需要知道哪个系统的轨道数更多,这样轨道数少的系统在轨道不存在时,视其轨道物体数目为0即可。
4.2.StellarSystem
模拟行星的系统在找difference时和原子轨道系统差别不大。因为行星系统每个轨道上只有一个行星,因此我们在输出轨道物体数目差异的同时,将物体的name打印出来即可。需要注意的依然是对于轨道数较少的系统,当其比较到空轨道时,轨道物体数为0,轨道物体名为“无“。
4.3.SocialNetworkCircle
因为对于社交网络,一个轨道上有多个物体且需要区分,这种情况下表达“物体差异”可以用类似于集合的形式。
此时和StellarSystem的差异在于,找轨道物体差异的时候,需要对系统1某轨道的每个物体,在系统2对应轨道上找不同,并将它们的字符串连接起来。同样的,对于SocialNetworkCircle也需要根据系统轨道数的多少分情况讨论。
1.静态图的实现
由于AtomStructure的GUI实现比较简单,下面只介绍SocialNetworkCircle的GUI实现,此图较AtomStructure的GUI实现,添加了中心物体对于轨道物体的映射连线,和轨道物体之间映射的连线。
具体实现步骤:
1.窗口类:继承自JFrame,初始化分为以下几步:设置窗口标题和窗口大小;设置窗口关闭按钮的默认操作(点击关闭时退出进程);把窗口位置设置到屏幕的中心;设置窗口的内容面板。
2.内容面板类:继承自JPanel。重写paintComponent方法,并在方法中调用drawArc(Graphics g)。
3. public static void visualizes(CircularOrbit c)
这个静态方法主要的目的时获取轨道系统GUI所需要的参数,包括轨道数目,中心物体和轨道物体的映射L2E,轨道物体之间的映射E2E。另外,在 AWT 的事件队列线程中创建窗口和组件,确保线程安全, 即 组件创建、绘制、事件响应 需要处于同一线程。
4.具体谈作图过程:因为是圆轨道,因此我们作图前需要将圆心位置确定,定为(400,400),对于每个轨道,我们使用drawArc画一个360度的圆弧,即圆。对于中心点物体,我们使用fillArc填充一个圆即可。
较为麻烦的是画出轨道物体,并且记录它们的位置进行连线。首先,轨道物体的圆心在轨道圆周上,另外物体自身也可使用fillArc填充为一个实心圆。对于轨道物体特定的位置,我们调用物体的hashCode方法,这样就可以将每个物体映射到一个确定的且唯一的位置。因为我们已经将轨道系统当作参数传入,并且获得了映射关系,因此调用drawLine先将中心物体和第一轨道的物体连线,再找轨道物体之间,如果存在相邻的关系,则将二者连线。
在GUI上可视化AtomStructure如下:
在GUI上可视化SocialNetworkCircle如下:
2.动态图的实现
动态图很多基础的实现和静态图是一致的,这里主要介绍我们究竟如何让图“动“起来。
Circle类:
private Point2D.Double center; //中心坐标
private double radius; //半径
private int tick; //记录在轨道的位置
private int orientation; // 1或-1
private int numOfTrack; //轨道
private String color; //颜色
其中初始的tick是根据轨道物体的角度设置,在轨道上物体运动的过程中,tick是不断加1的;orientation取1或-1,这是为了物体的角度增加或是减少(顺时针或是逆时针);color颜色是根据planet颜色设置,其中包含了fillArc方法。
Fields:
private ArrayList
private ArrayList
centers和orbiters分别是轨道圆和orbiters的集合。
主要的方法:
2.1 private void updateOrbiterLoc()
根据orbiters的tick值,更新其位置。
2.2 public void tick()
相当于时钟的“滴答“,所有轨道物体的tick值发生变化,变化大小为其orientation值(1或-1),每次tick都需要调用updateOrbitLoc方法更新其位置。
2.3 public void draw(Graphics2D g)
centers和orbiters分别调用Circle类中的drawCenter和drawOrbit方法进行画图。
2.4 public DrawMovingOrbit(Circle center)
构造器,根据传入center进行画圆,其中每个轨道上都有个center半径1/8的orbiter ,根据初始的tick值,设置初始位置。
2.5 public static void visualizes(CircularOrbit c)
这个方法一方面是为了从多轨道系统中获取数据,另一方面,承载了主要的作图过程。
最主要的是线程定时器Timer和监听器的使用,为了在进行某一个操作的时候触发更新功能轨道物体位置的功能,重写了actionPerformed方法,在其中分别调用tick()更新轨道物体位置、repaint()重新作图、restart()重启计时器。
画出的动态图如下(不会截成动态的):
1.对于前6个功能,实现方式与AtomStrcture和StellarSystem完全相类似。只是在某个轨道上添加或删除用户的时候,需要添加或删除其对应的SocialTie,改变图的结构和映射关系,实现的手段就是调用LE_IntimacyMap和EE_IntimacyMap中的put和remove方法即可。
2.特殊功能
判断每个用户是哪个轨道上的:遍历所有的轨道,打印轨道用户
计算某个处于第一条轨道上好友的“信息扩散度“:先让用户从第一条轨道上选择好友,我们遍历该好友的所有朋友(不包括CentralUser),如果两人社交的亲密度大于设定值(暂设为0.5),则可将“信息扩散度”加1,最后打印出“信息扩散度”。
增加和删除一条社交关系:这是所有应用中最难的部分。我们先要判断SocialTie是轨道用户之间的,还是轨道用户和CentralUser之间的,分别调用EE_IntimacyMap和LE_IntimacyMap中的方法增加和删除图中的映射即可。难点在于如何调整图的结构。我的做法是,保留图中存在的所有映射,将轨道和轨道上的物体清空(对象还在),然后将SocialNetworkCircle中读取图是建立轨道的部分,封装到一个BuildRelation类中,这样在图的结构每次改变之后,保留映射关系和对象,重写建立轨道即可。
删除了TommyWong和DavidChen之间的SocialTie之后,DavidChen从第一轨道移到了第三轨道,如图所示:
以下各小节,只需保留和完成你所选定的应用即可。
首先我们需要自己写一个txt文件MyStellarSystem,将其中的半径那一项,改为半长轴a,半短轴b,和焦点c,根据这个文件我们建立行星运动系统的椭圆轨道。
修改Planet和Track的Fileds,将其中的轨道半径分别改为半长轴a,半短轴b,和焦点c,同时修改Constructor和getter方法。
其次就是修改读文件部分了,因为我们之前写的正则表达式就是按行读取,然后将等号后面的部分按照逗号分开,获取数据,因此这次处理改动不大,只需要将之前字符串分割的8项改为分割成10项即可。
然后我们需要根据提示的一些错误简单修改其他类,正确地传入参数。代码修改量和花费的时间不多。
最后一部分时考虑如何可视化模拟一个椭圆运动的行星轨道,这乍一看很难,其实程序要修改的地方并不多。首先是从轨道系统中获得各个行星的参数(由半径radius变化为长半轴、短半轴、焦点),为了方便处理,我们将读入的shortAxis设置为longAxis的一半。
此时在绘制椭圆轨道时,只需将drawOval的参数x设置为centerx减去longAxis/2,参数width设置为longAxis;将参数y设置为center.y减去shortAxis/2,参数height设置为shortAxis。
更新轨道物体坐标的方法中,设置新的位置:
Point2D.Double d = new Point2D.Double();
d.setLocation(xCenter + longAxis * Math.cos(tick * DEG_TO_RAD), yCenter + shortAxis * Math.sin(tick * DEG_TO_RAD));
行星运动如下(依然没能截取动态图)
1.社交关系方向性:由于我们为社交关系的方向性和亲密度写的EE_IntimacyMap和LE_IntimacyMap,在这里社交关系的方向性自动满足。
2.忽略轨道物体到中心物体的关系:这个实现起来也比较容易,当读文件SocialTie部分,CentralUser如果是用户2,则不执行任何操作,即不添加映射关系即可。
3.忽略外层轨道到内层轨道的映射关系。事实上,对于社交关系网络,在社交映射全部添加,社交图构建完毕之前,我们并不知道每个物体所处的轨道。因此,我们的做法是,先根据对象和映射关系,调用BuildRelation类中的方法第一次构建轨道。然后,对于已知的轨道,从内到外两层循环,如果存在外层轨道物体对内层轨道物体的映射,将其删除。最后我们先将轨道和轨道物体全部删除,再调用一次buildRelation方法,根据对象和新的映射关系重新构建轨道系统即可。