Changeset 454

Show
Ignore:
Timestamp:
Tue Jun 17 21:47:54 2008
Author:
Alexander
Message:

elegant algorithm for following a role connector

Files:

Legend:

Unmodified
Added
Removed
Modified
  • ganttpv/trunk/ORM.py

    r451 r454  
    62 62 # 080606 - Brian - display constraint symbols, connect correctly to adjacent pairs of roles  
    63 63 # 080606 - Alex - deontic constraints; three-way role constraints; shift-click on constraint to add role sequence; new icons  
    64   # 080617 - Alex - fixed bug where role names moved twice as much; added pencil cursor  
      64 # 080617 - Alex - elegant algorithm for following a role connector; added pencil cursor  
    64 64  
      65 from __future__ import division  
    65 66 import wx  
    66 67 # import wx.lib.flatnotebook as fnb  
     
    122 123  
    123 124  
    124    
    125 125 # implement "any" function for lesser versions of python  
    126 126 try:  
     
    134 134  
    135 135  
      136 # geometric functions for positioning role names  
      137  
      138 def NearestPointOnLine(pointA, pointB, pointC):  
      139     """ Find the nearest point on the line from A to B """  
      140     x0, y0 = pointA  
      141     x1, y1 = pointB  
      142     x2, y2 = pointC  
      143     if x0 == x1:  
      144         return x0, y2  
      145     dx, dy = x1 - x0, y1 - y0  
      146     x = ( ((dx * dx) * x2 + (dx * dy) * (y2 - y0) + (dy * dy) * x0) /  
      147           (dx * dx + dy * dy) )  
      148     y = (dy / dx) * (x - x0) + y0  
      149     return x, y  
      150  
      151 def dist(pointA, pointB):  
      152     x0, y0 = pointA  
      153     x1, y1 = pointB  
      154     dx, dy = x1 - x0, y1 - y0  
      155     return math.sqrt(dx * dx + dy * dy)  
      156  
      157 def SetLineFollowerMetrics(line, follower):  
      158     """ Impossible to explain without drawing a diagram. """  
      159     pointA, pointB = line.GetEnds()  
      160     pointC = follower.GetPos()  
      161     junction = NearestPointOnLine(pointA, pointB, pointC)  
      162     parallel = dist(pointA, junction) / dist(pointA, pointB)  
      163     orthagonal = dist(junction, pointC)  
      164     if ((pointB[1] > 0 and pointC[0] < junction[0]) or  
      165         (pointB[1] == 0 and pointC[1] < junction[1]) or  
      166         (pointB[1] < 0 and pointC[0] > junction[0])):  
      167         orthagonal = -orthagonal  
      168         # if follower is clockwise from the line, make orthagonal negative  
      169     follower._SetInShell('parallel', parallel)  
      170     follower._SetInShell('orthagonal', orthagonal)  
      171  
      172  
    136 173  
    137 174 #---------------------------------------------------------------------------  
     
    2032 2069         return self.GetPos()  
    2033 2070  
      2071     def GetEnds(self):  
      2072         x, y = self.GetPos()  
      2073         nodea = self.Get('NodeA')  
      2074         nodeb = self.Get('NodeB')  
      2075         endb = nodeb.GetPos()  
      2076         enda = nodea.AdjustEnd(endb)  
      2077         endb = nodeb.AdjustEnd(enda)  
      2078         return enda, endb  
      2079  
      2080     def AddFollower(self, follower_shape):  
      2081         if not follower_shape in self.followers:  
      2082             self.followers.append(follower_shape)  
      2083             SetLineFollowerMetrics(self, follower_shape)  
      2084  
    2034 2085     def Follow(self, remove=None):  
    2035 2086         nodea = self.Get('NodeA')  # these objects might not already be in the diagram  
     
    2953 3004  
    2954 3005 class ORMRoleConnectorShape(ORMConnector):  
      3006     def GetEnds(self):  
      3007         orm_object = self.Get('Target')  
      3008         x, y = self.GetPos()  
      3009         nodea = self.Get('NodeA')  
      3010         nodeb = self.Get('NodeB')  
      3011  
      3012         # connect base on reading sequence, not fact sequence  
      3013         seq = [ x.Seq for x in nodea.Target.ORMFactReading.ORMRoleSequence.GetList('ORMRolePosition')  
      3014                 if x.ORMRole.ID == orm_object.ID ][0]  
      3015         endb = nodeb.GetPos()  
      3016         enda = nodea.AdjustEnd(endb, seq)  # nodeA should be a fact shape  
      3017         endb = nodeb.AdjustEnd(enda)  
      3018         return enda, endb  
      3019  
    2955 3020     def Draw(self, dc):  
    2956 3021         orm_object = self.Get('Target')  
     
    3122 3187  
    3123 3188 class ORMConstraintConnectorShape(ORMConnector):  
      3189     def GetEnds(self):  
      3190         x, y = self.GetPos()  
      3191         nodea = self.Get('NodeA')  
      3192         nodeb = self.Get('NodeB')  
      3193  
      3194         constraintlist = self.Get('Target')  
      3195         cr_list = constraintlist.GetList('ORMRolePosition')  
      3196         fact_roles = [cr.ORMRole for cr in cr_list if cr.ORMRole.ORMFactTypeID == nodea.Target.ID]  
      3197  
      3198         seq_xref = dict([ (x.ORMRoleID, x.Seq) for x in nodea.Target.ORMFactReading.ORMRoleSequence.GetList('ORMRolePosition') ])  
      3199         seq_numbers = [seq_xref[role.ID] for role in fact_roles]  
      3200         if not seq_numbers: return  
      3201  
      3202         min_seq = min(seq_numbers)  
      3203         max_seq = max(seq_numbers)  
      3204         seq = (min_seq + max_seq) / 2.0  
      3205  
      3206         endb = nodeb.GetPos()  
      3207         enda = nodea.AdjustEnd(endb, seq)  # nodeA should be a fact shape  
      3208         endb = nodeb.AdjustEnd(enda)  
      3209         return enda, endb  
      3210  
    3124 3211     '''Connects to Fact Type at one or more Role boxes'''  
    3125 3212     def Draw(self, dc):  
     
    4825 4912         r = self.pdc.GetIdBounds(dcid)  
    4826 4913         if shape.NodeAID and shape.NodeBID:  # recalculate ends  
    4827               x, y = shape.GetPos()  
    4828 4914             shape.Draw(self.pdc)  
    4829               newx, newy = shape.GetPos()  
    4830               dx, dy = newx - x, newy - y  
    4831 4915         elif shape.NodeAID:  # follow  
    4832 4916             nodea = shape.Get('NodeA')  
    4833               if nodea.GetSelected():  # need a better solution  
    4834                   if shape.GetSelected() and follow: return  # we're both selected (extra move)  
    4835                   self.pdc.TranslateId(dcid, dx, dy) # move will be repeated twice?  
    4836                   x,y = shape.GetPos()  
    4837                   shape.SetTempPos(x+dx, y+dy)                 
    4838               else:    # we are not following  
    4839                   self.pdc.TranslateId(dcid, dx, dy)  
    4840                   x,y = shape.GetPos()  
    4841                   shape.SetTempPos(x+dx, y+dy)  
      4917             if nodea.GetSelected() and shape.GetSelected() and follow:  
      4918                 return  # we're both selected; prevent double move  
      4919             self.pdc.TranslateId(dcid, dx, dy)  
      4920             x,y = shape.GetPos()  
      4921             shape.SetTempPos(x+dx, y+dy)  
      4922             if nodea.NodeAID and nodea.NodeBID and not follow:  
      4923                 SetLineFollowerMetrics(nodea, shape)  
    4842 4924         else:  
    4843 4925             self.pdc.TranslateId(dcid, dx, dy)  
     
    4853 4935 #        shape.PosY += dy  
    4854 4936  
    4855           for follower_shape in shape.GetFollowers():  
    4856               self.MoveID(follower_shape.dcid, dx, dy, 1)  
      4937         if shape.NodeAID and shape.NodeBID:  
      4938             self.MoveLineFollowers(shape)  
      4939         else:  
      4940             for follower in shape.GetFollowers():  
      4941                 self.MoveID(follower.dcid, dx, dy, 1)  
      4942  
      4943     def MoveLineFollowers(self, line):  
      4944         pointA, pointB = line.GetEnds()  
      4945         length = dist(pointA, pointB) or 1  
      4946         x0, y0 = pointA  
      4947         x1, y1 = pointB  
      4948         width, height = x1 - x0, y1 - y0  
      4949         for f in line.GetFollowers():  
      4950             x2 = x0 + width * f.parallel - height * f.orthagonal / length  
      4951             y2 = y0 + height * f.parallel + width * f.orthagonal / length  
      4952  
      4953             # # draw the lines used to position the follower  
      4954             # junctionX = x0 + width * f.parallel  
      4955             # junctionY = y0 + height * f.parallel  
      4956             # self.pdc.SetPen(self.CachedPen(1, 1, wx.DOT))  
      4957             # self.pdc.DrawLine(x0, y0, junctionX, junctionY)  
      4958             # self.pdc.DrawLine(junctionX, junctionY, x2, y2)  
      4959  
      4960             # x, y = f.GetPos()  
      4961             # dx, dy = x2 - x, y2 - y  
      4962             # self.MoveID(f.dcid, dx, dy, 1)  # drifting for some reason  
      4963             f.SetTempPos(x2, y2)  
      4964             self.RedrawID(f.dcid)  
    4857 4965  
    4858 4966     def CommitPositionSelection(self):