HIT 软件构造 lab3

由于上次lab2几乎把整个报告都放上去了,导致最后很多盆友的写报告的时候直接照样复制了,有点不太好。因此这次我只写了lab3自己的部分实现。其实我ADT的设计挺一般的,而且ddl都到了,我想这个时候大家的实验应该都写完了吧。

由于最近太忙,文章内容我也没怎么认真排版了,大家见谅。

    1. 待开发的三个应用场景

首先请列出你要完成的具体应用场景(至少3个,12中选一,3必选,45中选一,鼓励完成更多的应用场景)。

  1. StellarSystem
  2. AtomStructure
  3. SocialNetworkCircle

 

分析你所选定的多个应用场景的异同,理解需求:它们在哪些方面有共性、哪些方面有差异。

共性:

1.都具备轨道系统中基本存在的对象,包括轨道、中心点物体、轨道物体.

2.都需要具备很多相同的操作:添加/删除轨道,在某一条轨道山添加/删除物体,从文件中读取轨道(读取方式不同)。

3.获得轨道系统的熵值,获得逻辑距离,比较两个同类型轨道的difference,检查轨道系统的合法化,GUI可视化。

 

差异:

1.StellarSystem的轨道物体考虑绝对位置,而其他两个系统无需考虑。

2.StellarSystem的轨道需要按照轨道半径进行排序,SocialNetworkCircle则是根据关系的级数进行轨道排序。

3.SocialNetworkCircle需要考虑复杂的映射关系,包括中心点物体映射轨道物体,和轨道物体之间的映射。并且在GUI可视化时,需要根据这些逻辑关系进行连线。

4.每个轨道系统都有着其特殊的功能,包括电子跃迁,计算行星物理距离和增加社交关系等。

    1. 基于语法的图数据输入

分别是在三个应用的读文件中设计正则表达式。然后对正则表达式进行解析匹配。

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. 可复用API设计

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. 图的可视化:第三方API的复用

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<Circle> centers = new ArrayList<Circle>();

    private ArrayList<Circle> orbiters = new ArrayList<Circle>();

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. SocialNetworkCircle

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从第一轨道移到了第三轨道,如图所示:

 

    1. 应对应用面临的新变化

以下各小节,只需保留和完成你所选定的应用即可。

      1. StellarSystem

首先我们需要自己写一个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(xCenterlongAxis * Math.cos(tick * DEG_TO_RAD), yCenter + shortAxis * Math.sin(tick * DEG_TO_RAD));

 

行星运动如下(依然没能截取动态图)

      1. SocialNetworkCircle

1.社交关系方向性:由于我们为社交关系的方向性和亲密度写的EE_IntimacyMap和LE_IntimacyMap,在这里社交关系的方向性自动满足。

2.忽略轨道物体到中心物体的关系:这个实现起来也比较容易,当读文件SocialTie部分,CentralUser如果是用户2,则不执行任何操作,即不添加映射关系即可。

3.忽略外层轨道到内层轨道的映射关系。事实上,对于社交关系网络,在社交映射全部添加,社交图构建完毕之前,我们并不知道每个物体所处的轨道。因此,我们的做法是,先根据对象和映射关系,调用BuildRelation类中的方法第一次构建轨道。然后,对于已知的轨道,从内到外两层循环,如果存在外层轨道物体对内层轨道物体的映射,将其删除。最后我们先将轨道和轨道物体全部删除,再调用一次buildRelation方法,根据对象和新的映射关系重新构建轨道系统即可。

 

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值