From 38e28ec74acdaeff6541800c03144f4dc7b49790 Mon Sep 17 00:00:00 2001 From: Brady Jacobs Date: Tue, 21 Aug 2012 16:52:13 -0400 Subject: [PATCH 1/8] Dialogs List comprehensions and useful dialogs for unlocking. --- RankPanda/Commands.py | 44 +++++---- RankPanda/CoreWrapper.py | 86 ++++++++++-------- RankPanda/CubicHermiteSpline.py | 10 +-- RankPanda/GUIDialogs.py | 9 +- RankPanda/GUIMain.py | 153 +++++++++++++++++++------------- RankPanda/Move.py | 12 +-- RankPanda/Move_test.py | 9 ++ RankPanda/Song.py | 11 +-- 8 files changed, 200 insertions(+), 134 deletions(-) diff --git a/RankPanda/Commands.py b/RankPanda/Commands.py index eb914e3..f8f971d 100755 --- a/RankPanda/Commands.py +++ b/RankPanda/Commands.py @@ -12,56 +12,64 @@ def __init__(self, length, beginLocation): self._name = 'Command' - # Given the beginning location, calculates the location that the - # rank should be in after count counts. Uses information such as - # self._delta and the command type, but does NOT use - # self.endLocation. + # Be sure to overwrite this in each Command! def CalcLocation(self, count, beginLocation): + ''' Given the beginning location, calculates the location that the + rank should be in after count counts. Uses information such as + self._delta and the command type, but does NOT use + self.endLocation. ''' return beginLocation - # Splits the command in two, at the specified count. Changes this command - # to be the appropriate length, and returns this command as well as the - # new one. + # Be sure to overwrite this in each Command! def Split(self, count, beginLocation): + ''' Splits the command in two, at the specified count. Changes this command + to be the appropriate length, and returns this command as well as the + new one.''' pass - # Returns the current value of the self._name field. + def GetName(self): + ''' Returns the current value of the self._name field.''' return self._name - # Sets the value of the self._name field. + def SetName(self, name): + ''' Sets the value of the self._name field.''' self._name = name - # Returns the number of counts this command spans. If you want to change - # this value, make a new command instead. + def GetLength(self): + ''' Returns the number of counts this command spans. If you want to change + this value, make a new command instead.''' return round(self._length) - # Simple getter, in case the beginning location isn't readily available. + def GetEndLocation(self): + ''' Simple getter, in case the beginning location isn't readily available.''' return self._endLocation def SnapEndLocation(self, newBeginLocation): self._endLocation = self.CalcLocation(self.GetLength(), newBeginLocation) - # From the ending location, calculate the beginning location. Will require - # basically the same implementation as CalcLocation(), but in reverse. - # count should be the location in the whole command you want it to be - # returned. So, pass in 0 to run the whole command. + # Be sure to overwrite this in each Command! def CalcBeginLocation(self, count, endLocation): + ''' From the ending location, calculate the beginning location. Will require + basically the same implementation as CalcLocation(), but in reverse. + count should be the location in the whole command you want it to be + returned. So, pass in 0 to run the whole command.''' return None - # Checks following to see if it's a command of the same name. If so, - # return the merged commands. + def MergeWithFollowing(self, following): + ''' Checks following to see if it's a command of the same name. If so, + return the merged commands.''' if ((following._name == self._name) and (isinstance(following, self))): self._length = self._length + following._length self._endLocation = following._endLocation diff --git a/RankPanda/CoreWrapper.py b/RankPanda/CoreWrapper.py index 2a0ef18..f3ff675 100755 --- a/RankPanda/CoreWrapper.py +++ b/RankPanda/CoreWrapper.py @@ -20,9 +20,7 @@ class CoreWrapper(object): def __init__(self, title, numberMeasures, CountsPerMeasureList, StepsPerCountList): if (numberMeasures < 1): raise NameError("Number of measures can't be less than 1!") - if (len(CountsPerMeasureList) == 0): - raise NameError("You must input the initial number of counts per measure!") - if (CountsPerMeasureList[0][0] != 1): + if (len(CountsPerMeasureList) == 0 or CountsPerMeasureList[0][0] != 1): raise NameError("You must input the initial number of counts per measure!") self._song = Song.Song(title, numberMeasures, CountsPerMeasureList[0][1]) i = 1 @@ -54,16 +52,21 @@ def GetSong(self): # [(0, 'Move 0'), (1, 'Move 1'), ...] def GetMoves(self): listOfMoves = self._song.GetMoveList() - i = 0 - moveInfo = [] - while (i < len(listOfMoves)): - curmove = listOfMoves[i] - moveInfo.append((curmove.GetNumber(), curmove.GetName())) - i = i + 1 - return moveInfo + #(Brady) Trying list comprehension + return [(curmove.GetNumber(), curmove.GetName()) for curmove in listOfMoves] + #Original Code + #i = 0 + #moveInfo = [] + #while (i < len(listOfMoves)): + # curmove = listOfMoves[i] + # moveInfo.append((curmove.GetNumber(), curmove.GetName())) + # i = i + 1 + #return moveInfo + + # Returns (current move number, current move name, current length of move in counts) - # Returns None is there is no current move + # Returns None if there is no current move def GetCurrentMove(self): if (self._song.currentMove is None): return None @@ -76,29 +79,37 @@ def GetCurrentMove(self): # ? # List of all ranks in current move # [(id0, name0, location0), (id1, name1, location1)] - def GetRanks(self): + #(Brady) Merged next method with this + def GetRanks(self, moveNumber = None): if (self._song.currentMove is None): return None - allRanks = self._song.currentMove.GetAllRanks() - i = 0 - allRankInfo = [] - while (i < len(allRanks)): - allRankInfo.append((allRanks[i].GetID(), allRanks[i].GetName(), allRanks[i].GetEndLocation(), allRanks[i].GetLabelLocation())) - i = i + 1 - return allRankInfo - - + if (moveNumber is None): + allRanks = self._song.currentMove.GetAllRanks() + else: + allRanks = self._song.GetMoveList()[moveNumber].GetAllRanks() + #(Brady) List comprehensions + return [(r.GetID(), r.GetName(), r.GetEndLocation(), r.GetLabelLocation()) for r in allRanks] + #Orig. code + #i = 0 + #allRankInfo = [] + #while (i < len(allRanks)): + # allRankInfo.append((allRanks[i].GetID(), allRanks[i].GetName(), allRanks[i].GetEndLocation(), allRanks[i].GetLabelLocation())) + # i = i + 1 + #return allRankInfo + + #(Brady) merged with prev with optional args. Keeping this so I dont have to change all the method calls. # Same as above but now you input the move number. def GetRanksGivenMove(self, moveNumber): - if (self._song.currentMove is None): - return None - allRanks = self._song.GetMoveList()[moveNumber].GetAllRanks() - i = 0 - allRankInfo = [] - while (i < len(allRanks)): - allRankInfo.append((allRanks[i].GetID(), allRanks[i].GetName(), allRanks[i].GetEndLocation(), allRanks[i].GetLabelLocation())) - i = i + 1 - return allRankInfo + return self.GetRanks(moveNumber) + # if (self._song.currentMove is None): + # return None + # allRanks = self._song.GetMoveList()[moveNumber].GetAllRanks() + # i = 0 + # allRankInfo = [] + # while (i < len(allRanks)): + # allRankInfo.append((allRanks[i].GetID(), allRanks[i].GetName(), allRanks[i].GetEndLocation(), allRanks[i].GetLabelLocation())) + # i = i + 1 + # return allRankInfo def IsRankHeld(self, ID): @@ -113,12 +124,15 @@ def GetSelectedRanks(self): if (self._song.currentMove is None): return None allSelectedRanks = self._song.currentMove.GetSelectedRanks() - i = 0 - allSelectedRankInfo = [] - while (i < len(allSelectedRanks)): - allSelectedRankInfo.append((allSelectedRanks[i].GetID(), allSelectedRanks[i].GetName(), allSelectedRanks[i].GetEndLocation(), allSelectedRanks[i].GetLabelLocation())) - i = i + 1 - return allSelectedRankInfo + #(Brady) List comp. + return [(r.GetID(), r.GetName(), r.GetEndLocation(), r.GetLabelLocation()) for r in allSelectedRanks] + #Orig. code + #i = 0 + #allSelectedRankInfo = [] + #while (i < len(allSelectedRanks)): + # allSelectedRankInfo.append((allSelectedRanks[i].GetID(), allSelectedRanks[i].GetName(), allSelectedRanks[i].GetEndLocation(), allSelectedRanks[i].GetLabelLocation())) + # i = i + 1 + #return allSelectedRankInfo def GetAdditionalRanks(self): curList = [] diff --git a/RankPanda/CubicHermiteSpline.py b/RankPanda/CubicHermiteSpline.py index 8b6ed17..6c96b91 100755 --- a/RankPanda/CubicHermiteSpline.py +++ b/RankPanda/CubicHermiteSpline.py @@ -145,11 +145,11 @@ def GetPoints(cls, splineList): NUMBERPERSTEP = 8 lengths = SplineGenerator.GetLengths(splineList) i = 0 -# Ignore and feel free to remove the next lines, I think they're repetitiions of the GetLengths() function. -# while (i < len(splineList)): -# lengths.append(SplineGenerator.GetLength(splineList[i], 0.001)) -# i = i + 1 -# i = 0 + # Ignore and feel free to remove the next lines, I think they're repetitiions of the GetLengths() function. + # while (i < len(splineList)): + # lengths.append(SplineGenerator.GetLength(splineList[i], 0.001)) + # i = i + 1 + # i = 0 listOfPointLists = [] while (i < len(splineList)): total = NUMBERPERSTEP*lengths[i] diff --git a/RankPanda/GUIDialogs.py b/RankPanda/GUIDialogs.py index 2ccffbf..4913fe9 100755 --- a/RankPanda/GUIDialogs.py +++ b/RankPanda/GUIDialogs.py @@ -536,7 +536,7 @@ def __init__(self, parent, main, endpoint, rankName, length, FTAranks): # endpoi self.okButton.SetDefault() self.okButton.Bind(wx.EVT_BUTTON, self.OnOK) - self.straightButton = wx.BitmapButton(self, wx.ID_ANY, wx.Bitmap('icons/zigzagicon.png')) + self.straightButton = wx.BitmapButton(self, wx.ID_ANY, wx.Bitmap('icons/zigzagicon.png')) self.straightButton.SetDefault() self.straightButton.Bind(wx.EVT_BUTTON, self.OnStraight) @@ -833,14 +833,14 @@ def __init__(self, parent): # begin layout code self.directionPanel = wx.BoxSizer(wx.HORIZONTAL) self.directionChoices = ['Clockwise', 'Counter-clockwise'] - self.directionChoicesValues = [0, 1] + #self.directionChoicesValues = [0, 1] #These aren't really needed I dont think self.directionRadioBox = wx.RadioBox(self, wx.ID_ANY, "Direction", choices = self.directionChoices, majorDimension = 2, style = wx.RA_SPECIFY_COLS) self.directionRadioBox.SetSelection(0) # clockwise selected by default self.directionPanel.Add(self.directionRadioBox, 1) self.pivotPanel = wx.BoxSizer(wx.HORIZONTAL) self.pivotChoices = ['At arrow ', 'At point'] - self.pivotChoicesValues = [0, 1] + #self.pivotChoicesValues = [0, 1] self.pivotRadioBox = wx.RadioBox(self, wx.ID_ANY, "Non-Pivot Point", choices = self.pivotChoices, majorDimension = 2, style = wx.RA_SPECIFY_COLS) self.pivotRadioBox.SetSelection(0) # arrow selected by default self.pivotPanel.Add(self.pivotRadioBox, 1) @@ -896,7 +896,8 @@ def OnOK(self, event): d.ShowModal() d.Destroy() return - self.output = (self.directionChoicesValues[self.directionRadioBox.GetSelection()], self.pivotChoicesValues[self.pivotRadioBox.GetSelection()] , length) + #self.output = (self.directionChoicesValues[self.directionRadioBox.GetSelection()], self.pivotChoicesValues[self.pivotRadioBox.GetSelection()] , length) + self.output = (self.directionRadioBox.GetSelection(), self.pivotRadioBox.GetSelection(), length) self.EndModal(0) def OnCancel(self, event): diff --git a/RankPanda/GUIMain.py b/RankPanda/GUIMain.py index 05b0f7d..ac48b90 100755 --- a/RankPanda/GUIMain.py +++ b/RankPanda/GUIMain.py @@ -93,8 +93,8 @@ def __init__(self, parent): self.core = CoreWrapper.CoreWrapper("Dummy", 10, [(1, 4)], [(1, 1)]) while loop: - self.dirname = '' - e = wx.FileDialog(self, "Open File", self.dirname, "", "*.panda", wx.OPEN) + dirname = '' + e = wx.FileDialog(self, "Open File", dirname, "", "*.panda", wx.OPEN) if e.ShowModal() == wx.ID_OK: self.filename = os.path.join(e.GetDirectory(), e.GetFilename()) if self.core.Load(self.filename) == -1: # load failed @@ -123,6 +123,7 @@ def __init__(self, parent): #self.CreateStatusBar(2) #self.SetStatusText("Try these: Exit, Open, Help, About", 1) + # begin menubar code filemenu = wx.Menu() filemenu.Append(ID_OPEN, "&Open...", "Open something") @@ -236,10 +237,12 @@ def __init__(self, parent): self.fieldpanel = wx.BoxSizer(wx.VERTICAL) self.fieldpanel.Add(self.statusBar, 0, wx.ALIGN_CENTER) self.fieldpanel.Add(self.field, 1, wx.EXPAND) - + + + self.rankNameUnicode = wx.Button(self.panel, wx.ID_ANY, "Unicode") self.rankNameUnicode.Bind(wx.EVT_BUTTON, self.OnRankNameUnicode) - self.holdRankButton = wx.BitmapButton(self.panel, wx.ID_ANY, wx.Bitmap('icons/holdicon.png')) + self.holdRankButton = wx.BitmapButton(self.panel, wx.ID_ANY, wx.Bitmap('icons/holdicon.png')) self.holdRankButton.Bind(wx.EVT_BUTTON, self.OnHoldRank) self.curveRankButton = wx.BitmapButton(self.panel, wx.ID_ANY, wx.Bitmap('icons/curveicon.png')) self.curveRankButton.Bind(wx.EVT_BUTTON, self.OnCurveRank) @@ -276,7 +279,7 @@ def __init__(self, parent): self.fieldbar.Bind(wx.EVT_PAINT, self.fieldbar.OnPaint) self.fieldbar.Bind(wx.EVT_SIZE, self.fieldbar.OnResize) self.fieldbar.Bind(wx.EVT_LEFT_DOWN, self.fieldbar.OnLeftClick) - self.fieldbar.Bind(wx.EVT_RIGHT_DOWN, self.fieldbar.OnRightClick) + self.fieldbar.Bind(wx.EVT_RIGHT_DOWN, self.fieldbar.OnRightClick) #right clicks dont actually do anything, ATM. self.fieldbar.Bind(wx.EVT_LEFT_UP, self.fieldbar.OnLeftUnclick) self.fieldbar.Bind(wx.EVT_RIGHT_UP, self.fieldbar.OnRightUnclick) self.fieldbar.Bind(wx.EVT_MOTION, self.fieldbar.OnMouseMove) @@ -357,6 +360,7 @@ def __init__(self, parent): self.rankNamePanel = wx.BoxSizer(wx.HORIZONTAL) self.rankNamePanel.Add(self.rankNameListPanel, 1, wx.EXPAND) + #TODO Make this a static Module level or Class level variable. self.commandAddChoices = ['MT', 'Hlt', 'FM', 'BM', 'RS', 'LS', 'Flat'] self.commandAddButtons = [] @@ -419,7 +423,7 @@ def __init__(self, parent): self.animationCaption = wx.StaticText(self.panel, wx.ID_ANY, "Animation Controls") - self.playButton = wx.BitmapButton(self.panel, wx.ID_ANY, wx.Bitmap('icons/playicon.png')) + self.playButton = wx.BitmapButton(self.panel, wx.ID_ANY, wx.Bitmap('icons/playicon.png')) self.playButton.Bind(wx.EVT_BUTTON, self.OnAnimationBegin) self.stopButton = wx.BitmapButton(self.panel, wx.ID_ANY, wx.Bitmap('icons/stopicon.png')) self.stopButton.Bind(wx.EVT_BUTTON, self.OnAnimationEnd) @@ -485,6 +489,7 @@ def __init__(self, parent): self.animranks = None # list of animated rank locations when animating; None when not self.animtimer = wx.Timer(self, ID_ANIM_TIMER) wx.EVT_TIMER(self, ID_ANIM_TIMER, self.OnAnimationTimer) + def RefreshTitleBar(self): if self.filename is None: @@ -506,7 +511,7 @@ def RefreshMoveList(self): item.SetText(m[1]) item.SetImage(i) - self.moveSetList.Add(self.field.RenderSet(self.core.GetRanksGivenMove(i))) + self.moveSetList.Add(self.field.RenderSet(self.core.GetRanks(moveNumber = i))) #(Brady) changed from GetRanksGivenMove(i) to GetRanks(moveNumber=i) self.moveList.InsertItem(item) @@ -518,7 +523,7 @@ def RefreshMoveList(self): def RefreshCurrentMove(self): curr = self.core.GetCurrentMove()[0] - self.moveSetList.Replace(curr, self.field.RenderSet(self.core.GetRanksGivenMove(curr))) + self.moveSetList.Replace(curr, self.field.RenderSet(self.core.GetRanks(moveNumber = curr))) self.moveList.RefreshItem(curr) def RefreshRankList(self): @@ -531,10 +536,10 @@ def RefreshRankList(self): i = 0 for r in ranks: - if r[1] is not None: + if r[1] is not None: #checks the name isSelected = False - for s in selected: + for s in selected: #(Brady) This seems inefficient, try to fix if r[0] == s[0]: isSelected = True @@ -620,7 +625,7 @@ def CreateSong(self, event): self.RefreshCommandList() self.RefreshStatusBar() - def EditSong(self, event): + def EditSong(self, event): #try not having event. d = GUIDialogs.SongCreationDialog(self) if d.ShowModal() == 0: try: @@ -811,11 +816,20 @@ def OnCommandAddSpecialButtons(self, event, i): #d.ShowModal() #d.Destroy() #return - n=0 - while(n startCount): + if (self._moveList[i].GetStartCount() > startCount): #find move that starts after startCount stop = True else: i = i + 1 - if (i > 0): - if ((self._moveList[i - 1].GetStartCount() + self._moveList[i - 1].GetLength()) > startCount): + if (i > 0): #if move before i, would overlap with the startCount, return none + if ((self._moveList[i - 1].GetStartCount() + self._moveList[i - 1].GetLength()) > startCount): return None if (i < len(self._moveList)): - if (self._moveList[i].GetStartCount() < endCount): + if (self._moveList[i].GetStartCount() < endCount): #if move i starts before endCount return None prior = None following = None @@ -369,7 +369,8 @@ def AnimationBegin(self, count): def AnimationStep(self): if (not self.animating): - return (None, 0) + print("not self.animating") #(Brady) debug + return (None, 0, 0) #(Brady) added the last 0 to fix error unpacking tuple (try other values to see if it has any effect) if (self.songLoaded): time = self.timeOffset + pygame.mixer.music.get_pos() count = self.ConvertTimeToCount(time) From fe834d71f8f815f77a0c67e1eee1893da28e58a6 Mon Sep 17 00:00:00 2001 From: Brady Jacobs Date: Fri, 24 Aug 2012 01:16:29 -0400 Subject: [PATCH 2/8] better python code and some other fixes For ease of use, when adding waypoints, if a time is left blank it is set to zero. --- RankPanda/GUIDialogs.py | 157 ++++++++++++++++++++++------------------ RankPanda/Move.py | 19 +---- RankPanda/Move_test.py | 5 +- RankPanda/Rank.py | 47 ++++-------- 4 files changed, 104 insertions(+), 124 deletions(-) diff --git a/RankPanda/GUIDialogs.py b/RankPanda/GUIDialogs.py index 4913fe9..157daa7 100755 --- a/RankPanda/GUIDialogs.py +++ b/RankPanda/GUIDialogs.py @@ -137,7 +137,7 @@ def OnAdd(self, event): count = self.inputList.GetItemCount() i = 0 while i < count: - val = int(self.inputList.GetItemText(i)) + val = int(self.inputList.GetItemText(i)) #this gets the first column if val == fromMeasure: # found our measure self.countsPerMeasureList[i] = (fromMeasure, countsPerMeasure) self.countsPerStepList[i] = (fromMeasure, countsPerStep) @@ -743,36 +743,49 @@ def OnOK(self, event): d.ShowModal() d.Destroy() return + + #If no entry is given for the time, assume it is zero + min = self.minText.GetValue() + if min == "": + min = 0 + else: + try: + min = int(min) + if min < 0: + raise Exception # so we trigger the dialog below + except Exception: + d = wx.MessageDialog(self, "Invalid entry for Minutes!", "Parse Error", wx.OK) + d.ShowModal() + d.Destroy() + return - try: - min = int(self.minText.GetValue()) - if min < 0: - raise Exception # so we trigger the dialog below - except Exception: - d = wx.MessageDialog(self, "Invalid entry for Minutes!", "Parse Error", wx.OK) - d.ShowModal() - d.Destroy() - return - - try: - sec = int(self.secText.GetValue()) - if sec < 0: - raise Exception # so we trigger the dialog below - except Exception: - d = wx.MessageDialog(self, "Invalid entry for Seconds!", "Parse Error", wx.OK) - d.ShowModal() - d.Destroy() - return + sec = self.secText.GetValue() + if sec == "": + sec = 0 + else: + try: + sec = int(sec) + if sec < 0: + raise Exception # so we trigger the dialog below + except Exception: + d = wx.MessageDialog(self, "Invalid entry for Seconds!", "Parse Error", wx.OK) + d.ShowModal() + d.Destroy() + return - try: - msec = int(self.msecText.GetValue()) - if msec < 0: - raise Exception # so we trigger the dialog below - except Exception: - d = wx.MessageDialog(self, "Invalid entry for Milliseconds!", "Parse Error", wx.OK) - d.ShowModal() - d.Destroy() - return + msec = self.msecText.GetValue() + if msec == "": + msec = 0 + else: + try: + msec = int(msec) + if msec < 0: + raise Exception # so we trigger the dialog below + except Exception: + d = wx.MessageDialog(self, "Invalid entry for Milliseconds!", "Parse Error", wx.OK) + d.ShowModal() + d.Destroy() + return time = 60000 * min + 1000 * sec + msec @@ -791,7 +804,7 @@ def __init__(self, parent, text, title): # begin layout code self.textCtrl = wx.TextCtrl(self, wx.ID_OK, "", style = wx.TE_MULTILINE) self.textCtrl.SetValue(text) - + self.okButton = wx.Button(self, wx.ID_OK, "OK") self.okButton.SetDefault() self.okButton.Bind(wx.EVT_BUTTON, self.OnOK) @@ -819,11 +832,11 @@ def OnOK(self, event): self.EndModal(0) def OnCancel(self, event): - self.EndModal(-1) + self.EndModal(-1) - - - + + + class GateTurnDialog(wx.Dialog): """ Dialog for specifying Gate Turn details. """ @@ -837,21 +850,21 @@ def __init__(self, parent): self.directionRadioBox = wx.RadioBox(self, wx.ID_ANY, "Direction", choices = self.directionChoices, majorDimension = 2, style = wx.RA_SPECIFY_COLS) self.directionRadioBox.SetSelection(0) # clockwise selected by default self.directionPanel.Add(self.directionRadioBox, 1) - + self.pivotPanel = wx.BoxSizer(wx.HORIZONTAL) self.pivotChoices = ['At arrow ', 'At point'] #self.pivotChoicesValues = [0, 1] self.pivotRadioBox = wx.RadioBox(self, wx.ID_ANY, "Non-Pivot Point", choices = self.pivotChoices, majorDimension = 2, style = wx.RA_SPECIFY_COLS) self.pivotRadioBox.SetSelection(0) # arrow selected by default - self.pivotPanel.Add(self.pivotRadioBox, 1) - + self.pivotPanel.Add(self.pivotRadioBox, 1) + self.lengthPanel = wx.BoxSizer(wx.HORIZONTAL) self.lengthLabel = wx.StaticText(self, wx.ID_ANY, "Length: ") self.lengthText = wx.TextCtrl(self, wx.ID_ANY, "", size = (100, 25)) self.lengthPanel.Add(self.lengthLabel, 0, wx.ALIGN_RIGHT | wx.ALIGN_CENTRE_VERTICAL) self.lengthPanel.Add(self.lengthText) - + self.okButton = wx.Button(self, wx.ID_OK, "Add Command") self.okButton.SetDefault() self.okButton.Bind(wx.EVT_BUTTON, self.OnOK) @@ -876,7 +889,7 @@ def __init__(self, parent): self.panelSizer.Add(self.lengthPanel, 0, wx.ALIGN_LEFT) self.panelSizer.Add((1,25), 0, wx.ALIGN_LEFT) self.panelSizer.Add(self.buttonSizer, 0, wx.ALIGN_LEFT) - + self.wholePanel.Add((100,25), 0, wx.ALIGN_LEFT) self.wholePanel.Add(self.panelSizer) @@ -901,9 +914,9 @@ def OnOK(self, event): self.EndModal(0) def OnCancel(self, event): - self.EndModal(-1) - - + self.EndModal(-1) + + @@ -921,14 +934,14 @@ def __init__(self, parent): self.directionRadioBox = wx.RadioBox(self, wx.ID_ANY, "Direction", choices = self.directionChoices, majorDimension = 2, style = wx.RA_SPECIFY_COLS) self.directionRadioBox.SetSelection(0) # clockwise selected by default self.directionPanel.Add(self.directionRadioBox, 1) - + self.lengthPanel = wx.BoxSizer(wx.HORIZONTAL) self.lengthLabel = wx.StaticText(self, wx.ID_ANY, "Length: ") self.lengthText = wx.TextCtrl(self, wx.ID_ANY, "", size = (100, 25)) self.lengthPanel.Add(self.lengthLabel, 0, wx.ALIGN_RIGHT | wx.ALIGN_CENTRE_VERTICAL) self.lengthPanel.Add(self.lengthText) - + self.okButton = wx.Button(self, wx.ID_OK, "Add Command") self.okButton.SetDefault() self.okButton.Bind(wx.EVT_BUTTON, self.OnOK) @@ -970,7 +983,7 @@ def OnOK(self, event): self.EndModal(0) def OnCancel(self, event): - self.EndModal(-1) + self.EndModal(-1) @@ -988,15 +1001,15 @@ def __init__(self, parent): self.pivotChoicesValues = [0, 1] self.pivotRadioBox = wx.RadioBox(self, wx.ID_ANY, "Leading Point", choices = self.pivotChoices, majorDimension = 2, style = wx.RA_SPECIFY_COLS) self.pivotRadioBox.SetSelection(0) # arrow selected by default - self.pivotPanel.Add(self.pivotRadioBox, 1) - + self.pivotPanel.Add(self.pivotRadioBox, 1) + self.lengthPanel = wx.BoxSizer(wx.HORIZONTAL) self.lengthLabel = wx.StaticText(self, wx.ID_ANY, "Length: ") self.lengthText = wx.TextCtrl(self, wx.ID_ANY, "", size = (100, 25)) self.lengthPanel.Add(self.lengthLabel, 0, wx.ALIGN_RIGHT | wx.ALIGN_CENTRE_VERTICAL) self.lengthPanel.Add(self.lengthText) - + self.okButton = wx.Button(self, wx.ID_OK, "Add Command") self.okButton.SetDefault() self.okButton.Bind(wx.EVT_BUTTON, self.OnOK) @@ -1038,11 +1051,11 @@ def OnOK(self, event): self.EndModal(0) def OnCancel(self, event): - self.EndModal(-1) + self.EndModal(-1) - + class ExpandDialog(wx.Dialog): @@ -1057,15 +1070,15 @@ def __init__(self, parent): self.pivotChoicesValues = [0, 1] self.pivotRadioBox = wx.RadioBox(self, wx.ID_ANY, "Moving Point", choices = self.pivotChoices, majorDimension = 2, style = wx.RA_SPECIFY_COLS) self.pivotRadioBox.SetSelection(0) # arrow selected by default - self.pivotPanel.Add(self.pivotRadioBox, 1) - + self.pivotPanel.Add(self.pivotRadioBox, 1) + self.lengthPanel = wx.BoxSizer(wx.HORIZONTAL) self.lengthLabel = wx.StaticText(self, wx.ID_ANY, "Length: ") self.lengthText = wx.TextCtrl(self, wx.ID_ANY, "", size = (100, 25)) self.lengthPanel.Add(self.lengthLabel, 0, wx.ALIGN_RIGHT | wx.ALIGN_CENTRE_VERTICAL) self.lengthPanel.Add(self.lengthText) - + self.okButton = wx.Button(self, wx.ID_OK, "Add Command") self.okButton.SetDefault() self.okButton.Bind(wx.EVT_BUTTON, self.OnOK) @@ -1107,10 +1120,10 @@ def OnOK(self, event): self.EndModal(0) def OnCancel(self, event): - self.EndModal(-1) + self.EndModal(-1) - - + + class CondenseDialog(wx.Dialog): @@ -1125,15 +1138,15 @@ def __init__(self, parent): self.pivotChoicesValues = [0, 1] self.pivotRadioBox = wx.RadioBox(self, wx.ID_ANY, "Moving Point", choices = self.pivotChoices, majorDimension = 2, style = wx.RA_SPECIFY_COLS) self.pivotRadioBox.SetSelection(0) # arrow selected by default - self.pivotPanel.Add(self.pivotRadioBox, 1) - + self.pivotPanel.Add(self.pivotRadioBox, 1) + self.lengthPanel = wx.BoxSizer(wx.HORIZONTAL) self.lengthLabel = wx.StaticText(self, wx.ID_ANY, "Length: ") self.lengthText = wx.TextCtrl(self, wx.ID_ANY, "", size = (100, 25)) self.lengthPanel.Add(self.lengthLabel, 0, wx.ALIGN_RIGHT | wx.ALIGN_CENTRE_VERTICAL) self.lengthPanel.Add(self.lengthText) - + self.okButton = wx.Button(self, wx.ID_OK, "Add Command") self.okButton.SetDefault() self.okButton.Bind(wx.EVT_BUTTON, self.OnOK) @@ -1175,7 +1188,7 @@ def OnOK(self, event): self.EndModal(0) def OnCancel(self, event): - self.EndModal(-1) + self.EndModal(-1) @@ -1188,14 +1201,14 @@ def __init__(self, parent): wx.Dialog.__init__(self, parent, wx.ID_ANY, "Add DTP Command", size = (400, 150)) # begin layout code - + self.lengthPanel = wx.BoxSizer(wx.HORIZONTAL) self.lengthLabel = wx.StaticText(self, wx.ID_ANY, "Length: ") self.lengthText = wx.TextCtrl(self, wx.ID_ANY, "", size = (100, 25)) self.lengthPanel.Add(self.lengthLabel, 0, wx.ALIGN_RIGHT | wx.ALIGN_CENTRE_VERTICAL) self.lengthPanel.Add(self.lengthText) - + self.okButton = wx.Button(self, wx.ID_OK, "Continue") self.okButton.SetDefault() self.okButton.Bind(wx.EVT_BUTTON, self.OnOK) @@ -1235,10 +1248,10 @@ def OnOK(self, event): self.EndModal(0) def OnCancel(self, event): - self.EndModal(-1) + self.EndModal(-1) - + class CurveDialog(wx.Dialog): @@ -1248,14 +1261,14 @@ def __init__(self, parent): wx.Dialog.__init__(self, parent, wx.ID_ANY, "Add Curve Command", size = (400, 150)) # begin layout code - + self.lengthPanel = wx.BoxSizer(wx.HORIZONTAL) self.lengthLabel = wx.StaticText(self, wx.ID_ANY, "Length: ") self.lengthText = wx.TextCtrl(self, wx.ID_ANY, "", size = (100, 25)) self.lengthPanel.Add(self.lengthLabel, 0, wx.ALIGN_RIGHT | wx.ALIGN_CENTRE_VERTICAL) self.lengthPanel.Add(self.lengthText) - + self.okButton = wx.Button(self, wx.ID_OK, "Continue") self.okButton.SetDefault() self.okButton.Bind(wx.EVT_BUTTON, self.OnOK) @@ -1295,8 +1308,8 @@ def OnOK(self, event): self.EndModal(0) def OnCancel(self, event): - self.EndModal(-1) - + self.EndModal(-1) + class ExportPDFDialog(wx.Dialog): """ Dialog for Exporting to PDF """ @@ -1426,13 +1439,13 @@ def __init__(self, parent): self.fontPanel.Add(self.fontLabel, 0, wx.ALIGN_RIGHT | wx.ALIGN_CENTRE_VERTICAL) self.fontPanel.Add(self.fontText) - + self.columnPanel = wx.BoxSizer(wx.HORIZONTAL) self.columnLabel = wx.StaticText(self, wx.ID_ANY, "# of Columns per Page: ") self.columnPanel.Add(self.columnLabel, 0, wx.ALIGN_RIGHT | wx.ALIGN_CENTRE_VERTICAL) - self.columnInput = wx.SpinCtrl(self, wx.ID_ANY, size=(100,25), min = 1, max = 10, initial = 3) + self.columnInput = wx.SpinCtrl(self, wx.ID_ANY, size=(100,25), min = 1, max = 10, initial = 3) self.columnPanel.Add(self.columnInput) - + self.okButton = wx.Button(self, wx.ID_OK, "Export to PDF") self.okButton.SetDefault() self.okButton.Bind(wx.EVT_BUTTON, self.OnOK) @@ -1486,7 +1499,7 @@ def OnOK(self, event): self.EndModal(0) def OnCancel(self, event): - self.EndModal(-1) + self.EndModal(-1) class ProgramOptionsDialog(wx.Dialog): diff --git a/RankPanda/Move.py b/RankPanda/Move.py index 7c1b1c8..6cc7e0d 100755 --- a/RankPanda/Move.py +++ b/RankPanda/Move.py @@ -77,24 +77,11 @@ def SetPrior(self, newPrior): self._prior = newPrior self.UpdateAllRanksCommandList() - # TODO(astory): make sane def GetAllRanks(self): - allRanks = [] - i = 0 - items = self._idRankIndex.items() - while (i < len(items)): - allRanks.append(items[i][1]) - i = i + 1 - return allRanks - + return self._idRankIndex.values() + def GetAllNamedRanks(self): - allNamedRanks = [] - i = 0 - items = self._nameRankIndex.items() - while (i < len(items)): - allNamedRanks.append(items[i][1]) - i = i + 1 - return allNamedRanks + return self._nameRankIndex.values() # Shortcut def UpdateAllRanksCommandList(self): diff --git a/RankPanda/Move_test.py b/RankPanda/Move_test.py index f67354c..148b5c6 100644 --- a/RankPanda/Move_test.py +++ b/RankPanda/Move_test.py @@ -11,8 +11,8 @@ def testSetNumber(self): m.SetNumber(7) self.assertEquals(m._number, 7) self.assertEquals(m._name, 'Move 7') - - #(Brady) Test how it deals with nonstandard names (i.e. not 'Move' + num) + + #(Brady) Test how it deals with nonstandard names (i.e. not 'Move' + num) def testSetNumberName(self): m = Move(0, 4, None, None, None) m.SetName('Lala') @@ -20,6 +20,7 @@ def testSetNumberName(self): m.SetNumber(3) self.assertEquals(m._number, 3) self.assertEquals(m._name, 'Lala') + if __name__ == '__main__': unittest.main() diff --git a/RankPanda/Rank.py b/RankPanda/Rank.py index 5815d3d..9824adb 100644 --- a/RankPanda/Rank.py +++ b/RankPanda/Rank.py @@ -4,6 +4,15 @@ import Point import pprint +#(Brady) matches command names to function references to call in AddBasicCommand() +commandHash = {"MarkTime": Commands.MarkTime, "MT": Commands.MarkTime, "ForwardMarch":Commands.ForwardMarch, "FM":Commands.ForwardMarch, + "BackMarch":Commands.BackMarch, "BM":Commands.BackMarch, "RightSlide":Commands.RightSlide, "RS":Commands.RightSlide, + "LeftSlide":Commands.LeftSlide, "LS":Commands.LeftSlide, "GTCCW0":Commands.GTCCW0, "GTCW0":Commands.GTCW0, + "GTCCW1":Commands.GTCCW1, "GTCW1":Commands.GTCW1, "PWCCW":Commands.PWCCW, "PWCW":Commands.PWCW, + "Expand0":Commands.Expand0, "Exp0":Commands.Expand0, "Expand1":Commands.Expand1, "Exp1":Commands.Expand1, + "Condense0":Commands.Condense0, "Cond0":Commands.Condense0, "Condense1":Commands.Condense1, "Cond1":Commands.Condense1, + "Flatten":Commands.Flatten, "Flat":Commands.Flatten} + class Rank(object): def __init__(self, endLocation, move): self._endLocation = endLocation @@ -224,7 +233,7 @@ def GetEndLocation(self): # TODO(astory): this might be superfluous since GetCalculatedLocation never # returns none AFAICT def GetLocationAtCount(self, count): - """Returns the rank's location at a the given count""" + """Returns the rank's location at the given count""" loc = self.GetCalculatedLocation(count) if (loc is None): loc = self.GetEndLocation() @@ -255,39 +264,9 @@ def AddBasicCommand(self, commandName, number, length, name): beginLocation = self.GetPrior().GetEndLocation() else: beginLocation = self._commandList[number - 1].GetEndLocation() - # TODO(astory): this should be done with a hash defined once - if ((commandName == "MarkTime") or (commandName == "MT")): - newCommand = Commands.MarkTime(length, beginLocation) - elif ((commandName == "ForwardMarch") or (commandName == "FM")): - newCommand = Commands.ForwardMarch(length, beginLocation) - elif ((commandName == "BackMarch") or (commandName == "BM")): - newCommand = Commands.BackMarch(length, beginLocation) - elif ((commandName == "RightSlide") or (commandName == "RS")): - newCommand = Commands.RightSlide(length, beginLocation) - elif ((commandName == "LeftSlide") or (commandName == "LS")): - newCommand = Commands.LeftSlide(length, beginLocation) - elif (commandName == "GTCCW0"): - newCommand = Commands.GTCCW0(length, beginLocation) - elif (commandName == "GTCW0"): - newCommand = Commands.GTCW0(length, beginLocation) - elif (commandName == "GTCCW1"): - newCommand = Commands.GTCCW1(length, beginLocation) - elif (commandName == "GTCW1"): - newCommand = Commands.GTCW1(length, beginLocation) - elif (commandName == "PWCCW"): - newCommand = Commands.PWCCW(length, beginLocation) - elif (commandName == "PWCW"): - newCommand = Commands.PWCW(length, beginLocation) - elif ((commandName == "Expand0") or (commandName == "Exp0")): - newCommand = Commands.Expand0(length, beginLocation) - elif ((commandName == "Expand1") or (commandName == "Exp1")): - newCommand = Commands.Expand1(length, beginLocation) - elif ((commandName == "Condense0") or (commandName == "Cond0")): - newCommand = Commands.Condense0(length, beginLocation) - elif ((commandName == "Condense1") or (commandName == "Cond1")): - newCommand = Commands.Condense1(length, beginLocation) - elif ((commandName == "Flatten") or (commandName == "Flat")): - newCommand = Commands.Flatten(length, beginLocation) + + #(Brady) simplified this by adding a dictionary to hash names to the function + newCommand = commandHash[commandName](length, beginLocation) #May want to catch a KeyError in case of typo or something... self._commandList.insert(number, newCommand) if (name is not None): From 9a8f986a6b1c4637b0ca4fea6d0c5f3092587184 Mon Sep 17 00:00:00 2001 From: Brady Jacobs Date: Fri, 7 Sep 2012 00:35:28 -0400 Subject: [PATCH 3/8] Removed SplineGenerator Class also started trying to make things iterative. still working on it.... --- RankPanda/Commands.py | 26 +- RankPanda/CoreWrapper.py | 1 + RankPanda/CubicHermiteSpline.py | 419 +++++++++++++++++--------------- RankPanda/GUIMain.py | 4 + RankPanda/Rank.py | 11 +- RankPanda/RankLocation.py | 14 +- 6 files changed, 252 insertions(+), 223 deletions(-) diff --git a/RankPanda/Commands.py b/RankPanda/Commands.py index f8f971d..7882f56 100755 --- a/RankPanda/Commands.py +++ b/RankPanda/Commands.py @@ -16,40 +16,40 @@ def __init__(self, length, beginLocation): # Be sure to overwrite this in each Command! def CalcLocation(self, count, beginLocation): - ''' Given the beginning location, calculates the location that the + """ Given the beginning location, calculates the location that the rank should be in after count counts. Uses information such as self._delta and the command type, but does NOT use - self.endLocation. ''' + self.endLocation. """ return beginLocation # Be sure to overwrite this in each Command! def Split(self, count, beginLocation): - ''' Splits the command in two, at the specified count. Changes this command + """ Splits the command in two, at the specified count. Changes this command to be the appropriate length, and returns this command as well as the - new one.''' + new one.""" pass def GetName(self): - ''' Returns the current value of the self._name field.''' + """ Returns the current value of the self._name field.""" return self._name def SetName(self, name): - ''' Sets the value of the self._name field.''' + """ Sets the value of the self._name field.""" self._name = name def GetLength(self): - ''' Returns the number of counts this command spans. If you want to change - this value, make a new command instead.''' + """Returns the number of counts this command spans. If you want to change + this value, make a new command instead.""" return round(self._length) def GetEndLocation(self): - ''' Simple getter, in case the beginning location isn't readily available.''' + """Simple getter, in case the beginning location isn't readily available.""" return self._endLocation def SnapEndLocation(self, newBeginLocation): @@ -59,17 +59,17 @@ def SnapEndLocation(self, newBeginLocation): # Be sure to overwrite this in each Command! def CalcBeginLocation(self, count, endLocation): - ''' From the ending location, calculate the beginning location. Will require + """From the ending location, calculate the beginning location. Will require basically the same implementation as CalcLocation(), but in reverse. count should be the location in the whole command you want it to be - returned. So, pass in 0 to run the whole command.''' + returned. So, pass in 0 to run the whole command.""" return None def MergeWithFollowing(self, following): - ''' Checks following to see if it's a command of the same name. If so, - return the merged commands.''' + """ Checks following to see if it's a command of the same name. If so, + return the merged commands.""" if ((following._name == self._name) and (isinstance(following, self))): self._length = self._length + following._length self._endLocation = following._endLocation diff --git a/RankPanda/CoreWrapper.py b/RankPanda/CoreWrapper.py index f3ff675..fce563a 100755 --- a/RankPanda/CoreWrapper.py +++ b/RankPanda/CoreWrapper.py @@ -789,6 +789,7 @@ def RankDeleteSpline(self, rankID, number): return None self._song.currentMove.LookUpID(rankID).DeleteSplinePoint(number) + #(Brady) TODO: prevent changing name if not on the very first move. # Pass in the ID of the rank to name, and its new name. def NameRank(self, name): if (self._song.currentMove is None): diff --git a/RankPanda/CubicHermiteSpline.py b/RankPanda/CubicHermiteSpline.py index 6c96b91..3e718e1 100755 --- a/RankPanda/CubicHermiteSpline.py +++ b/RankPanda/CubicHermiteSpline.py @@ -19,212 +19,237 @@ # TODO(astory): convert to module level functions. None of these actually take # advantage of, or have any reason for, being in a class. -class SplineGenerator: - - # This calculated what the slopes at the points should be for the splines. - # The method we use simply draws a straight line between the point before - # and the point after, and this becomes the slope of the point. - # Note that the slopes are dx/dt and dy/dt, not dx/dy. - @classmethod - def _GetSlope(cls, PointTriple): - p0 = PointTriple[0] - p1 = PointTriple[1] - p2 = PointTriple[2] - pFirst = None - pSecond = None - n = 1 - if (p0 is None): - pFirst = p1 - pSecond = p2 - n = 2 - elif (p2 is None): - pFirst = p0 - pSecond = p1 - n = 2 - else: - pFirst = p0 - pSecond = p2 - slopex = (n/float(2))*(pSecond.x - pFirst.x) - slopey = (n/float(2))*(pSecond.y - pFirst.y) - return Point.Point(slopex, slopey) - - # The main splining method. Takes in a list of points and their slopes, - # and calculates the splines connecting them. Farily straightforward, once - # you know how it works, as described above. - # Note that is any slope is None, it'll automatically be calculated. - # This should often be the case, except when doing something like - # intermediate DTP locations or something. - # A given spline is stored as a list of four elements: - # [constantTerm, linearTerm, quadraticTerm, cubicTerm] - # The spline lists are then saved as [xsplines, ysplines] - # Each one is saved in order. - @classmethod - def GetSplines(cls, pointList, oldSlopeList): - slopeList = [] - l = len(pointList) - i = 0 - while (i < l): - if (oldSlopeList[i] is None): - if (i == 0): - slopeList.append(SplineGenerator._GetSlope([None, pointList[0], pointList[1]])) - elif (i == (l - 1)): - slopeList.append(SplineGenerator._GetSlope([pointList[i - 1], pointList[i], None])) - else: - slopeList.append(SplineGenerator._GetSlope([pointList[i - 1], pointList[i], pointList[i + 1]])) + +# This calculated what the slopes at the points should be for the splines. +# The method we use simply draws a straight line between the point before +# and the point after, and this becomes the slope of the point. +# Note that the slopes are dx/dt and dy/dt, not dx/dy. +def _GetSlope(PointTriple): + p0 = PointTriple[0] + p1 = PointTriple[1] + p2 = PointTriple[2] + pFirst = None + pSecond = None + n = 1 + if (p0 is None): + pFirst = p1 + pSecond = p2 + n = 2 + elif (p2 is None): + pFirst = p0 + pSecond = p1 + n = 2 + else: + pFirst = p0 + pSecond = p2 + slopex = (n/float(2))*(pSecond.x - pFirst.x) + slopey = (n/float(2))*(pSecond.y - pFirst.y) + return Point.Point(slopex, slopey) + +# The main splining method. Takes in a list of points and their slopes, +# and calculates the splines connecting them. Farily straightforward, once +# you know how it works, as described above. +# Note that is any slope is None, it'll automatically be calculated. +# This should often be the case, except when doing something like +# intermediate DTP locations or something. +# A given spline is stored as a list of four elements: +# [constantTerm, linearTerm, quadraticTerm, cubicTerm] +# The spline lists are then saved as [xsplines, ysplines] +# Each one is saved in order. +def GetSplines(pointList, oldSlopeList): + slopeList = [] + l = len(pointList) + i = 0 + while (i < l): + if (oldSlopeList[i] is None): + if (i == 0): + slopeList.append(_GetSlope([None, pointList[0], pointList[1]])) + elif (i == (l - 1)): + slopeList.append(_GetSlope([pointList[i - 1], pointList[i], None])) else: - slopeList.append(oldSlopeList[i]) - i = i + 1 - splineList = [] - i = 0 - while (i < (l - 1)): - xfn = [0, 0, 0, 0] - yfn = [0, 0, 0, 0] - xfn[0] = pointList[i].x - yfn[0] = pointList[i].y - xfn[1] = slopeList[i].x - yfn[1] = slopeList[i].y - xfn[2] = (-3*pointList[i].x) + (-2*slopeList[i].x) + (3*pointList[i+1].x) + (-1*slopeList[i+1].x) - yfn[2] = (-3*pointList[i].y) + (-2*slopeList[i].y) + (3*pointList[i+1].y) + (-1*slopeList[i+1].y) - xfn[3] = (2*pointList[i].x) + (slopeList[i].x) + (-2*pointList[i+1].x) + (slopeList[i+1].x) - yfn[3] = (2*pointList[i].y) + (slopeList[i].y) + (-2*pointList[i+1].y) + (slopeList[i+1].y) - splineList.append([xfn,yfn]) - i = i + 1 - return splineList - - - # Algorithm here: Find the length between the two end points. Then, - # find the length from point 0 to the midpoint of the spline, and then - # from the midpoint to point 1. If the sum of these two lengths is close - # to the length of the first one, return the length. If not, recurse - # and add the lengths together. - @classmethod - def GetLength(cls, fnList, tol): - return SplineGenerator._GetLengthHelper(fnList, 0, 1, tol) - - # TODO: Make iterative! It'll help. - @classmethod - def _GetLengthHelper(cls, fnList, ti, tf, tol): - xi = SplineGenerator.EvalCubic(ti, fnList[0]) - yi = SplineGenerator.EvalCubic(ti, fnList[1]) - xf = SplineGenerator.EvalCubic(tf, fnList[0]) - yf = SplineGenerator.EvalCubic(tf, fnList[1]) - tm = (ti + tf)/float(2) - xm = SplineGenerator.EvalCubic(tm, fnList[0]) - ym = SplineGenerator.EvalCubic(tm, fnList[1]) - side3 = math.sqrt((xf - xi)*(xf - xi) + (yf - yi)*(yf - yi)) - side1 = math.sqrt((xm - xi)*(xm - xi) + (ym - yi)*(ym - yi)) - side2 = math.sqrt((xf - xm)*(xf - xm) + (yf - ym)*(yf - ym)) + slopeList.append(_GetSlope([pointList[i - 1], pointList[i], pointList[i + 1]])) + else: + slopeList.append(oldSlopeList[i]) + i = i + 1 + splineList = [] + i = 0 + while (i < (l - 1)): + xfn = [0, 0, 0, 0] + yfn = [0, 0, 0, 0] + xfn[0] = pointList[i].x + yfn[0] = pointList[i].y + xfn[1] = slopeList[i].x + yfn[1] = slopeList[i].y + xfn[2] = (-3*pointList[i].x) + (-2*slopeList[i].x) + (3*pointList[i+1].x) + (-1*slopeList[i+1].x) + yfn[2] = (-3*pointList[i].y) + (-2*slopeList[i].y) + (3*pointList[i+1].y) + (-1*slopeList[i+1].y) + xfn[3] = (2*pointList[i].x) + (slopeList[i].x) + (-2*pointList[i+1].x) + (slopeList[i+1].x) + yfn[3] = (2*pointList[i].y) + (slopeList[i].y) + (-2*pointList[i+1].y) + (slopeList[i+1].y) + splineList.append([xfn,yfn]) + i = i + 1 + return splineList + + +# Algorithm here: Find the length between the two end points. Then, +# find the length from point 0 to the midpoint of the spline, and then +# from the midpoint to point 1. If the sum of these two lengths is close +# to the length of the first one, return the length. If not, recurse +# and add the lengths together. +def GetLength(fnList, tol): + return _GetLengthHelper(fnList, 0, 1, tol) + +def dist(p1, p2): + """Calculates the euclidean distance between points p1 and p2 + p1, p2 are a tuple of form (x,y)""" + return math.sqrt((p2[0]-p1[0])**2 + (p2[1] - p1[1])**2) + +#(Brady) Making it iterative (note it doesnt have underscore...) +def GetLengthHelper(fnList, ti, tf, tol): +#maybe use a stack?? + xi = EvalCubic(ti, fnList[0]) + yi = EvalCubic(ti, fnList[1]) + xf = EvalCubic(tf, fnList[0]) + yf = EvalCubic(tf, fnList[1]) + tm = (ti + tf)/float(2) + xm = EvalCubic(tm, fnList[0]) + ym = EvalCubic(tm, fnList[1]) + + side3 = dist((xi,yi), (xf,yf)) #from beginning to end + side1 = dist((xi,yi), (xm,ym)) #from middle to begining + side2 = dist((xm,ym), (xf,yf)) #from middle to end + if ((math.fabs(((side1 + side2)/float(side3)) - 1)) <= tol): + return math.fabs(side1 + side2) + #essentially else + cumSideLeft = side1 + cumSideRight = side2 + while((math.fabs(((side1 + side2)/float(side3)) -1)) > tol): try: if ((math.fabs(((side1 + side2)/float(side3)) - 1)) <= tol): return math.fabs(side1 + side2) else: - return (SplineGenerator._GetLengthHelper(fnList, ti, tm, tol)) + (SplineGenerator._GetLengthHelper(fnList, tm, tf, tol)) + return (_GetLengthHelper(fnList, ti, tm, tol)) + (_GetLengthHelper(fnList, tm, tf, tol)) except: if ((math.fabs(((side1 + side2)/float(side3)) - 1)) <= tol): return math.fabs(side1 + side2) else: - return (SplineGenerator._GetLengthHelper(fnList, ti, tm, tol)) + (SplineGenerator._GetLengthHelper(fnList, tm, tf, tol)) - - # Simply find the value of the spline function at the point. - @classmethod - def EvalCubic(cls, t, fn): - return (fn[0] + fn[1]*t + fn[2]*t*t + fn[3]*t*t*t) - - @classmethod - def EvalSlopeOfCubic(cls, t, fn): - return (fn[1] + 2*fn[2]*t + 3*fn[3]*t*t) - - # This method takes in a splineList and figures out all the points to draw - # along the spline. - # Increase the value of NUMBERPERSTEP to draw more points. Decrease to draw fewer. - # I return a list of lists - each inner list contains all the points to be - # drawn. - - @classmethod - def GetPoints(cls, splineList): - NUMBERPERSTEP = 8 - lengths = SplineGenerator.GetLengths(splineList) - i = 0 - # Ignore and feel free to remove the next lines, I think they're repetitiions of the GetLengths() function. - # while (i < len(splineList)): - # lengths.append(SplineGenerator.GetLength(splineList[i], 0.001)) - # i = i + 1 - # i = 0 - listOfPointLists = [] - while (i < len(splineList)): - total = NUMBERPERSTEP*lengths[i] - t = 0 - listOfPointLists.append([]) - xfn = splineList[i][0] - yfn = splineList[i][1] - while (t <= 1): - listOfPointLists[i].append(Point.Point(SplineGenerator.EvalCubic(t, xfn), SplineGenerator.EvalCubic(t, yfn))) - t = t + (1/float(total)) - i = i + 1 - return listOfPointLists - - - # Pass in a fraction along the spline (as a whole) that you want - # information about. For example, pass in 0.5 to get exactly halfway along. - # Returns a point, a slope, and an index at the given fractional length - # along a spline. - # The index is which spline part the point in question lies along. - - # First, I get the lengths of each spline part. I then go through and find - # the total lenght along the splines needed, and find which spline part the - # fraction will lie on. - # I then find the t value at which we need, and then just find the point - # and slope at tha t value. - # Note: Not designed for repeated use in real time, because the finding of - # the t value recurses a recursive method until it's found. - @classmethod - def GetInformationAtLengthFraction(cls, splineList, lengthFraction): - lengths = SplineGenerator.GetLengths(splineList) - totalLength = sum(lengths) - lengthNeeded = lengthFraction * totalLength - i = 0 - if (lengthNeeded > 0.1): - while (lengthNeeded > 0.1): - lengthNeeded = lengthNeeded - lengths[i] - i = i + 1 - i = i - 1 - lengthNeeded = lengthNeeded + lengths[i] - if lengthNeeded <= 0.1: - t = 0 + return (_GetLengthHelper(fnList, ti, tm, tol)) + (_GetLengthHelper(fnList, tm, tf, tol)) + +# TODO: Make iterative! It'll help. +def _GetLengthHelper(fnList, ti, tf, tol): + xi = EvalCubic(ti, fnList[0]) + yi = EvalCubic(ti, fnList[1]) + xf = EvalCubic(tf, fnList[0]) + yf = EvalCubic(tf, fnList[1]) + tm = (ti + tf)/float(2) + xm = EvalCubic(tm, fnList[0]) + ym = EvalCubic(tm, fnList[1]) + side3 = math.sqrt((xf - xi)**2 + (yf - yi)**2) #from beginning to end + side1 = math.sqrt((xm - xi)**2 + (ym - yi)**2) #from middle to begining + side2 = math.sqrt((xf - xm)**2 + (yf - ym)**2) #from middle to end + try: + if ((math.fabs(((side1 + side2)/float(side3)) - 1)) <= tol): + return math.fabs(side1 + side2) else: - t = SplineGenerator._GetTValueAtLengthHelper(splineList[i], lengthNeeded, 0, 1, 0.001, 0.001) - x = SplineGenerator.EvalCubic(t, splineList[i][0]) - y = SplineGenerator.EvalCubic(t, splineList[i][1]) - dx = SplineGenerator.EvalSlopeOfCubic(t, splineList[i][0]) - dy = SplineGenerator.EvalSlopeOfCubic(t, splineList[i][1]) - return [(Point.Point(x, y)),(Point.Point(dx, dy)), i] - - # Finds the length of each spline part in the list - @classmethod - def GetLengths(cls, splineList): - lengths = [] - i = 0 - while (i < len(splineList)): - lengths.append(SplineGenerator.GetLength(splineList[i], 0.001)) + return (_GetLengthHelper(fnList, ti, tm, tol)) + (_GetLengthHelper(fnList, tm, tf, tol)) + except: + if ((math.fabs(((side1 + side2)/float(side3)) - 1)) <= tol): + return math.fabs(side1 + side2) + else: + return (_GetLengthHelper(fnList, ti, tm, tol)) + (_GetLengthHelper(fnList, tm, tf, tol)) + +# Simply find the value of the spline function at the point. +def EvalCubic(t, fn): + return (fn[0] + fn[1]*t + fn[2]*t*t + fn[3]*t*t*t) + +def EvalSlopeOfCubic(t, fn): + return (fn[1] + 2*fn[2]*t + 3*fn[3]*t*t) + +# This method takes in a splineList and figures out all the points to draw +# along the spline. +# Increase the value of NUMBERPERSTEP to draw more points. Decrease to draw fewer. +# I return a list of lists - each inner list contains all the points to be +# drawn. + +def GetPoints(splineList): + NUMBERPERSTEP = 8 + lengths = GetLengths(splineList) + i = 0 +# Ignore and feel free to remove the next lines, I think they're repetitiions of the GetLengths() function. +# while (i < len(splineList)): +# lengths.append(SplineGenerator.GetLength(splineList[i], 0.001)) +# i = i + 1 +# i = 0 + listOfPointLists = [] + while (i < len(splineList)): + total = NUMBERPERSTEP*lengths[i] + t = 0 + listOfPointLists.append([]) + xfn = splineList[i][0] + yfn = splineList[i][1] + while (t <= 1): + listOfPointLists[i].append(Point.Point(EvalCubic(t, xfn), EvalCubic(t, yfn))) + t = t + (1/float(total)) + i = i + 1 + return listOfPointLists + + +# Pass in a fraction along the spline (as a whole) that you want +# information about. For example, pass in 0.5 to get exactly halfway along. +# Returns a point, a slope, and an index at the given fractional length +# along a spline. +# The index is which spline part the point in question lies along. + +# First, I get the lengths of each spline part. I then go through and find +# the total lenght along the splines needed, and find which spline part the +# fraction will lie on. +# I then find the t value at which we need, and then just find the point +# and slope at tha t value. +# Note: Not designed for repeated use in real time, because the finding of +# the t value recurses a recursive method until it's found. +def GetInformationAtLengthFraction(splineList, lengthFraction): + lengths = GetLengths(splineList) + totalLength = sum(lengths) + lengthNeeded = lengthFraction * totalLength + i = 0 + if (lengthNeeded > 0.1): + while (lengthNeeded > 0.1): + lengthNeeded = lengthNeeded - lengths[i] i = i + 1 - return lengths - - # A simple recursive method. Finds the midpoint of the current section - # of the spline and zeros in until the length desired is found. - # Note that while it's a recursive method, every call calls - # GetLengthHelper(), another recursive method, which is why this shouldn't - # be used in real time, if possible. - # TODO: make it iterative! Should sav both space (for obvious - # recursion-related reasons) and time (Python's not great with method calls, - # I believe.) - @classmethod - def _GetTValueAtLengthHelper(cls, fnList, length, tinit, tfinal, lengthCalcTol, lengthCompareTol): - tmid = (tinit + tfinal) / float(2) - curLength = SplineGenerator._GetLengthHelper(fnList, 0, tmid, lengthCalcTol) - if (math.fabs((curLength / float(length)) - 1) < lengthCompareTol): - return tmid + i = i - 1 + lengthNeeded = lengthNeeded + lengths[i] + if lengthNeeded <= 0.1: + t = 0 + else: + t = _GetTValueAtLengthHelper(splineList[i], lengthNeeded, 0, 1, 0.001, 0.001) + x = EvalCubic(t, splineList[i][0]) + y = EvalCubic(t, splineList[i][1]) + dx = EvalSlopeOfCubic(t, splineList[i][0]) + dy = EvalSlopeOfCubic(t, splineList[i][1]) + return [(Point.Point(x, y)),(Point.Point(dx, dy)), i] + +# Finds the length of each spline part in the list +def GetLengths(splineList): + lengths = [] + i = 0 + while (i < len(splineList)): + lengths.append(GetLength(splineList[i], 0.001)) + i = i + 1 + return lengths + +# A simple recursive method. Finds the midpoint of the current section +# of the spline and zeros in until the length desired is found. +# Note that while it's a recursive method, every call calls +# GetLengthHelper(), another recursive method, which is why this shouldn't +# be used in real time, if possible. +# TODO: make it iterative! Should sav both space (for obvious +# recursion-related reasons) and time (Python's not great with method calls, +# I believe.) +def _GetTValueAtLengthHelper(fnList, length, tinit, tfinal, lengthCalcTol, lengthCompareTol): + tmid = (tinit + tfinal) / float(2) + curLength = _GetLengthHelper(fnList, 0, tmid, lengthCalcTol) + if (math.fabs((curLength / float(length)) - 1) < lengthCompareTol): + return tmid + else: + if (curLength > length): + return _GetTValueAtLengthHelper(fnList, length, tinit, tmid, lengthCalcTol, lengthCompareTol) else: - if (curLength > length): - return SplineGenerator._GetTValueAtLengthHelper(fnList, length, tinit, tmid, lengthCalcTol, lengthCompareTol) - else: - return SplineGenerator._GetTValueAtLengthHelper(fnList, length, tmid, tfinal, lengthCalcTol, lengthCompareTol) + return _GetTValueAtLengthHelper(fnList, length, tmid, tfinal, lengthCalcTol, lengthCompareTol) diff --git a/RankPanda/GUIMain.py b/RankPanda/GUIMain.py index ac48b90..05fea2a 100755 --- a/RankPanda/GUIMain.py +++ b/RankPanda/GUIMain.py @@ -1306,6 +1306,10 @@ def OnDeleteRank(self, event): self.RefreshRankList() self.RefreshCommandList() + #TODO: (Brady) Make a select all button: OnSelectAll(self, event) + + + def OnSnapEnd(self, event): self.core.SnapEndLocations() self.field.Refresh(False) diff --git a/RankPanda/Rank.py b/RankPanda/Rank.py index 9824adb..8ea8004 100644 --- a/RankPanda/Rank.py +++ b/RankPanda/Rank.py @@ -651,20 +651,19 @@ def _CalcLengthsHelper(self, location, number): # Same with GT/PW. def _GenerateCommandListStraightLine(self, beginLocation, endLocation,\ length, commandListSoFar): - begin0 = beginLocation.GetListOfPoints()[0] - begin1 = beginLocation.GetListOfPoints()[-1] + begin0 = beginLocation.GetListOfPoints()[0] #(Brady) "Left" end + begin1 = beginLocation.GetListOfPoints()[-1] #(Brady) "Right" end (i think...) end0 = endLocation.GetListOfPoints()[0] end1 = endLocation.GetListOfPoints()[-1] endMid = endLocation.GetMidPoint() beginMid = beginLocation.GetMidPoint() #LMD change to round beginLength = int(round(math.sqrt(\ - (begin1.x - begin0.x)*(begin1.x - begin0.x)\ - + (begin1.y - begin0.y)*(begin1.y - begin0.y)))) + (begin1.x - begin0.x)**2 + (begin1.y - begin0.y)**2))) #LMD change to round endLength = int(round(math.sqrt(\ - (end1.x - end0.x)*(end1.x - end0.x)\ - + (end1.y - end0.y)*(end1.y - end0.y)))) + (end1.x - end0.x)**2 + (end1.y - end0.y)**2))) + #(Brady) Pretty sure this assumes that in usually ranks are 16 steps long (which is generally true) if (beginLength != 16): newMid1x = (begin1.x - begin0.x)*(8/beginLength) + begin0.x newMid1y = (begin1.y - begin0.y)*(8/beginLength) + begin0.y diff --git a/RankPanda/RankLocation.py b/RankPanda/RankLocation.py index 447debd..63ef584 100755 --- a/RankPanda/RankLocation.py +++ b/RankPanda/RankLocation.py @@ -102,10 +102,10 @@ def SetListOfPoints(self, listOfPoints, listOfSlopes): self._listOfSlopes = listOfSlopes self._splineFunctions = \ - CHS.SplineGenerator.GetSplines(self._listOfPoints, + CHS.GetSplines(self._listOfPoints, self._listOfSlopes) self._drawingPoints = \ - CHS.SplineGenerator.GetPoints(self._splineFunctions) + CHS.GetPoints(self._splineFunctions) else: self._listOfSlopes = None self._splineFunctions = None @@ -132,7 +132,7 @@ def GetMidPoint(self): def _Respline(self): self._splineFunctions = \ - CHS.SplineGenerator.GetSplines(self._listOfPoints, + CHS.GetSplines(self._listOfPoints, self._listOfSlopes) def GetPointAtT(self, t, number): @@ -153,8 +153,8 @@ def GetPointAtT(self, t, number): y = pfirst.y + t*(psecond.y - pfirst.y) return Point.Point(x,y) else: - x = CHS.SplineGenerator.EvalCubic(t, self._splineFunctions[number][0]) - y = CHS.SplineGenerator.EvalCubic(t, self._splineFunctions[number][1]) + x = CHS.EvalCubic(t, self._splineFunctions[number][0]) + y = CHS.EvalCubic(t, self._splineFunctions[number][1]) return Point.Point(x,y) @@ -175,7 +175,7 @@ def GetInformationAtLengthFraction(self, lengthFraction): if (not self.IsStraight()): if (self.curved): - return CHS.SplineGenerator.GetInformationAtLengthFraction(self._splineFunctions, lengthFraction) + return CHS.GetInformationAtLengthFraction(self._splineFunctions, lengthFraction) lengths = self.GetLengths() totalLength = sum(lengths) @@ -223,7 +223,7 @@ def GetLengths(self): """ if (not self.IsStraight()): if (self.curved): - return CHS.SplineGenerator.GetLengths( + return CHS.GetLengths( self._splineFunctions) i = 1 lengths = [] From 263d266ed5c954466d146b4fd83278d81de748a8 Mon Sep 17 00:00:00 2001 From: brajac709 Date: Wed, 19 Jun 2013 17:04:34 -0400 Subject: [PATCH 4/8] updated README Adds instructions for use with Python 2.7 --- ReadMe.txt | 46 ++++++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/ReadMe.txt b/ReadMe.txt index 28e0aa6..afd8a29 100644 --- a/ReadMe.txt +++ b/ReadMe.txt @@ -1,18 +1,28 @@ -1) Install Python 2.6 available in several formats at http://www.python.org/download/releases/2.6/ -2) Place the site-packages folder contained alongside this readme in the Python26\Lib folder. - (Overwrite the already existing site-packages folder) - The location of Python26 will depend on where you install it when you install python 2.6 -3) If you're using Windows, place the DejaVuSans.ttf and DejaVuSansMono.ttf files in C:\WINDOWS\Fonts. - If you're using Mac OS X, place the DejaVuSans.ttf and DejaVuSansMono.ttf files in the fonts folder of the library folder. - If you're using Mac OS 9.x or 8.x, place the DejaVuSans.ttf and DejaVuSansMono.ttf files in the System Folder. - If you're using Linux, these fonts are already included in /usr/share/fonts/truetype. -4) To Run: - * Linux, maybe Mac: run RankPanda.sh in the same directory as this readme. - * Other: Open the RankPanda folder contained alongside this readme. Double click on GUIMain.py to run. - -Special instructions for Ubuntu: - You will need some additional python packages: - - sudo apt-get install python2.6-wxgtk2.8 - sudo apt-get install python-reportlab - sudo apt-get install python2.6-pygame +1) Install Python 2.6 available in several formats at http://www.python.org/download/releases/2.6/ +2) Place the site-packages folder contained alongside this readme in the Python26\Lib folder. + (Overwrite the already existing site-packages folder) + The location of Python26 will depend on where you install it when you install python 2.6 +3) If you're using Windows, place the DejaVuSans.ttf and DejaVuSansMono.ttf files in C:\WINDOWS\Fonts. + If you're using Mac OS X, place the DejaVuSans.ttf and DejaVuSansMono.ttf files in the fonts folder of the library folder. + If you're using Mac OS 9.x or 8.x, place the DejaVuSans.ttf and DejaVuSansMono.ttf files in the System Folder. + If you're using Linux, these fonts are already included in /usr/share/fonts/truetype. +4) To Run: + * Linux, maybe Mac: run RankPanda.sh in the same directory as this readme. + * Other: Open the RankPanda folder contained alongside this readme. Double click on GUIMain.py to run. + +Special instructions for Ubuntu: + You will need some additional python packages: + + sudo apt-get install python2.6-wxgtk2.8 + sudo apt-get install python-reportlab + sudo apt-get install python2.6-pygame + + +UPDATED INSTRUCTIONS: (as of 6/19/13 - Brady) + Using python 2.7 should work now (only tested on Windows and using 32 bit version of 2.7) + In order to use version 2.7 you must download pygame and wxpython for version 2.7, which can be found here + http://www.pygame.org/download.shtml + http://www.wxpython.org/download.php + Install or build them and put them in the site-packages folder of PythonXX\Lib folder (where XX is the version of python you installed) + Python 2.7 may be downloaded from http://www.python.org/getit/ + NOTE: RankPanda will not work with Python 3. \ No newline at end of file From 59531fcf6938069a3c63a9b4bbfcf65eb3010dbf Mon Sep 17 00:00:00 2001 From: brajac709 Date: Fri, 21 Jun 2013 17:21:17 -0400 Subject: [PATCH 5/8] Style fixes + more iterative Made iterative versions of _GetLengthHelper and _GetTValueAtLengthHelper. Baseline tests show that they do the same thing as recursive versions but more tests may be needed. --- RankPanda/Commands.py | 21 +- RankPanda/CoreWrapper.py | 52 +- RankPanda/CubicHermiteSpline.py | 586 ++++--- RankPanda/GUIDialogs.py | 6 +- RankPanda/GUIField.py | 2544 +++++++++++++-------------- RankPanda/GUIMain.py | 2913 +++++++++++++++---------------- RankPanda/GUIRankList.py | 148 +- RankPanda/GUITimeBar.py | 488 +++--- RankPanda/Move.py | 618 +++---- RankPanda/Point.py | 126 +- RankPanda/Printer.py | 340 ++-- RankPanda/Rank.py | 1772 +++++++++---------- RankPanda/RankIDGen.py | 22 +- RankPanda/RankLocation.py | 532 +++--- RankPanda/Song.py | 6 +- RankPanda/Spline_test.py | 18 + RankPanda/Tester.py | 422 ++--- ReadMe.txt | 3 +- 18 files changed, 5343 insertions(+), 5274 deletions(-) create mode 100644 RankPanda/Spline_test.py diff --git a/RankPanda/Commands.py b/RankPanda/Commands.py index 7882f56..871c816 100755 --- a/RankPanda/Commands.py +++ b/RankPanda/Commands.py @@ -19,7 +19,8 @@ def CalcLocation(self, count, beginLocation): """ Given the beginning location, calculates the location that the rank should be in after count counts. Uses information such as self._delta and the command type, but does NOT use - self.endLocation. """ + self.endLocation. + """ return beginLocation @@ -28,7 +29,8 @@ def CalcLocation(self, count, beginLocation): def Split(self, count, beginLocation): """ Splits the command in two, at the specified count. Changes this command to be the appropriate length, and returns this command as well as the - new one.""" + new one. + """ pass @@ -44,7 +46,8 @@ def SetName(self, name): def GetLength(self): """Returns the number of counts this command spans. If you want to change - this value, make a new command instead.""" + this value, make a new command instead. + """ return round(self._length) @@ -59,17 +62,19 @@ def SnapEndLocation(self, newBeginLocation): # Be sure to overwrite this in each Command! def CalcBeginLocation(self, count, endLocation): - """From the ending location, calculate the beginning location. Will require - basically the same implementation as CalcLocation(), but in reverse. - count should be the location in the whole command you want it to be - returned. So, pass in 0 to run the whole command.""" + """From the ending location, calculate the beginning location. + Will require basically the same implementation as CalcLocation(), + but in reverse. count should be the location in the whole command + you want it to be returned. So, pass in 0 to run the whole command. + """ return None def MergeWithFollowing(self, following): """ Checks following to see if it's a command of the same name. If so, - return the merged commands.""" + return the merged commands. + """ if ((following._name == self._name) and (isinstance(following, self))): self._length = self._length + following._length self._endLocation = following._endLocation diff --git a/RankPanda/CoreWrapper.py b/RankPanda/CoreWrapper.py index fce563a..f63c1be 100755 --- a/RankPanda/CoreWrapper.py +++ b/RankPanda/CoreWrapper.py @@ -52,16 +52,8 @@ def GetSong(self): # [(0, 'Move 0'), (1, 'Move 1'), ...] def GetMoves(self): listOfMoves = self._song.GetMoveList() - #(Brady) Trying list comprehension return [(curmove.GetNumber(), curmove.GetName()) for curmove in listOfMoves] - #Original Code - #i = 0 - #moveInfo = [] - #while (i < len(listOfMoves)): - # curmove = listOfMoves[i] - # moveInfo.append((curmove.GetNumber(), curmove.GetName())) - # i = i + 1 - #return moveInfo + @@ -79,7 +71,6 @@ def GetCurrentMove(self): # ? # List of all ranks in current move # [(id0, name0, location0), (id1, name1, location1)] - #(Brady) Merged next method with this def GetRanks(self, moveNumber = None): if (self._song.currentMove is None): return None @@ -87,31 +78,9 @@ def GetRanks(self, moveNumber = None): allRanks = self._song.currentMove.GetAllRanks() else: allRanks = self._song.GetMoveList()[moveNumber].GetAllRanks() - #(Brady) List comprehensions - return [(r.GetID(), r.GetName(), r.GetEndLocation(), r.GetLabelLocation()) for r in allRanks] - #Orig. code - #i = 0 - #allRankInfo = [] - #while (i < len(allRanks)): - # allRankInfo.append((allRanks[i].GetID(), allRanks[i].GetName(), allRanks[i].GetEndLocation(), allRanks[i].GetLabelLocation())) - # i = i + 1 - #return allRankInfo - - #(Brady) merged with prev with optional args. Keeping this so I dont have to change all the method calls. - # Same as above but now you input the move number. - def GetRanksGivenMove(self, moveNumber): - return self.GetRanks(moveNumber) - # if (self._song.currentMove is None): - # return None - # allRanks = self._song.GetMoveList()[moveNumber].GetAllRanks() - # i = 0 - # allRankInfo = [] - # while (i < len(allRanks)): - # allRankInfo.append((allRanks[i].GetID(), allRanks[i].GetName(), allRanks[i].GetEndLocation(), allRanks[i].GetLabelLocation())) - # i = i + 1 - # return allRankInfo - - + return [(r.GetID(), r.GetName(), r.GetEndLocation(),\ + r.GetLabelLocation()) for r in allRanks] + def IsRankHeld(self, ID): if (self._song.currentMove is None): return None @@ -124,15 +93,8 @@ def GetSelectedRanks(self): if (self._song.currentMove is None): return None allSelectedRanks = self._song.currentMove.GetSelectedRanks() - #(Brady) List comp. - return [(r.GetID(), r.GetName(), r.GetEndLocation(), r.GetLabelLocation()) for r in allSelectedRanks] - #Orig. code - #i = 0 - #allSelectedRankInfo = [] - #while (i < len(allSelectedRanks)): - # allSelectedRankInfo.append((allSelectedRanks[i].GetID(), allSelectedRanks[i].GetName(), allSelectedRanks[i].GetEndLocation(), allSelectedRanks[i].GetLabelLocation())) - # i = i + 1 - #return allSelectedRankInfo + return [(r.GetID(), r.GetName(), r.GetEndLocation(),\ + r.GetLabelLocation()) for r in allSelectedRanks] def GetAdditionalRanks(self): curList = [] @@ -789,7 +751,7 @@ def RankDeleteSpline(self, rankID, number): return None self._song.currentMove.LookUpID(rankID).DeleteSplinePoint(number) - #(Brady) TODO: prevent changing name if not on the very first move. + #(Brady) TODO: prevent changing name if not on the very first move. # Pass in the ID of the rank to name, and its new name. def NameRank(self, name): if (self._song.currentMove is None): diff --git a/RankPanda/CubicHermiteSpline.py b/RankPanda/CubicHermiteSpline.py index 3e718e1..9beb8bf 100755 --- a/RankPanda/CubicHermiteSpline.py +++ b/RankPanda/CubicHermiteSpline.py @@ -1,255 +1,331 @@ -import math -import Point - - -# This class handles the creation of splines. -# A Cubic Hermite Spline can be defined by a list of points and their slopes. -# Once all these points and slopes are had, a smooth spline is defined between -# each consecutive two points. -# Let x(t) be a spline between two points, xi and xj (j = i + 1). -# Let the slope at xi be mi and the slope at xj be mj. -# Then, x(t) is defined as: -# x(t) = (2t^3 - 3t^2 + 1)xi + (t^3 - 2t^2 + t)mi + (-2t^3 + 3t^2)xj + (t^3 - t^2)mj -# Repeat to get y(t). -# When you shift t from 0 to 1, x(t) and y(t) will generate a smooth curve -# between each pair of points. Further, the curves will all be smooth with -# the one next to them, as the slopes are equal. -# The splines are saved in a simple format; see below. - -# TODO(astory): convert to module level functions. None of these actually take -# advantage of, or have any reason for, being in a class. - - -# This calculated what the slopes at the points should be for the splines. -# The method we use simply draws a straight line between the point before -# and the point after, and this becomes the slope of the point. -# Note that the slopes are dx/dt and dy/dt, not dx/dy. -def _GetSlope(PointTriple): - p0 = PointTriple[0] - p1 = PointTriple[1] - p2 = PointTriple[2] - pFirst = None - pSecond = None - n = 1 - if (p0 is None): - pFirst = p1 - pSecond = p2 - n = 2 - elif (p2 is None): - pFirst = p0 - pSecond = p1 - n = 2 - else: - pFirst = p0 - pSecond = p2 - slopex = (n/float(2))*(pSecond.x - pFirst.x) - slopey = (n/float(2))*(pSecond.y - pFirst.y) - return Point.Point(slopex, slopey) - -# The main splining method. Takes in a list of points and their slopes, -# and calculates the splines connecting them. Farily straightforward, once -# you know how it works, as described above. -# Note that is any slope is None, it'll automatically be calculated. -# This should often be the case, except when doing something like -# intermediate DTP locations or something. -# A given spline is stored as a list of four elements: -# [constantTerm, linearTerm, quadraticTerm, cubicTerm] -# The spline lists are then saved as [xsplines, ysplines] -# Each one is saved in order. -def GetSplines(pointList, oldSlopeList): - slopeList = [] - l = len(pointList) - i = 0 - while (i < l): - if (oldSlopeList[i] is None): - if (i == 0): - slopeList.append(_GetSlope([None, pointList[0], pointList[1]])) - elif (i == (l - 1)): - slopeList.append(_GetSlope([pointList[i - 1], pointList[i], None])) - else: - slopeList.append(_GetSlope([pointList[i - 1], pointList[i], pointList[i + 1]])) - else: - slopeList.append(oldSlopeList[i]) - i = i + 1 - splineList = [] - i = 0 - while (i < (l - 1)): - xfn = [0, 0, 0, 0] - yfn = [0, 0, 0, 0] - xfn[0] = pointList[i].x - yfn[0] = pointList[i].y - xfn[1] = slopeList[i].x - yfn[1] = slopeList[i].y - xfn[2] = (-3*pointList[i].x) + (-2*slopeList[i].x) + (3*pointList[i+1].x) + (-1*slopeList[i+1].x) - yfn[2] = (-3*pointList[i].y) + (-2*slopeList[i].y) + (3*pointList[i+1].y) + (-1*slopeList[i+1].y) - xfn[3] = (2*pointList[i].x) + (slopeList[i].x) + (-2*pointList[i+1].x) + (slopeList[i+1].x) - yfn[3] = (2*pointList[i].y) + (slopeList[i].y) + (-2*pointList[i+1].y) + (slopeList[i+1].y) - splineList.append([xfn,yfn]) - i = i + 1 - return splineList - - -# Algorithm here: Find the length between the two end points. Then, -# find the length from point 0 to the midpoint of the spline, and then -# from the midpoint to point 1. If the sum of these two lengths is close -# to the length of the first one, return the length. If not, recurse -# and add the lengths together. -def GetLength(fnList, tol): - return _GetLengthHelper(fnList, 0, 1, tol) - -def dist(p1, p2): - """Calculates the euclidean distance between points p1 and p2 - p1, p2 are a tuple of form (x,y)""" - return math.sqrt((p2[0]-p1[0])**2 + (p2[1] - p1[1])**2) - -#(Brady) Making it iterative (note it doesnt have underscore...) -def GetLengthHelper(fnList, ti, tf, tol): -#maybe use a stack?? - xi = EvalCubic(ti, fnList[0]) - yi = EvalCubic(ti, fnList[1]) - xf = EvalCubic(tf, fnList[0]) - yf = EvalCubic(tf, fnList[1]) - tm = (ti + tf)/float(2) - xm = EvalCubic(tm, fnList[0]) - ym = EvalCubic(tm, fnList[1]) - - side3 = dist((xi,yi), (xf,yf)) #from beginning to end - side1 = dist((xi,yi), (xm,ym)) #from middle to begining - side2 = dist((xm,ym), (xf,yf)) #from middle to end - if ((math.fabs(((side1 + side2)/float(side3)) - 1)) <= tol): - return math.fabs(side1 + side2) - #essentially else - cumSideLeft = side1 - cumSideRight = side2 - while((math.fabs(((side1 + side2)/float(side3)) -1)) > tol): - try: - if ((math.fabs(((side1 + side2)/float(side3)) - 1)) <= tol): - return math.fabs(side1 + side2) - else: - return (_GetLengthHelper(fnList, ti, tm, tol)) + (_GetLengthHelper(fnList, tm, tf, tol)) - except: - if ((math.fabs(((side1 + side2)/float(side3)) - 1)) <= tol): - return math.fabs(side1 + side2) - else: - return (_GetLengthHelper(fnList, ti, tm, tol)) + (_GetLengthHelper(fnList, tm, tf, tol)) - -# TODO: Make iterative! It'll help. -def _GetLengthHelper(fnList, ti, tf, tol): - xi = EvalCubic(ti, fnList[0]) - yi = EvalCubic(ti, fnList[1]) - xf = EvalCubic(tf, fnList[0]) - yf = EvalCubic(tf, fnList[1]) - tm = (ti + tf)/float(2) - xm = EvalCubic(tm, fnList[0]) - ym = EvalCubic(tm, fnList[1]) - side3 = math.sqrt((xf - xi)**2 + (yf - yi)**2) #from beginning to end - side1 = math.sqrt((xm - xi)**2 + (ym - yi)**2) #from middle to begining - side2 = math.sqrt((xf - xm)**2 + (yf - ym)**2) #from middle to end - try: - if ((math.fabs(((side1 + side2)/float(side3)) - 1)) <= tol): - return math.fabs(side1 + side2) - else: - return (_GetLengthHelper(fnList, ti, tm, tol)) + (_GetLengthHelper(fnList, tm, tf, tol)) - except: - if ((math.fabs(((side1 + side2)/float(side3)) - 1)) <= tol): - return math.fabs(side1 + side2) - else: - return (_GetLengthHelper(fnList, ti, tm, tol)) + (_GetLengthHelper(fnList, tm, tf, tol)) - -# Simply find the value of the spline function at the point. -def EvalCubic(t, fn): - return (fn[0] + fn[1]*t + fn[2]*t*t + fn[3]*t*t*t) - -def EvalSlopeOfCubic(t, fn): - return (fn[1] + 2*fn[2]*t + 3*fn[3]*t*t) - -# This method takes in a splineList and figures out all the points to draw -# along the spline. -# Increase the value of NUMBERPERSTEP to draw more points. Decrease to draw fewer. -# I return a list of lists - each inner list contains all the points to be -# drawn. - -def GetPoints(splineList): - NUMBERPERSTEP = 8 - lengths = GetLengths(splineList) - i = 0 -# Ignore and feel free to remove the next lines, I think they're repetitiions of the GetLengths() function. -# while (i < len(splineList)): -# lengths.append(SplineGenerator.GetLength(splineList[i], 0.001)) -# i = i + 1 -# i = 0 - listOfPointLists = [] - while (i < len(splineList)): - total = NUMBERPERSTEP*lengths[i] - t = 0 - listOfPointLists.append([]) - xfn = splineList[i][0] - yfn = splineList[i][1] - while (t <= 1): - listOfPointLists[i].append(Point.Point(EvalCubic(t, xfn), EvalCubic(t, yfn))) - t = t + (1/float(total)) - i = i + 1 - return listOfPointLists - - -# Pass in a fraction along the spline (as a whole) that you want -# information about. For example, pass in 0.5 to get exactly halfway along. -# Returns a point, a slope, and an index at the given fractional length -# along a spline. -# The index is which spline part the point in question lies along. - -# First, I get the lengths of each spline part. I then go through and find -# the total lenght along the splines needed, and find which spline part the -# fraction will lie on. -# I then find the t value at which we need, and then just find the point -# and slope at tha t value. -# Note: Not designed for repeated use in real time, because the finding of -# the t value recurses a recursive method until it's found. -def GetInformationAtLengthFraction(splineList, lengthFraction): - lengths = GetLengths(splineList) - totalLength = sum(lengths) - lengthNeeded = lengthFraction * totalLength - i = 0 - if (lengthNeeded > 0.1): - while (lengthNeeded > 0.1): - lengthNeeded = lengthNeeded - lengths[i] - i = i + 1 - i = i - 1 - lengthNeeded = lengthNeeded + lengths[i] - if lengthNeeded <= 0.1: - t = 0 - else: - t = _GetTValueAtLengthHelper(splineList[i], lengthNeeded, 0, 1, 0.001, 0.001) - x = EvalCubic(t, splineList[i][0]) - y = EvalCubic(t, splineList[i][1]) - dx = EvalSlopeOfCubic(t, splineList[i][0]) - dy = EvalSlopeOfCubic(t, splineList[i][1]) - return [(Point.Point(x, y)),(Point.Point(dx, dy)), i] - -# Finds the length of each spline part in the list -def GetLengths(splineList): - lengths = [] - i = 0 - while (i < len(splineList)): - lengths.append(GetLength(splineList[i], 0.001)) - i = i + 1 - return lengths - -# A simple recursive method. Finds the midpoint of the current section -# of the spline and zeros in until the length desired is found. -# Note that while it's a recursive method, every call calls -# GetLengthHelper(), another recursive method, which is why this shouldn't -# be used in real time, if possible. -# TODO: make it iterative! Should sav both space (for obvious -# recursion-related reasons) and time (Python's not great with method calls, -# I believe.) -def _GetTValueAtLengthHelper(fnList, length, tinit, tfinal, lengthCalcTol, lengthCompareTol): - tmid = (tinit + tfinal) / float(2) - curLength = _GetLengthHelper(fnList, 0, tmid, lengthCalcTol) - if (math.fabs((curLength / float(length)) - 1) < lengthCompareTol): - return tmid - else: - if (curLength > length): - return _GetTValueAtLengthHelper(fnList, length, tinit, tmid, lengthCalcTol, lengthCompareTol) - else: - return _GetTValueAtLengthHelper(fnList, length, tmid, tfinal, lengthCalcTol, lengthCompareTol) +import math +import Point +import decimal + +D = decimal.Decimal + + +# This class handles the creation of splines. +# A Cubic Hermite Spline can be defined by a list of points and their slopes. +# Once all these points and slopes are had, a smooth spline is defined between +# each consecutive two points. +# Let x(t) be a spline between two points, xi and xj (j = i + 1). +# Let the slope at xi be mi and the slope at xj be mj. +# Then, x(t) is defined as: +# x(t) = (2t^3 - 3t^2 + 1)xi + (t^3 - 2t^2 + t)mi + (-2t^3 + 3t^2)xj + (t^3 - t^2)mj +# Repeat to get y(t). +# When you shift t from 0 to 1, x(t) and y(t) will generate a smooth curve +# between each pair of points. Further, the curves will all be smooth with +# the one next to them, as the slopes are equal. +# The splines are saved in a simple format; see below. + + +# This calculated what the slopes at the points should be for the splines. +# The method we use simply draws a straight line between the point before +# and the point after, and this becomes the slope of the point. +# Note that the slopes are dx/dt and dy/dt, not dx/dy. +def _GetSlope(PointTriple): + p0 = PointTriple[0] + p1 = PointTriple[1] + p2 = PointTriple[2] + pFirst = None + pSecond = None + n = 1 + if (p0 is None): + pFirst = p1 + pSecond = p2 + n = 2 + elif (p2 is None): + pFirst = p0 + pSecond = p1 + n = 2 + else: + pFirst = p0 + pSecond = p2 + slopex = (n/2)*(pSecond.x - pFirst.x) + slopey = (n/2)*(pSecond.y - pFirst.y) + return Point.Point(slopex, slopey) + +# The main splining method. Takes in a list of points and their slopes, +# and calculates the splines connecting them. Farily straightforward, once +# you know how it works, as described above. +# Note that is any slope is None, it'll automatically be calculated. +# This should often be the case, except when doing something like +# intermediate DTP locations or something. +# A given spline is stored as a list of four elements: +# [constantTerm, linearTerm, quadraticTerm, cubicTerm] +# The spline lists are then saved as [xsplines, ysplines] +# Each one is saved in order. +def GetSplines(pointList, oldSlopeList): + slopeList = [] + l = len(pointList) + i = 0 + while (i < l): + if (oldSlopeList[i] is None): + if (i == 0): + slopeList.append(_GetSlope([None, pointList[0], pointList[1]])) + elif (i == (l - 1)): + slopeList.append(_GetSlope([pointList[i - 1], pointList[i], None])) + else: + slopeList.append(_GetSlope([pointList[i - 1], pointList[i], pointList[i + 1]])) + else: + slopeList.append(oldSlopeList[i]) + i = i + 1 + splineList = [] + i = 0 + while (i < (l - 1)): + xfn = [0, 0, 0, 0] + yfn = [0, 0, 0, 0] + xfn[0] = pointList[i].x + yfn[0] = pointList[i].y + xfn[1] = slopeList[i].x + yfn[1] = slopeList[i].y + xfn[2] = (-3*pointList[i].x) + (-2*slopeList[i].x) + (3*pointList[i+1].x) + (-1*slopeList[i+1].x) + yfn[2] = (-3*pointList[i].y) + (-2*slopeList[i].y) + (3*pointList[i+1].y) + (-1*slopeList[i+1].y) + xfn[3] = (2*pointList[i].x) + (slopeList[i].x) + (-2*pointList[i+1].x) + (slopeList[i+1].x) + yfn[3] = (2*pointList[i].y) + (slopeList[i].y) + (-2*pointList[i+1].y) + (slopeList[i+1].y) + splineList.append([xfn,yfn]) + i = i + 1 + return splineList + + +# Algorithm here: Find the length between the two end points. Then, +# find the length from point 0 to the midpoint of the spline, and then +# from the midpoint to point 1. If the sum of these two lengths is close +# to the length of the first one, return the length. If not, recurse +# and add the lengths together. +def GetLength(fnList, tol): + #return _GetLengthHelper(fnList, 0, 1, tol) + return GetLengthHelper(fnList, 0, 1, tol) + +def dist(p1, p2): + """Calculates the euclidean distance between points p1 and p2 + p1, p2 are a tuple of form (x,y) + """ + return math.sqrt((p2[0]-p1[0])**2 + (p2[1] - p1[1])**2) + +def findSideLengths(tif, fnList): + """Just a helper function to consolidate these actions. + tif is a tuple (ti,tf). returns a tuple (tm, side i-f, side i-m, side m-f) + """ + xi = EvalCubic(tif[0], fnList[0]) + yi = EvalCubic(tif[0], fnList[1]) + xf = EvalCubic(tif[1], fnList[0]) + yf = EvalCubic(tif[1], fnList[1]) + tm = (tif[0] + tif[1])/float(2) + xm = EvalCubic(tm, fnList[0]) + ym = EvalCubic(tm, fnList[1]) + side3 = dist((xi,yi), (xf,yf)) #from beginning to end (TODO: make a check for this being zero) + side1 = dist((xi,yi), (xm,ym)) #from middle to begining + side2 = dist((xm,ym), (xf,yf)) #from middle to end + return (tm, side3, side1, side2) + +#(Brady) Making it iterative (note it doesnt have underscore...) +def GetLengthHelper(fnList, ti, tf, tol): +#TODO: FIX IT!!! the "r" case is a little broken, sometimes need to pop twice, others not... + states = ["d", (ti,tf)] #a state is represented by a tuple of (ti, tf) for each phase + #try using some sort of call address (probably a string in this case) to identify what type of state this is... + #"d" => done + address = "s" #i think i use "s" for start, or like base case, or something... basically still recursing. + #import time #for debugging.... + while(len(states) > 0): + #time.sleep(.1) # Temp... + if address == "d": + returnValue = states.pop() + elif address == "l": #this is a lowercase L not a 1 + val = states.pop() + p = states.pop() + tm, side3, side1, side2 = findSideLengths(p, fnList) + if ((math.fabs(((side1 + side2)/float(side3)) - 1 )) <= tol): + address = states.pop() + states.append(val + math.fabs(side1 + side2)) + else: + states.append(val) + states.append("r") + states.append((tm,p[1])) + states.append("l") + states.append((p[0],tm)) + address = "s" + elif address == "r": + val1 = states.pop() + val2 = states.pop() + if val2 == "d": + returnValue = val1 #should be the length.... otherwise, keep going + break + elif val2 == "l" or val2 == "r": #not really sure if necessary, or right... + address = val2 + states.append(val1) + continue + address = states.pop() + address = states.pop() #need to pop twice... + states.append(val1 + val2) + elif address == "s": + #still recursing + p = states.pop() + tm, side3, side1, side2 = findSideLengths(p, fnList) + if ((math.fabs(((side1 + side2)/float(side3)) - 1)) <= tol): + #base case + address = states.pop() + states.append(math.fabs(side1 + side2)) #push the length of this segment. + #i think.... + else: + #push the 2 sides + #push "r", (tm,tf),"l", (ti,tm) tentatively + #address = "s" + states.append("r") + states.append((tm, p[1])) + states.append("l") + states.append((p[0],tm)) + address = "s" + else: #basically if the address is a number not a string. + val = states.pop() + newval = address + val + address = states.pop() + states.append(newval) + #print(states) + #print(address) + return returnValue + +# TODO: Make iterative! It'll help. +def _GetLengthHelper(fnList, ti, tf, tol): + xi = EvalCubic(ti, fnList[0]) + yi = EvalCubic(ti, fnList[1]) + xf = EvalCubic(tf, fnList[0]) + yf = EvalCubic(tf, fnList[1]) + tm = (ti + tf)/float(2) + xm = EvalCubic(tm, fnList[0]) + ym = EvalCubic(tm, fnList[1]) + side3 = math.sqrt((xf - xi)**2 + (yf - yi)**2) #from beginning to end + side1 = math.sqrt((xm - xi)**2 + (ym - yi)**2) #from middle to begining + side2 = math.sqrt((xf - xm)**2 + (yf - ym)**2) #from middle to end + try: + if ((math.fabs(((side1 + side2)/float(side3)) - 1)) <= tol): + return math.fabs(side1 + side2) + else: + return (_GetLengthHelper(fnList, ti, tm, tol)) + (_GetLengthHelper(fnList, tm, tf, tol)) + except: + if ((math.fabs(((side1 + side2)/float(side3)) - 1)) <= tol): + return math.fabs(side1 + side2) + else: + return (_GetLengthHelper(fnList, ti, tm, tol)) + (_GetLengthHelper(fnList, tm, tf, tol)) + +# Simply find the value of the spline function at the point. +def EvalCubic(t, fn): + return (fn[0] + fn[1]*t + fn[2]*t*t + fn[3]*t*t*t) + +def EvalSlopeOfCubic(t, fn): + return (fn[1] + 2*fn[2]*t + 3*fn[3]*t*t) + +# This method takes in a splineList and figures out all the points to draw +# along the spline. +# Increase the value of NUMBERPERSTEP to draw more points. Decrease to draw fewer. +# I return a list of lists - each inner list contains all the points to be +# drawn. + +def GetPoints(splineList): + NUMBERPERSTEP = 8 + lengths = GetLengths(splineList) + i = 0 +# Ignore and feel free to remove the next lines, I think they're repetitiions of the GetLengths() function. +# while (i < len(splineList)): +# lengths.append(SplineGenerator.GetLength(splineList[i], 0.001)) +# i = i + 1 +# i = 0 + listOfPointLists = [] + while (i < len(splineList)): + total = NUMBERPERSTEP*lengths[i] + t = 0 + listOfPointLists.append([]) + xfn = splineList[i][0] + yfn = splineList[i][1] + while (t <= 1): + listOfPointLists[i].append(Point.Point(EvalCubic(t, xfn), EvalCubic(t, yfn))) + t = t + (1/float(total)) + i = i + 1 + return listOfPointLists + + +# Pass in a fraction along the spline (as a whole) that you want +# information about. For example, pass in 0.5 to get exactly halfway along. +# Returns a point, a slope, and an index at the given fractional length +# along a spline. +# The index is which spline part the point in question lies along. + +# First, I get the lengths of each spline part. I then go through and find +# the total length along the splines needed, and find which spline part the +# fraction will lie on. +# I then find the t value at which we need, and then just find the point +# and slope at tha t value. +# Note: Not designed for repeated use in real time, because the finding of +# the t value recurses a recursive method until it's found. +def GetInformationAtLengthFraction(splineList, lengthFraction): + lengths = GetLengths(splineList) + totalLength = sum(lengths) + lengthNeeded = lengthFraction * totalLength + i = 0 + if (lengthNeeded > 0.1): + while (lengthNeeded > 0.1): + lengthNeeded = lengthNeeded - lengths[i] + i = i + 1 + i = i - 1 + lengthNeeded = lengthNeeded + lengths[i] + if lengthNeeded <= 0.1: + t = 0 + else: + #t = _GetTValueAtLengthHelper(splineList[i], lengthNeeded, 0, 1, 0.001, 0.001) + #TODO MAKE SURE THIS WORKS!!!! + t = GetTValueAtLengthHelper(splineList[i], lengthNeeded, 0, 1, 0.001, 0.001) + x = EvalCubic(t, splineList[i][0]) + y = EvalCubic(t, splineList[i][1]) + dx = EvalSlopeOfCubic(t, splineList[i][0]) + dy = EvalSlopeOfCubic(t, splineList[i][1]) + return [(Point.Point(x, y)),(Point.Point(dx, dy)), i] + +# Finds the length of each spline part in the list +def GetLengths(splineList): + lengths = [] + i = 0 + while (i < len(splineList)): + lengths.append(GetLength(splineList[i], 0.001)) + i = i + 1 + return lengths + +# A simple recursive method. Finds the midpoint of the current section +# of the spline and zeros in until the length desired is found. +# Note that while it's a recursive method, every call calls +# GetLengthHelper(), another recursive method, which is why this shouldn't +# be used in real time, if possible. +# TODO: make it iterative! Should sav both space (for obvious +# recursion-related reasons) and time (Python's not great with method calls, +# I believe.) +def _GetTValueAtLengthHelper(fnList, length, tinit, tfinal, lengthCalcTol, lengthCompareTol): + tmid = (tinit + tfinal) / float(2) + #curLength = _GetLengthHelper(fnList, 0, tmid, lengthCalcTol) + curLength = GetLengthHelper(fnList, 0, tmid, lengthCalcTol) + if (math.fabs((curLength / float(length)) - 1) < lengthCompareTol): + return tmid + else: + if (curLength > length): + return _GetTValueAtLengthHelper(fnList, length, tinit, tmid, lengthCalcTol, lengthCompareTol) + else: + return _GetTValueAtLengthHelper(fnList, length, tmid, tfinal, lengthCalcTol, lengthCompareTol) + +def GetTValueAtLengthHelper(fnList, length, tinit, tfinal, lengthCalcTol, lengthCompareTol): + """Iterative version of above. Finds the midpoint of the current section of spline + and zeros in until the length desired is found. + """ + stack = [(tinit, tfinal)] + while(len(stack) > 0): + tif = stack.pop() + tmid = (tif[0] + tif[1])/float(2) + curLength = GetLengthHelper(fnList, 0, tmid, lengthCalcTol) + if(math.fabs((curLength/float(length)) -1) < lengthCompareTol): + return tmid + else: + #NOTE: try using math.decimal package to avoid floating point errors at really small lengths. + if (curLength > length): + stack.append((tif[0], tmid)) + #print("Length: " + str(curLength) + ", Point: " + str(stack[-1])) #debugging... + else: + stack.append((tmid, tif[1])) + #print("Length: " + str(curLength) + ", Point: " +str(stack[-1])) diff --git a/RankPanda/GUIDialogs.py b/RankPanda/GUIDialogs.py index 157daa7..9dc5b70 100755 --- a/RankPanda/GUIDialogs.py +++ b/RankPanda/GUIDialogs.py @@ -846,14 +846,12 @@ def __init__(self, parent): # begin layout code self.directionPanel = wx.BoxSizer(wx.HORIZONTAL) self.directionChoices = ['Clockwise', 'Counter-clockwise'] - #self.directionChoicesValues = [0, 1] #These aren't really needed I dont think self.directionRadioBox = wx.RadioBox(self, wx.ID_ANY, "Direction", choices = self.directionChoices, majorDimension = 2, style = wx.RA_SPECIFY_COLS) self.directionRadioBox.SetSelection(0) # clockwise selected by default self.directionPanel.Add(self.directionRadioBox, 1) self.pivotPanel = wx.BoxSizer(wx.HORIZONTAL) self.pivotChoices = ['At arrow ', 'At point'] - #self.pivotChoicesValues = [0, 1] self.pivotRadioBox = wx.RadioBox(self, wx.ID_ANY, "Non-Pivot Point", choices = self.pivotChoices, majorDimension = 2, style = wx.RA_SPECIFY_COLS) self.pivotRadioBox.SetSelection(0) # arrow selected by default self.pivotPanel.Add(self.pivotRadioBox, 1) @@ -909,8 +907,8 @@ def OnOK(self, event): d.ShowModal() d.Destroy() return - #self.output = (self.directionChoicesValues[self.directionRadioBox.GetSelection()], self.pivotChoicesValues[self.pivotRadioBox.GetSelection()] , length) - self.output = (self.directionRadioBox.GetSelection(), self.pivotRadioBox.GetSelection(), length) + self.output = (self.directionRadioBox.GetSelection(),\ + self.pivotRadioBox.GetSelection(), length) self.EndModal(0) def OnCancel(self, event): diff --git a/RankPanda/GUIField.py b/RankPanda/GUIField.py index 6fef87f..b68dbab 100644 --- a/RankPanda/GUIField.py +++ b/RankPanda/GUIField.py @@ -1,1272 +1,1272 @@ -#!/usr/bin/env python -#GUI Field: contains controls for the field, as well as rendering field thumbnails - -import wx -#import CoreWrapper -import RankLocation -import Point - -# Debug constants -DEBUG = False -DEBUG_RANK_COLOUR = wx.Colour(127, 63, 0) - -# print constants -PRINT_WIDTH = 880.0 -PRINT_HEIGHT = 425.0 - -# field constants -FIELD_LENGTH_STEPS = 176.0 -FIELD_WIDTH_STEPS = 85.0# + 1.0/3.0 -FIELD_RATIO = FIELD_LENGTH_STEPS/FIELD_WIDTH_STEPS - -RANK_COLOUR = wx.Colour(0, 0, 0) -RANK_DRAW_COLOUR = wx.Colour(0, 0, 255) -SELECTED_RANK_COLOUR = wx.Colour(255, 0, 0) -RANK_START_COLOUR = wx.Colour(0, 255, 0) -RANK_END_COLOUR = wx.Colour(255, 0, 255) -RANK_CALCULATED_COLOUR = wx.Colour(127, 127, 0) - -MINI_FIELD_SIZE = (176, 85) - -# field line constants -FIELD_HORIZONTAL_SPACING_STEPS = 4.0 -FIELD_VERTICAL_SPACING_STEPS = 4.0 -FIELD_FRONT_HASH = 32.0 -FIELD_BACK_HASH = FIELD_WIDTH_STEPS - 32.0 - -FIELD_LINE_YARDLINE_COLOUR = wx.Colour(63, 63, 63) -FIELD_LINE_MAJOR_COLOUR = wx.Colour(95, 95, 95) -FIELD_LINE_MINOR_COLOUR = wx.Colour(127, 127, 127) -FIELD_LINE_HASH_COLOUR = wx.Colour(63, 63, 63) - -FIELD_LINE_YARDLINE_WIDTH = 3.0 -FIELD_LINE_MAJOR_WIDTH = 2.0 -FIELD_LINE_MINOR_WIDTH = 1.0 -FIELD_LINE_HASH_WIDTH = 3.0 - -MINI_FIELD_LINE_YARDLINE_WIDTH = 1.0 -MINI_FIELD_LINE_MAJOR_WIDTH = 1.0 -MINI_FIELD_LINE_MINOR_WIDTH = 0.0 -MINI_FIELD_LINE_HASH_WIDTH = 1.0 - -# field number constants -FIELD_NUMBER_FRONT_FRONT = 11.0 -FIELD_NUMBER_FRONT_BACK = 14.0 -FIELD_NUMBER_BACK_FRONT = FIELD_WIDTH_STEPS - FIELD_NUMBER_FRONT_FRONT -FIELD_NUMBER_BACK_BACK = FIELD_WIDTH_STEPS - FIELD_NUMBER_FRONT_BACK - -FIELD_NUMBER_HEIGHT = FIELD_NUMBER_FRONT_BACK - FIELD_NUMBER_FRONT_FRONT -FIELD_NUMBER_SPACE = 0.05 - -FIELD_NUMBER_COLOUR = wx.Colour(63, 63, 63) - -# drawing constants -ENDPOINT_RADIUS = 6.0 -SPLINEPOINT_RADIUS = 4.0 -LINE_WIDTH = 4.0 -LINEPOINT_RADIUS = 2.0 -ARROW_SEPARATION = 6.0 -ARROW_LENGTH = 12.0 - -MINI_ENDPOINT_RADIUS = 3.0 -MINI_SPLINEPOINT_RADIUS = 2.0 -MINI_LINE_WIDTH = 2.0 -MINI_LINEPOINT_RADIUS = 1.0 -MINI_ARROW_SEPARATION = 3.0 -MINI_ARROW_LENGTH = 6.0 - -NAME_COLOUR = wx.Colour(0, 127, 127) -SELECTED_NAME_COLOUR = wx.Colour(255, 127, 0) -NAME_CALCULATED_COLOUR = wx.Colour(127, 127, 0) - -NAME_RECT_WIDTH = 10.0 -NAME_RECT_HEIGHT = 20.0 -NAME_DIST = 8.0 -MINI_NAME_DIST = 2.5 # this one is not used as names are not drawn in the minifields - -# mode constants -RANK_OFF = 0.0 -RANK_ADD = 1.0 -RANK_DRAG_POINT = 2.0 -RANK_DRAG_LINE = 3.0 -FTA_DRAG_WAYPOINT = 4.0 - -# DTP constants -DTP_START_COLOUR = wx.Colour(0, 255, 0) -DTP_END_COLOUR = wx.Colour(255, 0, 255) - -# FTA constants -FTA_START_COLOUR = wx.Colour(0, 255, 0) -FTA_END_COLOUR = wx.Colour(255, 0, 255) - -FTA_WAYPOINT_COLOUR = wx.Colour(0, 127, 127) - -FTA_WAYPOINT_RADIUS = 6.0 - -FTA_WAYPOINT_NUMBER_COLOUR = wx.Colour(0, 127, 127) - -FTA_NUMBER_RECT_WIDTH = 10.0 -FTA_NUMBER_RECT_HEIGHT = 20.0 -FTA_NUMBER_DIST = 15.0 - - -class Field(wx.Panel): - - def __init__(self, parent, id, style, main): - wx.Panel.__init__(self, parent, id, style = style) - self.main = main - self.fieldNumbersFont = wx.Font(72,wx.FONTFAMILY_MODERN,wx.FONTSTYLE_NORMAL,wx.FONTWEIGHT_BOLD, False, u'DejaVuSans') - self.rankNameFont = wx.Font(12, wx.FONTFAMILY_TELETYPE, wx.FONTSTYLE_NORMAL, wx.BOLD, False, u'DejaVuSansMono') - - # init field numbers - self.InitNumbers() - - # mode variables - self.rankMode = RANK_OFF - self.rankOldPos = (0, 0) - - self.firstPoint = (0, 0) - self.secondPoint = (0, 0) - - self.fieldRect = None - - # init stuff - self.OnResize(None) - -# DEBUG - if DEBUG: - self.DisplayRanksOn = False -# DEBUG - - def OnPaint(self, event): - self.Draw() - fielddc = wx.PaintDC(self) - fielddc.DrawBitmap(self.paintBitmap, 0, 0, False) # manual double-buffering since nothing else seems to work... - - def Draw(self): - self.paintBitmap = wx.EmptyBitmap(*self.GetSizeTuple()) - dc = wx.MemoryDC() - dc.SetFont(wx.Font(10,wx.FONTFAMILY_MODERN,wx.FONTSTYLE_NORMAL,wx.FONTWEIGHT_NORMAL, faceName="DejaVu Sans")) - dc.SelectObject(self.paintBitmap) - - dc.SetBrush(wx.Brush('black')) - dc.DrawRectangle(0, 0, *self.GetSizeTuple()) - - dc.SetClippingRect(self.fieldRect) - - dc.SetBrush(wx.Brush('white')) -# dc.DrawRectangleRect(self.fieldRect) - dc.Clear() - - self.DrawFieldNumbers(self.fieldRect, dc) - self.DrawFieldLines(self.fieldRect, dc) - - dc.DestroyClippingRegion() - - additional = self.main.core.GetAdditionalRanks() - self.additionalStart = [] - self.additionalEnd = [] - - for r in additional: - if r[3] == 'Begin': - self.additionalStart.append(r) - elif r[3] == 'End': - self.additionalEnd.append(r) - - self.DrawRanks(self.fieldRect, self.additionalStart, RANK_START_COLOUR, dc) - self.DrawRanks(self.fieldRect, self.additionalEnd, RANK_END_COLOUR, dc) - if self.main.animranks == None: -# not animating - self.DrawRanks(self.fieldRect, self.main.core.GetCalculatedRanks(), RANK_CALCULATED_COLOUR, dc) - self.DrawRanks(self.fieldRect, self.main.core.GetRanks(), RANK_COLOUR, dc) - self.DrawRanks(self.fieldRect, self.main.core.GetSelectedRanks(), SELECTED_RANK_COLOUR, dc) - - self.DrawRankNames(self.fieldRect, self.main.core.GetCalculatedRanks(), NAME_CALCULATED_COLOUR, dc) - self.DrawRankNames(self.fieldRect, self.main.core.GetRanks(), NAME_COLOUR, dc) - self.DrawRankNames(self.fieldRect, self.main.core.GetSelectedRanks(), SELECTED_NAME_COLOUR, dc) - - else: -# is animating - self.DrawRanks(self.fieldRect, self.main.animranks, RANK_COLOUR, dc) - self.DrawRankNames(self.fieldRect, self.main.animranks, NAME_COLOUR, dc) - -# draw initial point if user is adding a rank - - if self.rankMode == RANK_ADD: - dc.SetPen(wx.Pen(RANK_DRAW_COLOUR, 0)) - dc.SetBrush(wx.Brush(RANK_DRAW_COLOUR)) - x1, y1 = self.firstPoint - x2, y2 = self.secondPoint - dc.DrawCircle(x1, y1, ENDPOINT_RADIUS) - dc.DrawCircle(x2, y2, ENDPOINT_RADIUS) - dc.SetPen(wx.Pen(RANK_DRAW_COLOUR, LINE_WIDTH)) - dc.DrawLine(x1, y1, x2, y2) - -# DEBUG - if DEBUG: - if self.DisplayRanksOn: - for l in self.main.core.DisplayRanks(): - self.DrawLocation(self.fieldRect, l, dc, DEBUG_RANK_COLOUR) -# DEBUG - - def OnResize(self, event): - self.fieldRect = self.AspectRatioRect(self.GetRect()) - self.ScaleNumbers(self.fieldRect) - - self.Refresh(False) - - def OnLeftClick(self, event): - self.SetFocus() - if not self.InFieldRect(event.m_x, event.m_y): - return - - if event.ControlDown(): - self.rankMode = RANK_ADD -# self.firstPoint = (event.m_x, event.m_y) -# snap to grid - self.firstPoint = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) - self.secondPoint = self.firstPoint - else: - id = self.PickSpline(self.main.core.GetSelectedRanks(), event.m_x, event.m_y) - if id is not None: - self.rankMode = RANK_DRAG_POINT - self.draggedPoint = id -# self.rankPos = (event.m_x, event.m_y) -# snap to grid - self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) - self.main.core.PointGrabbed(*id) - else: - id = self.PickRank(self.main.core.GetSelectedRanks(), event.m_x, event.m_y) - if id is not None: - self.rankMode = RANK_DRAG_LINE -# self.rankPos = (event.m_x, event.m_y) -# snap to grid - self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) - self.main.core.RanksGrabbed() - else: - self.rankMode = RANK_OFF - id = self.PickSpline(self.main.core.GetRanks(), event.m_x, event.m_y) - if id is not None: - self.main.core.RankClicked(id[0], event.ShiftDown()) # deselect all ranks if shift isn't down - else: - id = self.PickRank(self.main.core.GetRanks(), event.m_x, event.m_y) - if id is not None: - self.main.core.RankClicked(id, event.ShiftDown()) # deselect all ranks if shift isn't down - elif not event.ShiftDown(): - self.main.core.FieldClicked() # deselect all ranks if shift isn't down - - self.main.RefreshRankList() - self.main.RefreshCommandList() - - # need to repaint field after a click - self.Refresh(False) - - def OnRightClick(self, event): - self.SetFocus() - self.OnLeftUnclick(event) # simulate left release to avoid weirdness such as deleting a spline point while it is being dragged - - if self.rankMode != RANK_OFF: - return - - spline = self.PickSpline(self.main.core.GetSelectedRanks(), event.m_x, event.m_y) - if spline is not None: - if event.ShiftDown(): - self.main.core.RankDeleteSpline(*spline) - else: - self.main.core.RankAddSpline(*spline) - - self.Refresh(False) - self.main.RefreshCurrentMove() - - def OnLeftUnclick(self, event): - if self.rankMode == RANK_ADD: - x, y = self.firstPoint - -# firstPt = Point.Point(self.RX(self.fieldRect, x), self.RY(self.fieldRect, y)) -# secondPt = Point.Point(self.RX(self.fieldRect, event.m_x), self.RY(self.fieldRect, event.m_y)) -# snap to grid - firstPt = Point.Point(round(self.RX(self.fieldRect, x)), round(self.RY(self.fieldRect, y))) - secondPt = Point.Point(round(self.RX(self.fieldRect, event.m_x)), round(self.RY(self.fieldRect, event.m_y))) - - self.main.core.RankDrawn([firstPt, secondPt]) - - elif self.rankMode == RANK_DRAG_POINT: - x, y = self.rankPos -# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) -# snap to grid - delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) - - self.main.core.PointDragged(self.draggedPoint[0], self.draggedPoint[1], delta[0], delta[1]) - self.main.core.PointDropped(*self.draggedPoint) - self.main.RefreshCommandList() - elif self.rankMode == RANK_DRAG_LINE: - x, y = self.rankPos -# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) -# snap to grid - delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) - - self.main.core.RanksDragged(*delta) - self.main.core.RanksDropped() - self.main.RefreshCommandList() - - self.rankMode = RANK_OFF - self.Refresh(False) - self.main.RefreshCurrentMove() - - def OnRightUnclick(self, event): - pass - - def OnMouseMove(self, event): - if not self.InFieldRect(event.m_x, event.m_y): - self.OnMouseExit(event) - else: - if self.rankMode == RANK_ADD: -# self.secondPoint = (event.m_x, event.m_y) -# snap to grid - self.secondPoint = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) - - elif self.rankMode == RANK_DRAG_POINT: - x, y = self.rankPos -# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) -# snap to grid - delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) -# self.rankPos = (event.m_x, event.m_y) -# snap to grid - self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) - - self.main.core.PointDragged(self.draggedPoint[0], self.draggedPoint[1], delta[0], delta[1]) - elif self.rankMode == RANK_DRAG_LINE: - x, y = self.rankPos -# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) -# snap to grid - delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) -# self.rankPos = (event.m_x, event.m_y) -# snap to grid - self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) - - self.main.core.RanksDragged(*delta) - - self.Refresh(False) -# don't refresh every frame... -# self.main.RefreshCurrentMove() - - def OnMouseExit(self, event): - if self.rankMode == RANK_DRAG_POINT: - self.main.core.PointDropped(*self.draggedPoint) - self.main.RefreshCommandList() - elif self.rankMode == RANK_DRAG_LINE: - self.main.core.RanksDropped() - self.main.RefreshCommandList() - - self.rankMode = RANK_OFF - self.Refresh(False) - self.main.RefreshCurrentMove() - - - def RenderSet(self, ranks): - bitmap = wx.EmptyBitmap(*MINI_FIELD_SIZE) - dc = wx.MemoryDC() - dc.SelectObject(bitmap) - - rect = self.AspectRatioRect(wx.Rect(0, 0, *MINI_FIELD_SIZE)) - - dc.SetBrush(wx.Brush('black')) - dc.DrawRectangle(0, 0, *MINI_FIELD_SIZE) - -# now, we make the background white - dc.SetClippingRect(rect) - - dc.SetBrush(wx.Brush('white')) - dc.DrawRectangleRect(rect) -# dc.Clear() # we use the old line so that we get a thin black border - - self.DrawFieldLines(rect, dc, True) - - dc.DestroyClippingRegion() - - self.DrawRanks(self.fieldRect, self.main.core.GetCalculatedRanks(), RANK_CALCULATED_COLOUR, dc, True) - self.DrawRanks(rect, ranks, RANK_COLOUR, dc, True) -# self.DrawRankNames(self.fieldRect, self.main.core.GetCalculatedRanks(), NAME_CALCULATED_COLOUR, dc, True) -# self.DrawRankNames(rect, ranks, NAME_COLOUR, dc, True) - - return bitmap - - def PrintBitmap(self): - bitmap = wx.EmptyBitmap(PRINT_WIDTH, PRINT_HEIGHT) - dc = wx.MemoryDC() - dc.SelectObject(bitmap) - - dc.SetBrush(wx.Brush('white')) - dc.Clear() - - tempRect = wx.Rect(0, 0, PRINT_WIDTH, PRINT_HEIGHT) - - self.ScaleNumbers(tempRect) - self.DrawFieldNumbers(tempRect, dc) - self.DrawFieldLines(tempRect, dc) - - dc.DestroyClippingRegion() - - self.DrawRanks(tempRect, self.main.core.GetRanks(), wx.Colour(0,0,0), dc) - self.DrawRankNames(tempRect, self.main.core.GetRanks(), wx.Colour(0,0,0), dc) - - self.OnResize(None) # re-scale the numbers back to normal - - return bitmap - - def DrawFieldLines(self, rect, dc, mini = False): - if mini: - field_line_yardline_width = MINI_FIELD_LINE_YARDLINE_WIDTH - field_line_hash_width = MINI_FIELD_LINE_HASH_WIDTH - field_line_major_width = MINI_FIELD_LINE_MAJOR_WIDTH - field_line_minor_width = MINI_FIELD_LINE_MINOR_WIDTH - else: - field_line_yardline_width = FIELD_LINE_YARDLINE_WIDTH - field_line_hash_width = FIELD_LINE_HASH_WIDTH - field_line_major_width = FIELD_LINE_MAJOR_WIDTH - field_line_minor_width = FIELD_LINE_MINOR_WIDTH - - horizontalStep = self.T(rect, FIELD_HORIZONTAL_SPACING_STEPS) - verticalStep = self.T(rect, FIELD_VERTICAL_SPACING_STEPS) - - horizontalStart = rect.GetX() + rect.GetWidth() / 2.0 - fieldBottom = rect.GetY() - fieldTop = rect.GetY() + rect.GetHeight() - - verticalStart = rect.GetY() + rect.GetHeight() # we start from the front, and should DECREMENT y-pos per line - fieldLeft = rect.GetX() - fieldRight = rect.GetX() + rect.GetWidth() - - isMajor = False - isAugmented = False - - for i in range(1, int((FIELD_LENGTH_STEPS / (2.0 * FIELD_HORIZONTAL_SPACING_STEPS)) + 1)): - if isMajor: - if isAugmented: - dc.SetPen(wx.Pen(FIELD_LINE_YARDLINE_COLOUR, field_line_yardline_width)) - isAugmented = False - else: - dc.SetPen(wx.Pen(FIELD_LINE_MAJOR_COLOUR, field_line_major_width)) - isAugmented = True - isMajor = False - else: - dc.SetPen(wx.Pen(FIELD_LINE_MINOR_COLOUR, field_line_minor_width)) - isMajor = True - - if dc.GetPen().GetWidth() > 0 : - dc.DrawLine(horizontalStart + i * horizontalStep, fieldBottom, horizontalStart + i * horizontalStep, fieldTop) - dc.DrawLine(horizontalStart - i * horizontalStep, fieldBottom, horizontalStart - i * horizontalStep, fieldTop) - - isMajor = True - - for i in range(0, int((FIELD_WIDTH_STEPS / FIELD_VERTICAL_SPACING_STEPS) + 1)): - if isMajor: - dc.SetPen(wx.Pen(FIELD_LINE_MAJOR_COLOUR, field_line_major_width)) - isMajor = False - else: - dc.SetPen(wx.Pen(FIELD_LINE_MINOR_COLOUR, field_line_minor_width)) - isMajor = True - - # Note: Need to SUBTRACT from vertical position to move toward the back of the field! - if dc.GetPen().GetWidth() > 0 : - dc.DrawLine(fieldLeft, verticalStart - i * verticalStep, fieldRight, verticalStart - i * verticalStep) - - dc.SetPen(wx.Pen(FIELD_LINE_YARDLINE_COLOUR, field_line_yardline_width)) - dc.DrawLine(horizontalStart, fieldBottom, horizontalStart, fieldTop) - - dc.SetPen(wx.Pen(FIELD_LINE_HASH_COLOUR, field_line_hash_width, wx.LONG_DASH)) - dc.DrawLine(fieldLeft, self.TY(rect, FIELD_FRONT_HASH), fieldRight, self.TY(rect, FIELD_FRONT_HASH)) - dc.DrawLine(fieldLeft, self.TY(rect, FIELD_BACK_HASH), fieldRight, self.TY(rect, FIELD_BACK_HASH)) - - def InitNumbers(self): - self.fixednumbers = [] - for i in range(10): - dc = wx.MemoryDC() - dc.SetFont(self.fieldNumbersFont) - textdim = dc.GetTextExtent(str(i)) - textdim = (textdim[0], textdim[1]) - bitmap = wx.EmptyBitmap(*textdim) - dc.SelectObject(bitmap) - dc.SetBrush(wx.Brush('white')) - dc.Clear() - dc.SetTextForeground(FIELD_NUMBER_COLOUR) - textrect = wx.Rect(0, 0, *textdim) - dc.DrawImageLabel(str(i), wx.NullBitmap, textrect, wx.ALIGN_CENTRE) - self.fixednumbers.append((bitmap.ConvertToImage(), textdim[0], textdim[1])) - - def ScaleNumbers(self, rect): - self.numbers = [] - self.numbersheight = self.T(rect, FIELD_NUMBER_HEIGHT) + 2 # + self.T(rect, 10.0) - for i in range(20): # we store 180-rotated images at index i + 10 - if i < 10: - w = self.fixednumbers[i][1] * float(self.numbersheight) / self.fixednumbers[i][2] - self.numbers.append((self.fixednumbers[i][0].Scale(w, self.numbersheight, wx.IMAGE_QUALITY_HIGH).ConvertToBitmap(), w)) - else: # store the 180-rotated one - w = self.fixednumbers[i - 10][1] * float(self.numbersheight) / self.fixednumbers[i - 10][2] - self.numbers.append((self.fixednumbers[i - 10][0].Scale(w, self.numbersheight, wx.IMAGE_QUALITY_HIGH).Rotate90().Rotate90().ConvertToBitmap(), w)) - - def DrawFieldNumbers(self, rect, dc, mini = False): - horizontalStep = 2 * self.T(rect, FIELD_HORIZONTAL_SPACING_STEPS) - - horizontalStart = rect.GetX() + rect.GetWidth() / 2.0 - fieldBottom = rect.GetY() - fieldTop = rect.GetY() + rect.GetHeight() - - fronty = self.TY(rect, FIELD_NUMBER_FRONT_FRONT) - self.numbersheight - backy = self.TY(rect, FIELD_NUMBER_BACK_BACK) - self.numbersheight - space = self.T(rect, FIELD_NUMBER_SPACE) + (self.numbers[5][1] / 2.0) - - dc.DrawBitmap(self.numbers[5][0], horizontalStart - self.numbers[5][1] - space, fronty, False) - dc.DrawBitmap(self.numbers[0][0], horizontalStart + space, fronty, False) - dc.DrawBitmap(self.numbers[10][0], horizontalStart - self.numbers[5][1] - space, backy, False) - dc.DrawBitmap(self.numbers[15][0], horizontalStart + space, backy, False) - - number1 = 4 - number0 = 5 - -# self.ScaleNumbers(rect) # since we only call this when drawing the field proper, we scale the images in OnResize to save computations - - for i in range(1, int((FIELD_LENGTH_STEPS / (4.0 * FIELD_HORIZONTAL_SPACING_STEPS)) + 1)): - dc.DrawBitmap(self.numbers[number1][0], horizontalStart + i * horizontalStep - self.numbers[number1][1] - space, fronty, False) - dc.DrawBitmap(self.numbers[number1][0], horizontalStart - i * horizontalStep - self.numbers[number1][1] - space, fronty, False) - dc.DrawBitmap(self.numbers[number0][0], horizontalStart + i * horizontalStep + space, fronty, False) - dc.DrawBitmap(self.numbers[number0][0], horizontalStart - i * horizontalStep + space, fronty, False) - dc.DrawBitmap(self.numbers[number0 + 10][0], horizontalStart + i * horizontalStep - self.numbers[number1][1] - space, backy, False) - dc.DrawBitmap(self.numbers[number0 + 10][0], horizontalStart - i * horizontalStep - self.numbers[number1][1] - space, backy, False) - dc.DrawBitmap(self.numbers[number1 + 10][0], horizontalStart + i * horizontalStep + space, backy, False) - dc.DrawBitmap(self.numbers[number1 + 10][0], horizontalStart - i * horizontalStep + space, backy, False) - - number0 -= 5 - if number0 < 0: - number0 += 10 - number1 -= 1 - if number1 < 0: - break - - def DrawRanks(self, rect, ranks, colour, dc, mini = False): - if ranks is not None: - for r in ranks: - self.DrawLocation(rect, r[2], dc, colour, mini) - - def DrawLocation(self, rect, loc, dc, colour, mini = False): - if mini: - endpoint_radius = MINI_ENDPOINT_RADIUS - splinepoint_radius = MINI_SPLINEPOINT_RADIUS - line_width = MINI_LINE_WIDTH - linepoint_radius = MINI_LINEPOINT_RADIUS - arrow_separation = MINI_ARROW_SEPARATION - arrow_length = MINI_ARROW_LENGTH - else: - endpoint_radius = ENDPOINT_RADIUS - splinepoint_radius = SPLINEPOINT_RADIUS - line_width = LINE_WIDTH - linepoint_radius = LINEPOINT_RADIUS - arrow_separation = ARROW_SEPARATION - arrow_length = ARROW_LENGTH - - if loc.IsStraight(): - pts = self.TList(rect, loc.GetListOfPoints()) - - dc.SetPen(wx.Pen(colour, 0)) - dc.SetBrush(wx.Brush(colour)) - dc.DrawCircle(pts[0].x, pts[0].y, endpoint_radius) - dc.DrawCircle(pts[1].x, pts[1].y, endpoint_radius) - - dc.SetPen(wx.Pen(colour, line_width)) - dc.DrawLine(pts[0].x, pts[0].y, pts[1].x, pts[1].y) - - dx = pts[1].x - pts[0].x - dy = pts[1].y - pts[0].y - dist = (dx ** 2 + dy ** 2) ** .5 - - newpt = (pts[0].x + dx * arrow_length / dist, pts[0].y + dy * arrow_length / dist) - - arr = (-dy * arrow_separation / dist, dx * arrow_separation / dist) - - dc.SetPen(wx.Pen(colour, line_width)) - dc.DrawLine(pts[0].x, pts[0].y, newpt[0] + arr[0], newpt[1] + arr[1]) - dc.DrawLine(pts[0].x, pts[0].y, newpt[0] - arr[0], newpt[1] - arr[1]) - elif loc.curved: - pts = self.TList(rect, loc.GetListOfPoints()) - plen = len(pts) - - dc.SetPen(wx.Pen(colour, 0)) - dc.SetBrush(wx.Brush(colour)) - dc.DrawCircle(pts[0].x, pts[0].y, endpoint_radius) - dc.DrawCircle(pts[plen - 1].x, pts[plen - 1].y, endpoint_radius) - - for i in range(1, plen - 1): - dc.DrawCircle(pts[i].x, pts[i].y, splinepoint_radius) - - pts = self.TList2(rect, loc.GetListOfDrawingPoints()) - for p in pts: - dc.DrawCircle(p.x, p.y, linepoint_radius) - - dx = pts[1].x - pts[0].x - dy = pts[1].y - pts[0].y - dist = (dx ** 2 + dy ** 2) ** .5 - - newpt = (pts[0].x + dx * arrow_length / dist, pts[0].y + dy * arrow_length / dist) - - arr = (-dy * arrow_separation / dist, dx * arrow_separation / dist) - - dc.SetPen(wx.Pen(colour, line_width)) - dc.DrawLine(pts[0].x, pts[0].y, newpt[0] + arr[0], newpt[1] + arr[1]) - dc.DrawLine(pts[0].x, pts[0].y, newpt[0] - arr[0], newpt[1] - arr[1]) - else: # connect-the-dots - pts = self.TList(rect, loc.GetListOfPoints()) - plen = len(pts) - - dc.SetPen(wx.Pen(colour, 0)) - dc.SetBrush(wx.Brush(colour)) - dc.DrawCircle(pts[0].x, pts[0].y, endpoint_radius) - dc.DrawCircle(pts[plen - 1].x, pts[plen - 1].y, endpoint_radius) - - for i in range(1, plen): - dc.SetPen(wx.Pen(colour, 0)) - dc.DrawCircle(pts[i].x, pts[i].y, splinepoint_radius) - dc.SetPen(wx.Pen(colour, line_width)) - dc.DrawLine(pts[i - 1].x, pts[i - 1].y, pts[i].x, pts[i].y) - - dx = pts[1].x - pts[0].x - dy = pts[1].y - pts[0].y - dist = (dx ** 2 + dy ** 2) ** .5 - - newpt = (pts[0].x + dx * arrow_length / dist, pts[0].y + dy * arrow_length / dist) - - arr = (-dy * arrow_separation / dist, dx * arrow_separation / dist) - - dc.SetPen(wx.Pen(colour, line_width)) - dc.DrawLine(pts[0].x, pts[0].y, newpt[0] + arr[0], newpt[1] + arr[1]) - dc.DrawLine(pts[0].x, pts[0].y, newpt[0] - arr[0], newpt[1] - arr[1]) - - def DrawRankNames(self, rect, ranks, colour, dc, mini = False): - dc.SetTextForeground(colour) - - if mini: - name_separation = MINI_NAME_DIST - else: - name_separation = NAME_DIST - - dc.SetFont(self.rankNameFont) - for r in ranks: - if r[-1]: - inner_name_separation = name_separation - else: - inner_name_separation = -name_separation - - if r[1] is not None: - pts = self.TList(rect, r[2].GetListOfPoints()) - if len(pts) % 2 == 1: - middlei = int((len(pts) - 1) / 2) - middle = pts[middlei] - middle1 = pts[middlei - 1] - middle2 = pts[middlei - 2] - - (dx, dy) = (middle1.x - middle2.x, middle1.y - middle2.y) - dist = (dx ** 2 + dy ** 2) ** .5 - - newpt = (middle.x - dy * inner_name_separation / dist, middle.y + dx * inner_name_separation / dist) - - textrect = wx.Rect(newpt[0] - NAME_RECT_WIDTH / 2, newpt[1] - NAME_RECT_HEIGHT / 2, NAME_RECT_WIDTH, NAME_RECT_HEIGHT) - else: - middle1 = pts[int((len(pts) - 1) / 2)] - middle2 = pts[int((len(pts) + 1) / 2)] - middle = (round((middle1.x + middle2.x) / 2.0), round((middle1.y + middle2.y) / 2.0)) - - (dx, dy) = (middle1.x - middle2.x, middle1.y - middle2.y) - dist = (dx ** 2 + dy ** 2) ** .5 - - newpt = (middle[0] - dy * inner_name_separation / dist, middle[1] + dx * inner_name_separation / dist) - - textrect = wx.Rect(newpt[0] - NAME_RECT_WIDTH / 2, newpt[1] - NAME_RECT_HEIGHT / 2, NAME_RECT_WIDTH, NAME_RECT_HEIGHT) - - dc.DrawImageLabel(r[1], wx.NullBitmap, textrect, wx.ALIGN_CENTRE) - - -# converts distance d in steps to pixels - def T(self, rect, d): - return d * rect.GetWidth() / FIELD_LENGTH_STEPS - -#def TranslateXStepsToPixel(x): # converts x in steps to pixels - def TX(self, rect, x): - return x * rect.GetWidth() / FIELD_LENGTH_STEPS + rect.GetX() -#def TranslateYStepsToPixel(y): # converts y in steps to pixels - def TY(self, rect, y): - return (FIELD_WIDTH_STEPS - y) * rect.GetHeight() / FIELD_WIDTH_STEPS + rect.GetY() - - def TList(self, rect, l): # converts from steps to counts for an entire list of points - l2 = [] - for i in l: - l2.append(Point.Point(self.TX(rect, i.x), self.TY(rect, i.y))) - return l2 - - def TList2(self, rect, l): # converts from steps to counts for an entire list of lists of points - l2 = [] - for i in l: - for j in i: - l2.append(Point.Point(self.TX(rect, j.x), self.TY(rect, j.y))) - return l2 - -# converts distance d in pixels to steps - def R(self, rect, d): - return d * FIELD_LENGTH_STEPS / rect.GetWidth() - -# converts x in pixels to steps - def RX(self, rect, x): - return (x - rect.GetX()) * FIELD_LENGTH_STEPS / rect.GetWidth() -# converts y in pixels to steps - def RY(self, rect, y): - return (rect.GetHeight() - y + rect.GetY()) * FIELD_WIDTH_STEPS / rect.GetHeight() - - -# returns rank id or None - def PickRank(self, ranks, mx, my): - x = self.RX(self.fieldRect, mx) - y = self.RY(self.fieldRect, my) - - for r in ranks: - loc = r[2] - - if loc.IsStraight(): - pts = loc.GetListOfPoints() - if self.PickLine(((pts[0].x, pts[0].y), (pts[1].x, pts[1].y)), (x, y), self.R(self.fieldRect, LINE_WIDTH)): - return r[0] - elif loc.curved: - pts = loc.GetListOfDrawingPoints() - for i in pts: - for j in i: - if self.PickCircle((j.x, j.y), (x, y), self.R(self.fieldRect, LINEPOINT_RADIUS)): - return r[0] - else: - pts = loc.GetListOfPoints() - for i in range(0, len(pts) - 1): - if self.PickLine(((pts[i].x, pts[i].y), (pts[i + 1].x, pts[i + 1].y)), (x, y), self.R(self.fieldRect, LINE_WIDTH)): - return r[0] - # failed to find a rank - return None - -# returns (rank id, spline pt index) or None - def PickSpline(self, ranks, mx, my): - x = self.RX(self.fieldRect, mx) - y = self.RY(self.fieldRect, my) - - for r in ranks: - loc = r[2] - pts = loc.GetListOfPoints() - - if loc.IsStraight(): - if self.PickCircle((pts[0].x, pts[0].y), (x, y), self.R(self.fieldRect, ENDPOINT_RADIUS)): - return (r[0], 0) - if self.PickCircle((pts[1].x, pts[1].y), (x, y), self.R(self.fieldRect, ENDPOINT_RADIUS)): - return (r[0], 1) - else: - plen = len(pts) - 1 - if self.PickCircle((pts[0].x, pts[0].y), (x, y), self.R(self.fieldRect, ENDPOINT_RADIUS)): - return (r[0], 0) - if self.PickCircle((pts[plen].x, pts[plen].y), (x, y), self.R(self.fieldRect, ENDPOINT_RADIUS)): - return (r[0], plen) - - for i in range(1, plen): - if self.PickCircle((pts[i].x, pts[i].y), (x, y), self.R(self.fieldRect, SPLINEPOINT_RADIUS)): - return (r[0], i) - # failed to find a pt - return None - - def PickLine(self, endpts, pt, d): - """ True if pt lies within distance d of the line defined by endpts """ - p1, p2 = endpts - x1, y1 = p1 - x2, y2 = p2 - x, y = pt - - if x1 == x2 and y1 == y2: - return False - - dist = ((x2 - x1)**2 + (y2 - y1)**2)**.5 - dx, dy = x2 - x1, y2 - y1 - dxt, dyt = x - x1, y - y1 - - proj = (dxt * dx + dyt * dy) / float(dist) - - # if projection extends beyond endpoint: - if proj > dist or proj < 0: - return False - else: - x1p, y1p = x1 + (dx * proj / float(dist)), y1 + (dy * proj / float(dist)) - return (x - x1p)**2 + (y - y1p)**2 <= d**2 - - def PickCircle(self, centre, pt, r): - """ True if pt lies within distance r of centre """ - xc, yc = centre - x, y = pt - return (x - xc)**2 + (y - yc)**2 <= r**2 - - def InFieldRect(self, x, y): - """ True if pt lies within the field rect """ - leftBound = self.fieldRect.GetX() - rightBound = leftBound + self.fieldRect.GetWidth() - lowerBound = self.fieldRect.GetY() - upperBound = lowerBound + self.fieldRect.GetHeight() - return x >= leftBound and x <= rightBound and y >= lowerBound and y <= upperBound - - def AspectRatioRect(self, rect): - newRect = wx.Rect(0, 0, rect.GetWidth(), rect.GetHeight()) - if float(newRect.GetWidth()) / newRect.GetHeight() < FIELD_RATIO: - newHeight = newRect.GetWidth() / FIELD_RATIO - diff = newRect.GetHeight() - newHeight - newRect.SetY(diff / 2.0) - newRect.SetHeight(newHeight) - else: - newWidth = newRect.GetHeight() * FIELD_RATIO - diff = newRect.GetWidth() - newWidth - newRect.SetX(diff / 2.0) - newRect.SetWidth(newWidth) - - return newRect - - -class DTPField(Field): - ''' Used for DTP Add dialog ''' - - def __init__(self, parent, id, style, main, DTPranks): - Field.__init__(self, parent, id, style, main) - - # init field numbers - self.InitNumbers() - - self.DTPranks = DTPranks - self.DTPstaticranks = [] - self.DTPeditranks = [] - - for r in self.DTPranks: - if r[3] == 'End': - self.DTPeditranks.append(r) - elif r[3] == 'Begin': - self.DTPstaticranks.append(r) - - self.point = 0 - - def Draw(self): - self.paintBitmap = wx.EmptyBitmap(*self.GetSizeTuple()) - dc = wx.MemoryDC() - dc.SetFont(wx.Font(10,wx.FONTFAMILY_MODERN,wx.FONTSTYLE_NORMAL,wx.FONTWEIGHT_NORMAL, faceName="DejaVu Sans")) - dc.SelectObject(self.paintBitmap) - - dc.SetBrush(wx.Brush('black')) - dc.DrawRectangle(0, 0, *self.GetSizeTuple()) - - dc.SetClippingRect(self.fieldRect) - - dc.SetBrush(wx.Brush('white')) -# dc.DrawRectangleRect(self.fieldRect) - dc.Clear() - - self.DrawFieldNumbers(self.fieldRect, dc) - self.DrawFieldLines(self.fieldRect, dc) - - dc.DestroyClippingRegion() - - self.DrawRanks(self.fieldRect, self.main.core.GetCalculatedRanks(), RANK_CALCULATED_COLOUR, dc) - self.DrawRanks(self.fieldRect, self.main.core.GetRanks(), RANK_COLOUR, dc) - self.DrawRanks(self.fieldRect, self.main.core.GetSelectedRanks(), SELECTED_RANK_COLOUR, dc) - - self.DrawRankNames(self.fieldRect, self.main.core.GetCalculatedRanks(), NAME_CALCULATED_COLOUR, dc) - self.DrawRankNames(self.fieldRect, self.main.core.GetRanks(), NAME_COLOUR, dc) - self.DrawRankNames(self.fieldRect, self.main.core.GetSelectedRanks(), SELECTED_NAME_COLOUR, dc) - - self.DrawRanks(self.fieldRect, self.DTPstaticranks, DTP_START_COLOUR, dc) - self.DrawRanks(self.fieldRect, self.DTPeditranks, DTP_END_COLOUR, dc) - - def OnLeftClick(self, event): - self.SetFocus() - if not self.InFieldRect(event.m_x, event.m_y): - return - - id = self.PickSpline(self.DTPeditranks, event.m_x, event.m_y) - if id is not None: - self.rankMode = RANK_DRAG_POINT - self.draggedPoint = id -# self.rankPos = (event.m_x, event.m_y) -# snap to grid - self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) - else: - id = self.PickRank(self.DTPeditranks, event.m_x, event.m_y) - if id is not None: - self.rankMode = RANK_DRAG_LINE -# self.rankPos = (event.m_x, event.m_y) -# snap to grid - self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) - else: - self.rankMode = RANK_OFF - - # need to repaint field after a click - self.Refresh(False) - - def OnRightClick(self, event): - self.SetFocus() - self.OnLeftUnclick(event) # simulate left release to avoid weirdness such as deleting a spline point while it is being dragged - - spline = self.PickSpline(self.DTPeditranks, event.m_x, event.m_y) - if spline is not None: - if event.ShiftDown(): - self.main.core.DTPDeletingSplinePoint(spline[1]) - else: - self.main.core.DTPAddingSplinePoint(spline[1]) - - self.Refresh(False) - - def OnLeftUnclick(self, event): - if self.rankMode == RANK_DRAG_POINT: - x, y = self.rankPos -# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) -# snap to grid - delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) - - self.main.core.AdjustDTPPoint(self.draggedPoint[1], delta[0], delta[1]) - elif self.rankMode == RANK_DRAG_LINE: - x, y = self.rankPos -# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) -# snap to grid - delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) - - self.main.core.AdjustDTPWhole(*delta) - - self.rankMode = RANK_OFF - self.Refresh(False) - - def OnRightUnclick(self, event): - pass - - def OnMouseMove(self, event): - if not self.InFieldRect(event.m_x, event.m_y): - self.OnMouseExit(event) - else: - if self.rankMode == RANK_DRAG_POINT: - x, y = self.rankPos -# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) -# snap to grid - delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) -# self.rankPos = (event.m_x, event.m_y) -# snap to grid - self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) - - self.main.core.AdjustDTPPoint(self.draggedPoint[1], delta[0], delta[1]) - elif self.rankMode == RANK_DRAG_LINE: - x, y = self.rankPos -# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) -# snap to grid - delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) -# self.rankPos = (event.m_x, event.m_y) -# snap to grid - self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) - - self.main.core.AdjustDTPWhole(*delta) - - self.Refresh(False) -# don't refresh every frame... -# self.main.RefreshCurrentMove() - - def OnMouseExit(self, event): - self.rankMode = RANK_OFF - self.Refresh(False) - - -class FTAField(Field): - ''' Used for FTA Add dialog ''' - - def __init__(self, parent, id, style, main, endpoint, FTAranks): # endpoint is True (1) or False (0) - Field.__init__(self, parent, id, style, main) - self.waypointFont = wx.Font(12, wx.FONTFAMILY_TELETYPE, wx.FONTSTYLE_NORMAL, wx.BOLD, False, u'DejaVuSansMono') - - # init field numbers - self.InitNumbers() - - self.endpoint = endpoint - self.FTAranks = FTAranks - self.FTAstaticranks = [] - self.FTAwaypoints = [] - self.FTAeditranks = [] - -# for r in self.FTAranks: -# if r[3] == 'End': -# self.FTAeditranks.append(r) -# elif r[3] == 'Begin': -# self.FTAstaticranks.append(r) - -# we assume that FTAs will only ever be added for one rank at a time - self.FTAeditranks.append(self.FTAranks[2]) - self.FTAwaypoints = self.FTAranks[1] - self.FTAstaticranks.append(self.FTAranks[0]) - - self.point = 0 - - def Draw(self): - self.paintBitmap = wx.EmptyBitmap(*self.GetSizeTuple()) - dc = wx.MemoryDC() - dc.SetFont(wx.Font(10,wx.FONTFAMILY_MODERN,wx.FONTSTYLE_NORMAL,wx.FONTWEIGHT_NORMAL, faceName="DejaVu Sans")) - dc.SelectObject(self.paintBitmap) - - dc.SetBrush(wx.Brush('black')) - dc.DrawRectangle(0, 0, *self.GetSizeTuple()) - - dc.SetClippingRect(self.fieldRect) - - dc.SetBrush(wx.Brush('white')) -# dc.DrawRectangleRect(self.fieldRect) - dc.Clear() - - self.DrawFieldNumbers(self.fieldRect, dc) - self.DrawFieldLines(self.fieldRect, dc) - - dc.DestroyClippingRegion() - - self.DrawRanks(self.fieldRect, self.main.core.GetCalculatedRanks(), RANK_CALCULATED_COLOUR, dc) - self.DrawRanks(self.fieldRect, self.main.core.GetRanks(), RANK_COLOUR, dc) - self.DrawRanks(self.fieldRect, self.main.core.GetSelectedRanks(), SELECTED_RANK_COLOUR, dc) - - self.DrawRankNames(self.fieldRect, self.main.core.GetCalculatedRanks(), NAME_CALCULATED_COLOUR, dc) - self.DrawRankNames(self.fieldRect, self.main.core.GetRanks(), NAME_COLOUR, dc) - self.DrawRankNames(self.fieldRect, self.main.core.GetSelectedRanks(), SELECTED_NAME_COLOUR, dc) - - self.DrawRanks(self.fieldRect, self.FTAstaticranks, FTA_START_COLOUR, dc) - self.DrawRanks(self.fieldRect, self.FTAeditranks, FTA_END_COLOUR, dc) - self.DrawWaypoints(self.fieldRect, self.FTAwaypoints, FTA_WAYPOINT_COLOUR, FTA_WAYPOINT_NUMBER_COLOUR, dc) - - def OnLeftClick(self, event): - self.SetFocus() - if not self.InFieldRect(event.m_x, event.m_y): - return - - if event.ControlDown(): -# self.firstPoint = (event.m_x, event.m_y) -# snap to grid - point = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) - if self.endpoint: - self.main.core.FTA1AddingWayPoint(self.RX(self.fieldRect, point[0]), self.RY(self.fieldRect, point[1])) - else: - self.main.core.FTA0AddingWayPoint(self.RX(self.fieldRect, point[0]), self.RY(self.fieldRect, point[1])) - else: - id = self.PickSpline(self.FTAeditranks, event.m_x, event.m_y) - if id is not None: - self.rankMode = RANK_DRAG_POINT - self.draggedPoint = id -# self.rankPos = (event.m_x, event.m_y) -# snap to grid - self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) - else: - id = self.PickRank(self.FTAeditranks, event.m_x, event.m_y) - if id is not None: - self.rankMode = RANK_DRAG_LINE -# self.rankPos = (event.m_x, event.m_y) -# snap to grid - self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) - else: - self.rankMode = RANK_OFF - - i = 0 - x = self.RX(self.fieldRect, event.m_x) - y = self.RY(self.fieldRect, event.m_y) - for w in self.FTAwaypoints: - if self.PickCircle((w.x, w.y), (x, y), self.R(self.fieldRect, FTA_WAYPOINT_RADIUS)): - self.rankMode = FTA_DRAG_WAYPOINT - self.draggedPoint = i -# self.rankPos = (event.m_x, event.m_y) -# snap to grid - self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) - break - i += 1 - - # need to repaint field after a click - self.Refresh(False) - - def OnRightClick(self, event): - self.SetFocus() - self.OnLeftUnclick(event) # simulate left release to avoid weirdness such as deleting a spline point while it is being dragged - - if event.ShiftDown(): - i = 0 - x = self.RX(self.fieldRect, event.m_x) - y = self.RY(self.fieldRect, event.m_y) - for w in self.FTAwaypoints: - if self.PickCircle((w.x, w.y), (x, y), self.R(self.fieldRect, FTA_WAYPOINT_RADIUS)): - if self.endpoint: - self.main.core.FTA1DeleteWayPoint(i) - return - else: - self.main.core.FTA0DeleteWayPoint(i) - return - i += 1 - - spline = self.PickSpline(self.FTAeditranks, event.m_x, event.m_y) - if spline is not None: - if event.ShiftDown(): - if self.endpoint: - self.main.core.FTA1DeletingSplinePoint(spline[1]) - else: - self.main.core.FTA0DeletingSplinePoint(spline[1]) - else: - if self.endpoint: - self.main.core.FTA1AddingSplinePoint(spline[1]) - else: - self.main.core.FTA0AddingSplinePoint(spline[1]) - - self.Refresh(False) - - def OnLeftUnclick(self, event): - if self.rankMode == RANK_DRAG_POINT: - x, y = self.rankPos -# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) -# snap to grid - delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) - - if self.endpoint: - self.main.core.AdjustFTA1EndLocationPoint(self.draggedPoint[1], delta[0], delta[1]) - else: - self.main.core.AdjustFTA0EndLocationPoint(self.draggedPoint[1], delta[0], delta[1]) - elif self.rankMode == RANK_DRAG_LINE: - x, y = self.rankPos -# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) -# snap to grid - delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) - - if self.endpoint: - self.main.core.AdjustFTA1Whole(*delta) - else: - self.main.core.AdjustFTA0Whole(*delta) - elif self.rankMode == FTA_DRAG_WAYPOINT: - x, y = self.rankPos -# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) -# snap to grid - delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) - - if self.endpoint: - self.main.core.FTA1AdjustWayPoint(self.draggedPoint, *delta) - else: - self.main.core.FTA0AdjustWayPoint(self.draggedPoint, *delta) - - self.rankMode = RANK_OFF - self.Refresh(False) - - def OnRightUnclick(self, event): - pass - - def OnMouseMove(self, event): - if not self.InFieldRect(event.m_x, event.m_y): - self.OnMouseExit(event) - else: - if self.rankMode == RANK_DRAG_POINT: - x, y = self.rankPos -# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) -# snap to grid - delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) -# self.rankPos = (event.m_x, event.m_y) -# snap to grid - self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) - - if self.endpoint: - self.main.core.AdjustFTA1EndLocationPoint(self.draggedPoint[1], delta[0], delta[1]) - else: - self.main.core.AdjustFTA0EndLocationPoint(self.draggedPoint[1], delta[0], delta[1]) - elif self.rankMode == RANK_DRAG_LINE: - x, y = self.rankPos -# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) -# snap to grid - delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) -# self.rankPos = (event.m_x, event.m_y) -# snap to grid - self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) - - if self.endpoint: - self.main.core.AdjustFTA1Whole(*delta) - else: - self.main.core.AdjustFTA0Whole(*delta) - elif self.rankMode == FTA_DRAG_WAYPOINT: - x, y = self.rankPos -# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) -# snap to grid - delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) -# self.rankPos = (event.m_x, event.m_y) -# snap to grid - self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) - - if self.endpoint: - self.main.core.FTA1AdjustWayPoint(self.draggedPoint, *delta) - else: - self.main.core.FTA0AdjustWayPoint(self.draggedPoint, *delta) - - self.Refresh(False) -# don't refresh every frame... -# self.main.RefreshCurrentMove() - - def OnMouseExit(self, event): - self.rankMode = RANK_OFF - self.Refresh(False) - - def DrawWaypoints(self, rect, waypoints, colour, numbercolour, dc): - dc.SetPen(wx.Pen(colour, 0)) - dc.SetBrush(wx.Brush(colour)) - dc.SetTextForeground(colour) - i = 0 - - dc.SetFont(self.waypointFont) - for w in waypoints: - (x, y) = (self.TX(self.fieldRect, w.x), self.TY(self.fieldRect, w.y)) - dc.DrawCircle(x, y, FTA_WAYPOINT_RADIUS) - textrect = wx.Rect(x - FTA_NUMBER_RECT_WIDTH / 2, y - FTA_NUMBER_DIST - FTA_NUMBER_RECT_HEIGHT / 2, FTA_NUMBER_RECT_WIDTH, FTA_NUMBER_RECT_HEIGHT) - dc.DrawImageLabel(str(i), wx.NullBitmap, textrect, wx.ALIGN_CENTRE) - i += 1 - - -class StatusField(Field): - ''' Used for Ranks at Count dialog ''' - - def __init__(self, parent, id, style, main, count): - Field.__init__(self, parent, id, style, main) - - # init field numbers - self.InitNumbers() - - self.count = count - - def Draw(self): - self.paintBitmap = wx.EmptyBitmap(*self.GetSizeTuple()) - dc = wx.MemoryDC() - dc.SetFont(wx.Font(10,wx.FONTFAMILY_MODERN,wx.FONTSTYLE_NORMAL,wx.FONTWEIGHT_NORMAL, faceName="DejaVu Sans")) - dc.SelectObject(self.paintBitmap) - - dc.SetBrush(wx.Brush('black')) - dc.DrawRectangle(0, 0, *self.GetSizeTuple()) - - dc.SetClippingRect(self.fieldRect) - - dc.SetBrush(wx.Brush('white')) -# dc.DrawRectangleRect(self.fieldRect) - dc.Clear() - - self.DrawFieldNumbers(self.fieldRect, dc) - self.DrawFieldLines(self.fieldRect, dc) - - dc.DestroyClippingRegion() - - self.DrawRanks(self.fieldRect, self.main.core.DisplayStatusAtCount(self.count), RANK_COLOUR, dc) - - self.DrawRankNames(self.fieldRect, self.main.core.DisplayStatusAtCount(self.count), NAME_COLOUR, dc) - - def OnLeftClick(self, event): - pass - - def OnRightClick(self, event): - pass - - def OnLeftUnclick(self, event): - pass - - def OnRightUnclick(self, event): - pass - - def OnMouseMove(self, event): - pass - - def OnMouseExit(self, event): - pass +#!/usr/bin/env python +#GUI Field: contains controls for the field, as well as rendering field thumbnails + +import wx +#import CoreWrapper +import RankLocation +import Point + +# Debug constants +DEBUG = False +DEBUG_RANK_COLOUR = wx.Colour(127, 63, 0) + +# print constants +PRINT_WIDTH = 880.0 +PRINT_HEIGHT = 425.0 + +# field constants +FIELD_LENGTH_STEPS = 176.0 +FIELD_WIDTH_STEPS = 85.0# + 1.0/3.0 +FIELD_RATIO = FIELD_LENGTH_STEPS/FIELD_WIDTH_STEPS + +RANK_COLOUR = wx.Colour(0, 0, 0) +RANK_DRAW_COLOUR = wx.Colour(0, 0, 255) +SELECTED_RANK_COLOUR = wx.Colour(255, 0, 0) +RANK_START_COLOUR = wx.Colour(0, 255, 0) +RANK_END_COLOUR = wx.Colour(255, 0, 255) +RANK_CALCULATED_COLOUR = wx.Colour(127, 127, 0) + +MINI_FIELD_SIZE = (176, 85) + +# field line constants +FIELD_HORIZONTAL_SPACING_STEPS = 4.0 +FIELD_VERTICAL_SPACING_STEPS = 4.0 +FIELD_FRONT_HASH = 32.0 +FIELD_BACK_HASH = FIELD_WIDTH_STEPS - 32.0 + +FIELD_LINE_YARDLINE_COLOUR = wx.Colour(63, 63, 63) +FIELD_LINE_MAJOR_COLOUR = wx.Colour(95, 95, 95) +FIELD_LINE_MINOR_COLOUR = wx.Colour(127, 127, 127) +FIELD_LINE_HASH_COLOUR = wx.Colour(63, 63, 63) + +FIELD_LINE_YARDLINE_WIDTH = 3.0 +FIELD_LINE_MAJOR_WIDTH = 2.0 +FIELD_LINE_MINOR_WIDTH = 1.0 +FIELD_LINE_HASH_WIDTH = 3.0 + +MINI_FIELD_LINE_YARDLINE_WIDTH = 1.0 +MINI_FIELD_LINE_MAJOR_WIDTH = 1.0 +MINI_FIELD_LINE_MINOR_WIDTH = 0.0 +MINI_FIELD_LINE_HASH_WIDTH = 1.0 + +# field number constants +FIELD_NUMBER_FRONT_FRONT = 11.0 +FIELD_NUMBER_FRONT_BACK = 14.0 +FIELD_NUMBER_BACK_FRONT = FIELD_WIDTH_STEPS - FIELD_NUMBER_FRONT_FRONT +FIELD_NUMBER_BACK_BACK = FIELD_WIDTH_STEPS - FIELD_NUMBER_FRONT_BACK + +FIELD_NUMBER_HEIGHT = FIELD_NUMBER_FRONT_BACK - FIELD_NUMBER_FRONT_FRONT +FIELD_NUMBER_SPACE = 0.05 + +FIELD_NUMBER_COLOUR = wx.Colour(63, 63, 63) + +# drawing constants +ENDPOINT_RADIUS = 6.0 +SPLINEPOINT_RADIUS = 4.0 +LINE_WIDTH = 4.0 +LINEPOINT_RADIUS = 2.0 +ARROW_SEPARATION = 6.0 +ARROW_LENGTH = 12.0 + +MINI_ENDPOINT_RADIUS = 3.0 +MINI_SPLINEPOINT_RADIUS = 2.0 +MINI_LINE_WIDTH = 2.0 +MINI_LINEPOINT_RADIUS = 1.0 +MINI_ARROW_SEPARATION = 3.0 +MINI_ARROW_LENGTH = 6.0 + +NAME_COLOUR = wx.Colour(0, 127, 127) +SELECTED_NAME_COLOUR = wx.Colour(255, 127, 0) +NAME_CALCULATED_COLOUR = wx.Colour(127, 127, 0) + +NAME_RECT_WIDTH = 10.0 +NAME_RECT_HEIGHT = 20.0 +NAME_DIST = 8.0 +MINI_NAME_DIST = 2.5 # this one is not used as names are not drawn in the minifields + +# mode constants +RANK_OFF = 0.0 +RANK_ADD = 1.0 +RANK_DRAG_POINT = 2.0 +RANK_DRAG_LINE = 3.0 +FTA_DRAG_WAYPOINT = 4.0 + +# DTP constants +DTP_START_COLOUR = wx.Colour(0, 255, 0) +DTP_END_COLOUR = wx.Colour(255, 0, 255) + +# FTA constants +FTA_START_COLOUR = wx.Colour(0, 255, 0) +FTA_END_COLOUR = wx.Colour(255, 0, 255) + +FTA_WAYPOINT_COLOUR = wx.Colour(0, 127, 127) + +FTA_WAYPOINT_RADIUS = 6.0 + +FTA_WAYPOINT_NUMBER_COLOUR = wx.Colour(0, 127, 127) + +FTA_NUMBER_RECT_WIDTH = 10.0 +FTA_NUMBER_RECT_HEIGHT = 20.0 +FTA_NUMBER_DIST = 15.0 + + +class Field(wx.Panel): + + def __init__(self, parent, id, style, main): + wx.Panel.__init__(self, parent, id, style = style) + self.main = main + self.fieldNumbersFont = wx.Font(72,wx.FONTFAMILY_MODERN,wx.FONTSTYLE_NORMAL,wx.FONTWEIGHT_BOLD, False, u'DejaVuSans') + self.rankNameFont = wx.Font(12, wx.FONTFAMILY_TELETYPE, wx.FONTSTYLE_NORMAL, wx.BOLD, False, u'DejaVuSansMono') + + # init field numbers + self.InitNumbers() + + # mode variables + self.rankMode = RANK_OFF + self.rankOldPos = (0, 0) + + self.firstPoint = (0, 0) + self.secondPoint = (0, 0) + + self.fieldRect = None + + # init stuff + self.OnResize(None) + +# DEBUG + if DEBUG: + self.DisplayRanksOn = False +# DEBUG + + def OnPaint(self, event): + self.Draw() + fielddc = wx.PaintDC(self) + fielddc.DrawBitmap(self.paintBitmap, 0, 0, False) # manual double-buffering since nothing else seems to work... + + def Draw(self): + self.paintBitmap = wx.EmptyBitmap(*self.GetSizeTuple()) + dc = wx.MemoryDC() + dc.SetFont(wx.Font(10,wx.FONTFAMILY_MODERN,wx.FONTSTYLE_NORMAL,wx.FONTWEIGHT_NORMAL, faceName="DejaVu Sans")) + dc.SelectObject(self.paintBitmap) + + dc.SetBrush(wx.Brush('black')) + dc.DrawRectangle(0, 0, *self.GetSizeTuple()) + + dc.SetClippingRect(self.fieldRect) + + dc.SetBrush(wx.Brush('white')) +# dc.DrawRectangleRect(self.fieldRect) + dc.Clear() + + self.DrawFieldNumbers(self.fieldRect, dc) + self.DrawFieldLines(self.fieldRect, dc) + + dc.DestroyClippingRegion() + + additional = self.main.core.GetAdditionalRanks() + self.additionalStart = [] + self.additionalEnd = [] + + for r in additional: + if r[3] == 'Begin': + self.additionalStart.append(r) + elif r[3] == 'End': + self.additionalEnd.append(r) + + self.DrawRanks(self.fieldRect, self.additionalStart, RANK_START_COLOUR, dc) + self.DrawRanks(self.fieldRect, self.additionalEnd, RANK_END_COLOUR, dc) + if self.main.animranks == None: +# not animating + self.DrawRanks(self.fieldRect, self.main.core.GetCalculatedRanks(), RANK_CALCULATED_COLOUR, dc) + self.DrawRanks(self.fieldRect, self.main.core.GetRanks(), RANK_COLOUR, dc) + self.DrawRanks(self.fieldRect, self.main.core.GetSelectedRanks(), SELECTED_RANK_COLOUR, dc) + + self.DrawRankNames(self.fieldRect, self.main.core.GetCalculatedRanks(), NAME_CALCULATED_COLOUR, dc) + self.DrawRankNames(self.fieldRect, self.main.core.GetRanks(), NAME_COLOUR, dc) + self.DrawRankNames(self.fieldRect, self.main.core.GetSelectedRanks(), SELECTED_NAME_COLOUR, dc) + + else: +# is animating + self.DrawRanks(self.fieldRect, self.main.animranks, RANK_COLOUR, dc) + self.DrawRankNames(self.fieldRect, self.main.animranks, NAME_COLOUR, dc) + +# draw initial point if user is adding a rank + + if self.rankMode == RANK_ADD: + dc.SetPen(wx.Pen(RANK_DRAW_COLOUR, 0)) + dc.SetBrush(wx.Brush(RANK_DRAW_COLOUR)) + x1, y1 = self.firstPoint + x2, y2 = self.secondPoint + dc.DrawCircle(x1, y1, ENDPOINT_RADIUS) + dc.DrawCircle(x2, y2, ENDPOINT_RADIUS) + dc.SetPen(wx.Pen(RANK_DRAW_COLOUR, LINE_WIDTH)) + dc.DrawLine(x1, y1, x2, y2) + +# DEBUG + if DEBUG: + if self.DisplayRanksOn: + for l in self.main.core.DisplayRanks(): + self.DrawLocation(self.fieldRect, l, dc, DEBUG_RANK_COLOUR) +# DEBUG + + def OnResize(self, event): + self.fieldRect = self.AspectRatioRect(self.GetRect()) + self.ScaleNumbers(self.fieldRect) + + self.Refresh(False) + + def OnLeftClick(self, event): + self.SetFocus() + if not self.InFieldRect(event.m_x, event.m_y): + return + + if event.ControlDown(): + self.rankMode = RANK_ADD +# self.firstPoint = (event.m_x, event.m_y) +# snap to grid + self.firstPoint = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) + self.secondPoint = self.firstPoint + else: + id = self.PickSpline(self.main.core.GetSelectedRanks(), event.m_x, event.m_y) + if id is not None: + self.rankMode = RANK_DRAG_POINT + self.draggedPoint = id +# self.rankPos = (event.m_x, event.m_y) +# snap to grid + self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) + self.main.core.PointGrabbed(*id) + else: + id = self.PickRank(self.main.core.GetSelectedRanks(), event.m_x, event.m_y) + if id is not None: + self.rankMode = RANK_DRAG_LINE +# self.rankPos = (event.m_x, event.m_y) +# snap to grid + self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) + self.main.core.RanksGrabbed() + else: + self.rankMode = RANK_OFF + id = self.PickSpline(self.main.core.GetRanks(), event.m_x, event.m_y) + if id is not None: + self.main.core.RankClicked(id[0], event.ShiftDown()) # deselect all ranks if shift isn't down + else: + id = self.PickRank(self.main.core.GetRanks(), event.m_x, event.m_y) + if id is not None: + self.main.core.RankClicked(id, event.ShiftDown()) # deselect all ranks if shift isn't down + elif not event.ShiftDown(): + self.main.core.FieldClicked() # deselect all ranks if shift isn't down + + self.main.RefreshRankList() + self.main.RefreshCommandList() + + # need to repaint field after a click + self.Refresh(False) + + def OnRightClick(self, event): + self.SetFocus() + self.OnLeftUnclick(event) # simulate left release to avoid weirdness such as deleting a spline point while it is being dragged + + if self.rankMode != RANK_OFF: + return + + spline = self.PickSpline(self.main.core.GetSelectedRanks(), event.m_x, event.m_y) + if spline is not None: + if event.ShiftDown(): + self.main.core.RankDeleteSpline(*spline) + else: + self.main.core.RankAddSpline(*spline) + + self.Refresh(False) + self.main.RefreshCurrentMove() + + def OnLeftUnclick(self, event): + if self.rankMode == RANK_ADD: + x, y = self.firstPoint + +# firstPt = Point.Point(self.RX(self.fieldRect, x), self.RY(self.fieldRect, y)) +# secondPt = Point.Point(self.RX(self.fieldRect, event.m_x), self.RY(self.fieldRect, event.m_y)) +# snap to grid + firstPt = Point.Point(round(self.RX(self.fieldRect, x)), round(self.RY(self.fieldRect, y))) + secondPt = Point.Point(round(self.RX(self.fieldRect, event.m_x)), round(self.RY(self.fieldRect, event.m_y))) + + self.main.core.RankDrawn([firstPt, secondPt]) + + elif self.rankMode == RANK_DRAG_POINT: + x, y = self.rankPos +# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) +# snap to grid + delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) + + self.main.core.PointDragged(self.draggedPoint[0], self.draggedPoint[1], delta[0], delta[1]) + self.main.core.PointDropped(*self.draggedPoint) + self.main.RefreshCommandList() + elif self.rankMode == RANK_DRAG_LINE: + x, y = self.rankPos +# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) +# snap to grid + delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) + + self.main.core.RanksDragged(*delta) + self.main.core.RanksDropped() + self.main.RefreshCommandList() + + self.rankMode = RANK_OFF + self.Refresh(False) + self.main.RefreshCurrentMove() + + def OnRightUnclick(self, event): + pass + + def OnMouseMove(self, event): + if not self.InFieldRect(event.m_x, event.m_y): + self.OnMouseExit(event) + else: + if self.rankMode == RANK_ADD: +# self.secondPoint = (event.m_x, event.m_y) +# snap to grid + self.secondPoint = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) + + elif self.rankMode == RANK_DRAG_POINT: + x, y = self.rankPos +# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) +# snap to grid + delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) +# self.rankPos = (event.m_x, event.m_y) +# snap to grid + self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) + + self.main.core.PointDragged(self.draggedPoint[0], self.draggedPoint[1], delta[0], delta[1]) + elif self.rankMode == RANK_DRAG_LINE: + x, y = self.rankPos +# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) +# snap to grid + delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) +# self.rankPos = (event.m_x, event.m_y) +# snap to grid + self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) + + self.main.core.RanksDragged(*delta) + + self.Refresh(False) +# don't refresh every frame... +# self.main.RefreshCurrentMove() + + def OnMouseExit(self, event): + if self.rankMode == RANK_DRAG_POINT: + self.main.core.PointDropped(*self.draggedPoint) + self.main.RefreshCommandList() + elif self.rankMode == RANK_DRAG_LINE: + self.main.core.RanksDropped() + self.main.RefreshCommandList() + + self.rankMode = RANK_OFF + self.Refresh(False) + self.main.RefreshCurrentMove() + + + def RenderSet(self, ranks): + bitmap = wx.EmptyBitmap(*MINI_FIELD_SIZE) + dc = wx.MemoryDC() + dc.SelectObject(bitmap) + + rect = self.AspectRatioRect(wx.Rect(0, 0, *MINI_FIELD_SIZE)) + + dc.SetBrush(wx.Brush('black')) + dc.DrawRectangle(0, 0, *MINI_FIELD_SIZE) + +# now, we make the background white + dc.SetClippingRect(rect) + + dc.SetBrush(wx.Brush('white')) + dc.DrawRectangleRect(rect) +# dc.Clear() # we use the old line so that we get a thin black border + + self.DrawFieldLines(rect, dc, True) + + dc.DestroyClippingRegion() + + self.DrawRanks(self.fieldRect, self.main.core.GetCalculatedRanks(), RANK_CALCULATED_COLOUR, dc, True) + self.DrawRanks(rect, ranks, RANK_COLOUR, dc, True) +# self.DrawRankNames(self.fieldRect, self.main.core.GetCalculatedRanks(), NAME_CALCULATED_COLOUR, dc, True) +# self.DrawRankNames(rect, ranks, NAME_COLOUR, dc, True) + + return bitmap + + def PrintBitmap(self): + bitmap = wx.EmptyBitmap(PRINT_WIDTH, PRINT_HEIGHT) + dc = wx.MemoryDC() + dc.SelectObject(bitmap) + + dc.SetBrush(wx.Brush('white')) + dc.Clear() + + tempRect = wx.Rect(0, 0, PRINT_WIDTH, PRINT_HEIGHT) + + self.ScaleNumbers(tempRect) + self.DrawFieldNumbers(tempRect, dc) + self.DrawFieldLines(tempRect, dc) + + dc.DestroyClippingRegion() + + self.DrawRanks(tempRect, self.main.core.GetRanks(), wx.Colour(0,0,0), dc) + self.DrawRankNames(tempRect, self.main.core.GetRanks(), wx.Colour(0,0,0), dc) + + self.OnResize(None) # re-scale the numbers back to normal + + return bitmap + + def DrawFieldLines(self, rect, dc, mini = False): + if mini: + field_line_yardline_width = MINI_FIELD_LINE_YARDLINE_WIDTH + field_line_hash_width = MINI_FIELD_LINE_HASH_WIDTH + field_line_major_width = MINI_FIELD_LINE_MAJOR_WIDTH + field_line_minor_width = MINI_FIELD_LINE_MINOR_WIDTH + else: + field_line_yardline_width = FIELD_LINE_YARDLINE_WIDTH + field_line_hash_width = FIELD_LINE_HASH_WIDTH + field_line_major_width = FIELD_LINE_MAJOR_WIDTH + field_line_minor_width = FIELD_LINE_MINOR_WIDTH + + horizontalStep = self.T(rect, FIELD_HORIZONTAL_SPACING_STEPS) + verticalStep = self.T(rect, FIELD_VERTICAL_SPACING_STEPS) + + horizontalStart = rect.GetX() + rect.GetWidth() / 2.0 + fieldBottom = rect.GetY() + fieldTop = rect.GetY() + rect.GetHeight() + + verticalStart = rect.GetY() + rect.GetHeight() # we start from the front, and should DECREMENT y-pos per line + fieldLeft = rect.GetX() + fieldRight = rect.GetX() + rect.GetWidth() + + isMajor = False + isAugmented = False + + for i in range(1, int((FIELD_LENGTH_STEPS / (2.0 * FIELD_HORIZONTAL_SPACING_STEPS)) + 1)): + if isMajor: + if isAugmented: + dc.SetPen(wx.Pen(FIELD_LINE_YARDLINE_COLOUR, field_line_yardline_width)) + isAugmented = False + else: + dc.SetPen(wx.Pen(FIELD_LINE_MAJOR_COLOUR, field_line_major_width)) + isAugmented = True + isMajor = False + else: + dc.SetPen(wx.Pen(FIELD_LINE_MINOR_COLOUR, field_line_minor_width)) + isMajor = True + + if dc.GetPen().GetWidth() > 0 : + dc.DrawLine(horizontalStart + i * horizontalStep, fieldBottom, horizontalStart + i * horizontalStep, fieldTop) + dc.DrawLine(horizontalStart - i * horizontalStep, fieldBottom, horizontalStart - i * horizontalStep, fieldTop) + + isMajor = True + + for i in range(0, int((FIELD_WIDTH_STEPS / FIELD_VERTICAL_SPACING_STEPS) + 1)): + if isMajor: + dc.SetPen(wx.Pen(FIELD_LINE_MAJOR_COLOUR, field_line_major_width)) + isMajor = False + else: + dc.SetPen(wx.Pen(FIELD_LINE_MINOR_COLOUR, field_line_minor_width)) + isMajor = True + + # Note: Need to SUBTRACT from vertical position to move toward the back of the field! + if dc.GetPen().GetWidth() > 0 : + dc.DrawLine(fieldLeft, verticalStart - i * verticalStep, fieldRight, verticalStart - i * verticalStep) + + dc.SetPen(wx.Pen(FIELD_LINE_YARDLINE_COLOUR, field_line_yardline_width)) + dc.DrawLine(horizontalStart, fieldBottom, horizontalStart, fieldTop) + + dc.SetPen(wx.Pen(FIELD_LINE_HASH_COLOUR, field_line_hash_width, wx.LONG_DASH)) + dc.DrawLine(fieldLeft, self.TY(rect, FIELD_FRONT_HASH), fieldRight, self.TY(rect, FIELD_FRONT_HASH)) + dc.DrawLine(fieldLeft, self.TY(rect, FIELD_BACK_HASH), fieldRight, self.TY(rect, FIELD_BACK_HASH)) + + def InitNumbers(self): + self.fixednumbers = [] + for i in range(10): + dc = wx.MemoryDC() + dc.SetFont(self.fieldNumbersFont) + textdim = dc.GetTextExtent(str(i)) + textdim = (textdim[0], textdim[1]) + bitmap = wx.EmptyBitmap(*textdim) + dc.SelectObject(bitmap) + dc.SetBrush(wx.Brush('white')) + dc.Clear() + dc.SetTextForeground(FIELD_NUMBER_COLOUR) + textrect = wx.Rect(0, 0, *textdim) + dc.DrawImageLabel(str(i), wx.NullBitmap, textrect, wx.ALIGN_CENTRE) + self.fixednumbers.append((bitmap.ConvertToImage(), textdim[0], textdim[1])) + + def ScaleNumbers(self, rect): + self.numbers = [] + self.numbersheight = self.T(rect, FIELD_NUMBER_HEIGHT) + 2 # + self.T(rect, 10.0) + for i in range(20): # we store 180-rotated images at index i + 10 + if i < 10: + w = self.fixednumbers[i][1] * float(self.numbersheight) / self.fixednumbers[i][2] + self.numbers.append((self.fixednumbers[i][0].Scale(w, self.numbersheight, wx.IMAGE_QUALITY_HIGH).ConvertToBitmap(), w)) + else: # store the 180-rotated one + w = self.fixednumbers[i - 10][1] * float(self.numbersheight) / self.fixednumbers[i - 10][2] + self.numbers.append((self.fixednumbers[i - 10][0].Scale(w, self.numbersheight, wx.IMAGE_QUALITY_HIGH).Rotate90().Rotate90().ConvertToBitmap(), w)) + + def DrawFieldNumbers(self, rect, dc, mini = False): + horizontalStep = 2 * self.T(rect, FIELD_HORIZONTAL_SPACING_STEPS) + + horizontalStart = rect.GetX() + rect.GetWidth() / 2.0 + fieldBottom = rect.GetY() + fieldTop = rect.GetY() + rect.GetHeight() + + fronty = self.TY(rect, FIELD_NUMBER_FRONT_FRONT) - self.numbersheight + backy = self.TY(rect, FIELD_NUMBER_BACK_BACK) - self.numbersheight + space = self.T(rect, FIELD_NUMBER_SPACE) + (self.numbers[5][1] / 2.0) + + dc.DrawBitmap(self.numbers[5][0], horizontalStart - self.numbers[5][1] - space, fronty, False) + dc.DrawBitmap(self.numbers[0][0], horizontalStart + space, fronty, False) + dc.DrawBitmap(self.numbers[10][0], horizontalStart - self.numbers[5][1] - space, backy, False) + dc.DrawBitmap(self.numbers[15][0], horizontalStart + space, backy, False) + + number1 = 4 + number0 = 5 + +# self.ScaleNumbers(rect) # since we only call this when drawing the field proper, we scale the images in OnResize to save computations + + for i in range(1, int((FIELD_LENGTH_STEPS / (4.0 * FIELD_HORIZONTAL_SPACING_STEPS)) + 1)): + dc.DrawBitmap(self.numbers[number1][0], horizontalStart + i * horizontalStep - self.numbers[number1][1] - space, fronty, False) + dc.DrawBitmap(self.numbers[number1][0], horizontalStart - i * horizontalStep - self.numbers[number1][1] - space, fronty, False) + dc.DrawBitmap(self.numbers[number0][0], horizontalStart + i * horizontalStep + space, fronty, False) + dc.DrawBitmap(self.numbers[number0][0], horizontalStart - i * horizontalStep + space, fronty, False) + dc.DrawBitmap(self.numbers[number0 + 10][0], horizontalStart + i * horizontalStep - self.numbers[number1][1] - space, backy, False) + dc.DrawBitmap(self.numbers[number0 + 10][0], horizontalStart - i * horizontalStep - self.numbers[number1][1] - space, backy, False) + dc.DrawBitmap(self.numbers[number1 + 10][0], horizontalStart + i * horizontalStep + space, backy, False) + dc.DrawBitmap(self.numbers[number1 + 10][0], horizontalStart - i * horizontalStep + space, backy, False) + + number0 -= 5 + if number0 < 0: + number0 += 10 + number1 -= 1 + if number1 < 0: + break + + def DrawRanks(self, rect, ranks, colour, dc, mini = False): + if ranks is not None: + for r in ranks: + self.DrawLocation(rect, r[2], dc, colour, mini) + + def DrawLocation(self, rect, loc, dc, colour, mini = False): + if mini: + endpoint_radius = MINI_ENDPOINT_RADIUS + splinepoint_radius = MINI_SPLINEPOINT_RADIUS + line_width = MINI_LINE_WIDTH + linepoint_radius = MINI_LINEPOINT_RADIUS + arrow_separation = MINI_ARROW_SEPARATION + arrow_length = MINI_ARROW_LENGTH + else: + endpoint_radius = ENDPOINT_RADIUS + splinepoint_radius = SPLINEPOINT_RADIUS + line_width = LINE_WIDTH + linepoint_radius = LINEPOINT_RADIUS + arrow_separation = ARROW_SEPARATION + arrow_length = ARROW_LENGTH + + if loc.IsStraight(): + pts = self.TList(rect, loc.GetListOfPoints()) + + dc.SetPen(wx.Pen(colour, 0)) + dc.SetBrush(wx.Brush(colour)) + dc.DrawCircle(pts[0].x, pts[0].y, endpoint_radius) + dc.DrawCircle(pts[1].x, pts[1].y, endpoint_radius) + + dc.SetPen(wx.Pen(colour, line_width)) + dc.DrawLine(pts[0].x, pts[0].y, pts[1].x, pts[1].y) + + dx = pts[1].x - pts[0].x + dy = pts[1].y - pts[0].y + dist = (dx ** 2 + dy ** 2) ** .5 + + newpt = (pts[0].x + dx * arrow_length / dist, pts[0].y + dy * arrow_length / dist) + + arr = (-dy * arrow_separation / dist, dx * arrow_separation / dist) + + dc.SetPen(wx.Pen(colour, line_width)) + dc.DrawLine(pts[0].x, pts[0].y, newpt[0] + arr[0], newpt[1] + arr[1]) + dc.DrawLine(pts[0].x, pts[0].y, newpt[0] - arr[0], newpt[1] - arr[1]) + elif loc.curved: + pts = self.TList(rect, loc.GetListOfPoints()) + plen = len(pts) + + dc.SetPen(wx.Pen(colour, 0)) + dc.SetBrush(wx.Brush(colour)) + dc.DrawCircle(pts[0].x, pts[0].y, endpoint_radius) + dc.DrawCircle(pts[plen - 1].x, pts[plen - 1].y, endpoint_radius) + + for i in range(1, plen - 1): + dc.DrawCircle(pts[i].x, pts[i].y, splinepoint_radius) + + pts = self.TList2(rect, loc.GetListOfDrawingPoints()) + for p in pts: + dc.DrawCircle(p.x, p.y, linepoint_radius) + + dx = pts[1].x - pts[0].x + dy = pts[1].y - pts[0].y + dist = (dx ** 2 + dy ** 2) ** .5 + + newpt = (pts[0].x + dx * arrow_length / dist, pts[0].y + dy * arrow_length / dist) + + arr = (-dy * arrow_separation / dist, dx * arrow_separation / dist) + + dc.SetPen(wx.Pen(colour, line_width)) + dc.DrawLine(pts[0].x, pts[0].y, newpt[0] + arr[0], newpt[1] + arr[1]) + dc.DrawLine(pts[0].x, pts[0].y, newpt[0] - arr[0], newpt[1] - arr[1]) + else: # connect-the-dots + pts = self.TList(rect, loc.GetListOfPoints()) + plen = len(pts) + + dc.SetPen(wx.Pen(colour, 0)) + dc.SetBrush(wx.Brush(colour)) + dc.DrawCircle(pts[0].x, pts[0].y, endpoint_radius) + dc.DrawCircle(pts[plen - 1].x, pts[plen - 1].y, endpoint_radius) + + for i in range(1, plen): + dc.SetPen(wx.Pen(colour, 0)) + dc.DrawCircle(pts[i].x, pts[i].y, splinepoint_radius) + dc.SetPen(wx.Pen(colour, line_width)) + dc.DrawLine(pts[i - 1].x, pts[i - 1].y, pts[i].x, pts[i].y) + + dx = pts[1].x - pts[0].x + dy = pts[1].y - pts[0].y + dist = (dx ** 2 + dy ** 2) ** .5 + + newpt = (pts[0].x + dx * arrow_length / dist, pts[0].y + dy * arrow_length / dist) + + arr = (-dy * arrow_separation / dist, dx * arrow_separation / dist) + + dc.SetPen(wx.Pen(colour, line_width)) + dc.DrawLine(pts[0].x, pts[0].y, newpt[0] + arr[0], newpt[1] + arr[1]) + dc.DrawLine(pts[0].x, pts[0].y, newpt[0] - arr[0], newpt[1] - arr[1]) + + def DrawRankNames(self, rect, ranks, colour, dc, mini = False): + dc.SetTextForeground(colour) + + if mini: + name_separation = MINI_NAME_DIST + else: + name_separation = NAME_DIST + + dc.SetFont(self.rankNameFont) + for r in ranks: + if r[-1]: + inner_name_separation = name_separation + else: + inner_name_separation = -name_separation + + if r[1] is not None: + pts = self.TList(rect, r[2].GetListOfPoints()) + if len(pts) % 2 == 1: + middlei = int((len(pts) - 1) / 2) + middle = pts[middlei] + middle1 = pts[middlei - 1] + middle2 = pts[middlei - 2] + + (dx, dy) = (middle1.x - middle2.x, middle1.y - middle2.y) + dist = (dx ** 2 + dy ** 2) ** .5 + + newpt = (middle.x - dy * inner_name_separation / dist, middle.y + dx * inner_name_separation / dist) + + textrect = wx.Rect(newpt[0] - NAME_RECT_WIDTH / 2, newpt[1] - NAME_RECT_HEIGHT / 2, NAME_RECT_WIDTH, NAME_RECT_HEIGHT) + else: + middle1 = pts[int((len(pts) - 1) / 2)] + middle2 = pts[int((len(pts) + 1) / 2)] + middle = (round((middle1.x + middle2.x) / 2.0), round((middle1.y + middle2.y) / 2.0)) + + (dx, dy) = (middle1.x - middle2.x, middle1.y - middle2.y) + dist = (dx ** 2 + dy ** 2) ** .5 + + newpt = (middle[0] - dy * inner_name_separation / dist, middle[1] + dx * inner_name_separation / dist) + + textrect = wx.Rect(newpt[0] - NAME_RECT_WIDTH / 2, newpt[1] - NAME_RECT_HEIGHT / 2, NAME_RECT_WIDTH, NAME_RECT_HEIGHT) + + dc.DrawImageLabel(r[1], wx.NullBitmap, textrect, wx.ALIGN_CENTRE) + + +# converts distance d in steps to pixels + def T(self, rect, d): + return d * rect.GetWidth() / FIELD_LENGTH_STEPS + +#def TranslateXStepsToPixel(x): # converts x in steps to pixels + def TX(self, rect, x): + return x * rect.GetWidth() / FIELD_LENGTH_STEPS + rect.GetX() +#def TranslateYStepsToPixel(y): # converts y in steps to pixels + def TY(self, rect, y): + return (FIELD_WIDTH_STEPS - y) * rect.GetHeight() / FIELD_WIDTH_STEPS + rect.GetY() + + def TList(self, rect, l): # converts from steps to counts for an entire list of points + l2 = [] + for i in l: + l2.append(Point.Point(self.TX(rect, i.x), self.TY(rect, i.y))) + return l2 + + def TList2(self, rect, l): # converts from steps to counts for an entire list of lists of points + l2 = [] + for i in l: + for j in i: + l2.append(Point.Point(self.TX(rect, j.x), self.TY(rect, j.y))) + return l2 + +# converts distance d in pixels to steps + def R(self, rect, d): + return d * FIELD_LENGTH_STEPS / rect.GetWidth() + +# converts x in pixels to steps + def RX(self, rect, x): + return (x - rect.GetX()) * FIELD_LENGTH_STEPS / rect.GetWidth() +# converts y in pixels to steps + def RY(self, rect, y): + return (rect.GetHeight() - y + rect.GetY()) * FIELD_WIDTH_STEPS / rect.GetHeight() + + +# returns rank id or None + def PickRank(self, ranks, mx, my): + x = self.RX(self.fieldRect, mx) + y = self.RY(self.fieldRect, my) + + for r in ranks: + loc = r[2] + + if loc.IsStraight(): + pts = loc.GetListOfPoints() + if self.PickLine(((pts[0].x, pts[0].y), (pts[1].x, pts[1].y)), (x, y), self.R(self.fieldRect, LINE_WIDTH)): + return r[0] + elif loc.curved: + pts = loc.GetListOfDrawingPoints() + for i in pts: + for j in i: + if self.PickCircle((j.x, j.y), (x, y), self.R(self.fieldRect, LINEPOINT_RADIUS)): + return r[0] + else: + pts = loc.GetListOfPoints() + for i in range(0, len(pts) - 1): + if self.PickLine(((pts[i].x, pts[i].y), (pts[i + 1].x, pts[i + 1].y)), (x, y), self.R(self.fieldRect, LINE_WIDTH)): + return r[0] + # failed to find a rank + return None + +# returns (rank id, spline pt index) or None + def PickSpline(self, ranks, mx, my): + x = self.RX(self.fieldRect, mx) + y = self.RY(self.fieldRect, my) + + for r in ranks: + loc = r[2] + pts = loc.GetListOfPoints() + + if loc.IsStraight(): + if self.PickCircle((pts[0].x, pts[0].y), (x, y), self.R(self.fieldRect, ENDPOINT_RADIUS)): + return (r[0], 0) + if self.PickCircle((pts[1].x, pts[1].y), (x, y), self.R(self.fieldRect, ENDPOINT_RADIUS)): + return (r[0], 1) + else: + plen = len(pts) - 1 + if self.PickCircle((pts[0].x, pts[0].y), (x, y), self.R(self.fieldRect, ENDPOINT_RADIUS)): + return (r[0], 0) + if self.PickCircle((pts[plen].x, pts[plen].y), (x, y), self.R(self.fieldRect, ENDPOINT_RADIUS)): + return (r[0], plen) + + for i in range(1, plen): + if self.PickCircle((pts[i].x, pts[i].y), (x, y), self.R(self.fieldRect, SPLINEPOINT_RADIUS)): + return (r[0], i) + # failed to find a pt + return None + + def PickLine(self, endpts, pt, d): + """ True if pt lies within distance d of the line defined by endpts """ + p1, p2 = endpts + x1, y1 = p1 + x2, y2 = p2 + x, y = pt + + if x1 == x2 and y1 == y2: + return False + + dist = ((x2 - x1)**2 + (y2 - y1)**2)**.5 + dx, dy = x2 - x1, y2 - y1 + dxt, dyt = x - x1, y - y1 + + proj = (dxt * dx + dyt * dy) / float(dist) + + # if projection extends beyond endpoint: + if proj > dist or proj < 0: + return False + else: + x1p, y1p = x1 + (dx * proj / float(dist)), y1 + (dy * proj / float(dist)) + return (x - x1p)**2 + (y - y1p)**2 <= d**2 + + def PickCircle(self, centre, pt, r): + """ True if pt lies within distance r of centre """ + xc, yc = centre + x, y = pt + return (x - xc)**2 + (y - yc)**2 <= r**2 + + def InFieldRect(self, x, y): + """ True if pt lies within the field rect """ + leftBound = self.fieldRect.GetX() + rightBound = leftBound + self.fieldRect.GetWidth() + lowerBound = self.fieldRect.GetY() + upperBound = lowerBound + self.fieldRect.GetHeight() + return x >= leftBound and x <= rightBound and y >= lowerBound and y <= upperBound + + def AspectRatioRect(self, rect): + newRect = wx.Rect(0, 0, rect.GetWidth(), rect.GetHeight()) + if float(newRect.GetWidth()) / newRect.GetHeight() < FIELD_RATIO: + newHeight = newRect.GetWidth() / FIELD_RATIO + diff = newRect.GetHeight() - newHeight + newRect.SetY(diff / 2.0) + newRect.SetHeight(newHeight) + else: + newWidth = newRect.GetHeight() * FIELD_RATIO + diff = newRect.GetWidth() - newWidth + newRect.SetX(diff / 2.0) + newRect.SetWidth(newWidth) + + return newRect + + +class DTPField(Field): + ''' Used for DTP Add dialog ''' + + def __init__(self, parent, id, style, main, DTPranks): + Field.__init__(self, parent, id, style, main) + + # init field numbers + self.InitNumbers() + + self.DTPranks = DTPranks + self.DTPstaticranks = [] + self.DTPeditranks = [] + + for r in self.DTPranks: + if r[3] == 'End': + self.DTPeditranks.append(r) + elif r[3] == 'Begin': + self.DTPstaticranks.append(r) + + self.point = 0 + + def Draw(self): + self.paintBitmap = wx.EmptyBitmap(*self.GetSizeTuple()) + dc = wx.MemoryDC() + dc.SetFont(wx.Font(10,wx.FONTFAMILY_MODERN,wx.FONTSTYLE_NORMAL,wx.FONTWEIGHT_NORMAL, faceName="DejaVu Sans")) + dc.SelectObject(self.paintBitmap) + + dc.SetBrush(wx.Brush('black')) + dc.DrawRectangle(0, 0, *self.GetSizeTuple()) + + dc.SetClippingRect(self.fieldRect) + + dc.SetBrush(wx.Brush('white')) +# dc.DrawRectangleRect(self.fieldRect) + dc.Clear() + + self.DrawFieldNumbers(self.fieldRect, dc) + self.DrawFieldLines(self.fieldRect, dc) + + dc.DestroyClippingRegion() + + self.DrawRanks(self.fieldRect, self.main.core.GetCalculatedRanks(), RANK_CALCULATED_COLOUR, dc) + self.DrawRanks(self.fieldRect, self.main.core.GetRanks(), RANK_COLOUR, dc) + self.DrawRanks(self.fieldRect, self.main.core.GetSelectedRanks(), SELECTED_RANK_COLOUR, dc) + + self.DrawRankNames(self.fieldRect, self.main.core.GetCalculatedRanks(), NAME_CALCULATED_COLOUR, dc) + self.DrawRankNames(self.fieldRect, self.main.core.GetRanks(), NAME_COLOUR, dc) + self.DrawRankNames(self.fieldRect, self.main.core.GetSelectedRanks(), SELECTED_NAME_COLOUR, dc) + + self.DrawRanks(self.fieldRect, self.DTPstaticranks, DTP_START_COLOUR, dc) + self.DrawRanks(self.fieldRect, self.DTPeditranks, DTP_END_COLOUR, dc) + + def OnLeftClick(self, event): + self.SetFocus() + if not self.InFieldRect(event.m_x, event.m_y): + return + + id = self.PickSpline(self.DTPeditranks, event.m_x, event.m_y) + if id is not None: + self.rankMode = RANK_DRAG_POINT + self.draggedPoint = id +# self.rankPos = (event.m_x, event.m_y) +# snap to grid + self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) + else: + id = self.PickRank(self.DTPeditranks, event.m_x, event.m_y) + if id is not None: + self.rankMode = RANK_DRAG_LINE +# self.rankPos = (event.m_x, event.m_y) +# snap to grid + self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) + else: + self.rankMode = RANK_OFF + + # need to repaint field after a click + self.Refresh(False) + + def OnRightClick(self, event): + self.SetFocus() + self.OnLeftUnclick(event) # simulate left release to avoid weirdness such as deleting a spline point while it is being dragged + + spline = self.PickSpline(self.DTPeditranks, event.m_x, event.m_y) + if spline is not None: + if event.ShiftDown(): + self.main.core.DTPDeletingSplinePoint(spline[1]) + else: + self.main.core.DTPAddingSplinePoint(spline[1]) + + self.Refresh(False) + + def OnLeftUnclick(self, event): + if self.rankMode == RANK_DRAG_POINT: + x, y = self.rankPos +# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) +# snap to grid + delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) + + self.main.core.AdjustDTPPoint(self.draggedPoint[1], delta[0], delta[1]) + elif self.rankMode == RANK_DRAG_LINE: + x, y = self.rankPos +# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) +# snap to grid + delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) + + self.main.core.AdjustDTPWhole(*delta) + + self.rankMode = RANK_OFF + self.Refresh(False) + + def OnRightUnclick(self, event): + pass + + def OnMouseMove(self, event): + if not self.InFieldRect(event.m_x, event.m_y): + self.OnMouseExit(event) + else: + if self.rankMode == RANK_DRAG_POINT: + x, y = self.rankPos +# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) +# snap to grid + delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) +# self.rankPos = (event.m_x, event.m_y) +# snap to grid + self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) + + self.main.core.AdjustDTPPoint(self.draggedPoint[1], delta[0], delta[1]) + elif self.rankMode == RANK_DRAG_LINE: + x, y = self.rankPos +# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) +# snap to grid + delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) +# self.rankPos = (event.m_x, event.m_y) +# snap to grid + self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) + + self.main.core.AdjustDTPWhole(*delta) + + self.Refresh(False) +# don't refresh every frame... +# self.main.RefreshCurrentMove() + + def OnMouseExit(self, event): + self.rankMode = RANK_OFF + self.Refresh(False) + + +class FTAField(Field): + ''' Used for FTA Add dialog ''' + + def __init__(self, parent, id, style, main, endpoint, FTAranks): # endpoint is True (1) or False (0) + Field.__init__(self, parent, id, style, main) + self.waypointFont = wx.Font(12, wx.FONTFAMILY_TELETYPE, wx.FONTSTYLE_NORMAL, wx.BOLD, False, u'DejaVuSansMono') + + # init field numbers + self.InitNumbers() + + self.endpoint = endpoint + self.FTAranks = FTAranks + self.FTAstaticranks = [] + self.FTAwaypoints = [] + self.FTAeditranks = [] + +# for r in self.FTAranks: +# if r[3] == 'End': +# self.FTAeditranks.append(r) +# elif r[3] == 'Begin': +# self.FTAstaticranks.append(r) + +# we assume that FTAs will only ever be added for one rank at a time + self.FTAeditranks.append(self.FTAranks[2]) + self.FTAwaypoints = self.FTAranks[1] + self.FTAstaticranks.append(self.FTAranks[0]) + + self.point = 0 + + def Draw(self): + self.paintBitmap = wx.EmptyBitmap(*self.GetSizeTuple()) + dc = wx.MemoryDC() + dc.SetFont(wx.Font(10,wx.FONTFAMILY_MODERN,wx.FONTSTYLE_NORMAL,wx.FONTWEIGHT_NORMAL, faceName="DejaVu Sans")) + dc.SelectObject(self.paintBitmap) + + dc.SetBrush(wx.Brush('black')) + dc.DrawRectangle(0, 0, *self.GetSizeTuple()) + + dc.SetClippingRect(self.fieldRect) + + dc.SetBrush(wx.Brush('white')) +# dc.DrawRectangleRect(self.fieldRect) + dc.Clear() + + self.DrawFieldNumbers(self.fieldRect, dc) + self.DrawFieldLines(self.fieldRect, dc) + + dc.DestroyClippingRegion() + + self.DrawRanks(self.fieldRect, self.main.core.GetCalculatedRanks(), RANK_CALCULATED_COLOUR, dc) + self.DrawRanks(self.fieldRect, self.main.core.GetRanks(), RANK_COLOUR, dc) + self.DrawRanks(self.fieldRect, self.main.core.GetSelectedRanks(), SELECTED_RANK_COLOUR, dc) + + self.DrawRankNames(self.fieldRect, self.main.core.GetCalculatedRanks(), NAME_CALCULATED_COLOUR, dc) + self.DrawRankNames(self.fieldRect, self.main.core.GetRanks(), NAME_COLOUR, dc) + self.DrawRankNames(self.fieldRect, self.main.core.GetSelectedRanks(), SELECTED_NAME_COLOUR, dc) + + self.DrawRanks(self.fieldRect, self.FTAstaticranks, FTA_START_COLOUR, dc) + self.DrawRanks(self.fieldRect, self.FTAeditranks, FTA_END_COLOUR, dc) + self.DrawWaypoints(self.fieldRect, self.FTAwaypoints, FTA_WAYPOINT_COLOUR, FTA_WAYPOINT_NUMBER_COLOUR, dc) + + def OnLeftClick(self, event): + self.SetFocus() + if not self.InFieldRect(event.m_x, event.m_y): + return + + if event.ControlDown(): +# self.firstPoint = (event.m_x, event.m_y) +# snap to grid + point = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) + if self.endpoint: + self.main.core.FTA1AddingWayPoint(self.RX(self.fieldRect, point[0]), self.RY(self.fieldRect, point[1])) + else: + self.main.core.FTA0AddingWayPoint(self.RX(self.fieldRect, point[0]), self.RY(self.fieldRect, point[1])) + else: + id = self.PickSpline(self.FTAeditranks, event.m_x, event.m_y) + if id is not None: + self.rankMode = RANK_DRAG_POINT + self.draggedPoint = id +# self.rankPos = (event.m_x, event.m_y) +# snap to grid + self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) + else: + id = self.PickRank(self.FTAeditranks, event.m_x, event.m_y) + if id is not None: + self.rankMode = RANK_DRAG_LINE +# self.rankPos = (event.m_x, event.m_y) +# snap to grid + self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) + else: + self.rankMode = RANK_OFF + + i = 0 + x = self.RX(self.fieldRect, event.m_x) + y = self.RY(self.fieldRect, event.m_y) + for w in self.FTAwaypoints: + if self.PickCircle((w.x, w.y), (x, y), self.R(self.fieldRect, FTA_WAYPOINT_RADIUS)): + self.rankMode = FTA_DRAG_WAYPOINT + self.draggedPoint = i +# self.rankPos = (event.m_x, event.m_y) +# snap to grid + self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) + break + i += 1 + + # need to repaint field after a click + self.Refresh(False) + + def OnRightClick(self, event): + self.SetFocus() + self.OnLeftUnclick(event) # simulate left release to avoid weirdness such as deleting a spline point while it is being dragged + + if event.ShiftDown(): + i = 0 + x = self.RX(self.fieldRect, event.m_x) + y = self.RY(self.fieldRect, event.m_y) + for w in self.FTAwaypoints: + if self.PickCircle((w.x, w.y), (x, y), self.R(self.fieldRect, FTA_WAYPOINT_RADIUS)): + if self.endpoint: + self.main.core.FTA1DeleteWayPoint(i) + return + else: + self.main.core.FTA0DeleteWayPoint(i) + return + i += 1 + + spline = self.PickSpline(self.FTAeditranks, event.m_x, event.m_y) + if spline is not None: + if event.ShiftDown(): + if self.endpoint: + self.main.core.FTA1DeletingSplinePoint(spline[1]) + else: + self.main.core.FTA0DeletingSplinePoint(spline[1]) + else: + if self.endpoint: + self.main.core.FTA1AddingSplinePoint(spline[1]) + else: + self.main.core.FTA0AddingSplinePoint(spline[1]) + + self.Refresh(False) + + def OnLeftUnclick(self, event): + if self.rankMode == RANK_DRAG_POINT: + x, y = self.rankPos +# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) +# snap to grid + delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) + + if self.endpoint: + self.main.core.AdjustFTA1EndLocationPoint(self.draggedPoint[1], delta[0], delta[1]) + else: + self.main.core.AdjustFTA0EndLocationPoint(self.draggedPoint[1], delta[0], delta[1]) + elif self.rankMode == RANK_DRAG_LINE: + x, y = self.rankPos +# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) +# snap to grid + delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) + + if self.endpoint: + self.main.core.AdjustFTA1Whole(*delta) + else: + self.main.core.AdjustFTA0Whole(*delta) + elif self.rankMode == FTA_DRAG_WAYPOINT: + x, y = self.rankPos +# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) +# snap to grid + delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) + + if self.endpoint: + self.main.core.FTA1AdjustWayPoint(self.draggedPoint, *delta) + else: + self.main.core.FTA0AdjustWayPoint(self.draggedPoint, *delta) + + self.rankMode = RANK_OFF + self.Refresh(False) + + def OnRightUnclick(self, event): + pass + + def OnMouseMove(self, event): + if not self.InFieldRect(event.m_x, event.m_y): + self.OnMouseExit(event) + else: + if self.rankMode == RANK_DRAG_POINT: + x, y = self.rankPos +# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) +# snap to grid + delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) +# self.rankPos = (event.m_x, event.m_y) +# snap to grid + self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) + + if self.endpoint: + self.main.core.AdjustFTA1EndLocationPoint(self.draggedPoint[1], delta[0], delta[1]) + else: + self.main.core.AdjustFTA0EndLocationPoint(self.draggedPoint[1], delta[0], delta[1]) + elif self.rankMode == RANK_DRAG_LINE: + x, y = self.rankPos +# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) +# snap to grid + delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) +# self.rankPos = (event.m_x, event.m_y) +# snap to grid + self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) + + if self.endpoint: + self.main.core.AdjustFTA1Whole(*delta) + else: + self.main.core.AdjustFTA0Whole(*delta) + elif self.rankMode == FTA_DRAG_WAYPOINT: + x, y = self.rankPos +# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) +# snap to grid + delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) +# self.rankPos = (event.m_x, event.m_y) +# snap to grid + self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) + + if self.endpoint: + self.main.core.FTA1AdjustWayPoint(self.draggedPoint, *delta) + else: + self.main.core.FTA0AdjustWayPoint(self.draggedPoint, *delta) + + self.Refresh(False) +# don't refresh every frame... +# self.main.RefreshCurrentMove() + + def OnMouseExit(self, event): + self.rankMode = RANK_OFF + self.Refresh(False) + + def DrawWaypoints(self, rect, waypoints, colour, numbercolour, dc): + dc.SetPen(wx.Pen(colour, 0)) + dc.SetBrush(wx.Brush(colour)) + dc.SetTextForeground(colour) + i = 0 + + dc.SetFont(self.waypointFont) + for w in waypoints: + (x, y) = (self.TX(self.fieldRect, w.x), self.TY(self.fieldRect, w.y)) + dc.DrawCircle(x, y, FTA_WAYPOINT_RADIUS) + textrect = wx.Rect(x - FTA_NUMBER_RECT_WIDTH / 2, y - FTA_NUMBER_DIST - FTA_NUMBER_RECT_HEIGHT / 2, FTA_NUMBER_RECT_WIDTH, FTA_NUMBER_RECT_HEIGHT) + dc.DrawImageLabel(str(i), wx.NullBitmap, textrect, wx.ALIGN_CENTRE) + i += 1 + + +class StatusField(Field): + ''' Used for Ranks at Count dialog ''' + + def __init__(self, parent, id, style, main, count): + Field.__init__(self, parent, id, style, main) + + # init field numbers + self.InitNumbers() + + self.count = count + + def Draw(self): + self.paintBitmap = wx.EmptyBitmap(*self.GetSizeTuple()) + dc = wx.MemoryDC() + dc.SetFont(wx.Font(10,wx.FONTFAMILY_MODERN,wx.FONTSTYLE_NORMAL,wx.FONTWEIGHT_NORMAL, faceName="DejaVu Sans")) + dc.SelectObject(self.paintBitmap) + + dc.SetBrush(wx.Brush('black')) + dc.DrawRectangle(0, 0, *self.GetSizeTuple()) + + dc.SetClippingRect(self.fieldRect) + + dc.SetBrush(wx.Brush('white')) +# dc.DrawRectangleRect(self.fieldRect) + dc.Clear() + + self.DrawFieldNumbers(self.fieldRect, dc) + self.DrawFieldLines(self.fieldRect, dc) + + dc.DestroyClippingRegion() + + self.DrawRanks(self.fieldRect, self.main.core.DisplayStatusAtCount(self.count), RANK_COLOUR, dc) + + self.DrawRankNames(self.fieldRect, self.main.core.DisplayStatusAtCount(self.count), NAME_COLOUR, dc) + + def OnLeftClick(self, event): + pass + + def OnRightClick(self, event): + pass + + def OnLeftUnclick(self, event): + pass + + def OnRightUnclick(self, event): + pass + + def OnMouseMove(self, event): + pass + + def OnMouseExit(self, event): + pass diff --git a/RankPanda/GUIMain.py b/RankPanda/GUIMain.py index 05fea2a..d89c08a 100755 --- a/RankPanda/GUIMain.py +++ b/RankPanda/GUIMain.py @@ -1,1461 +1,1452 @@ -#!/usr/bin/env python -# GUI Main: RankPanda main window - -import os -import wx -import GUIDialogs -import GUIField -import GUIRankList -import GUITimeBar -import CoreWrapper -#import RankLocation -#import Point -import Printer - -DEBUG = False -# Idea: instead of displaying * for locked rank, how about use different colour? - -default_size = (800,600) - -ID_EXIT = 0 -ID_NEW = 1 -ID_OPEN = 2 -ID_CLOSE = 3 -ID_SAVE = 4 -ID_SAVE_AS = 5 -ID_HELP = 6 -ID_ABOUT = 7 -ID_UNDO = 8 -ID_REDO = 9 -ID_CUT = 10 -ID_COPY = 11 -ID_PASTE = 12 -ID_FIND = 13 -ID_FIND_REPLACE = 14 -ID_EXPORT_PDF = 15 -ID_SETTINGS = 16 -ID_FIELD = 17 -ID_COMMAND_LIST = 18 -ID_COMMAND_BUTTON_MOVE_UP = 19 -ID_COMMAND_BUTTON_MOVE_DOWN = 20 -ID_COMMAND_BUTTON_SPLIT = 21 -ID_COMMAND_BUTTON_MERGE = 22 -ID_COMMAND_BUTTON_DELETE = 23 -ID_MOVE_LIST = 24 -ID_MOVE_NEW = 25 -ID_MOVE_IMPORT = 26 -ID_MOVE_SPLIT = 27 -ID_MOVE_MERGE = 28 -ID_MOVE_SHIFT = 29 -ID_ANIM_TIMER = 30 -ID_EDIT = 31 -ID_EXPORT_IND = 32 -ID_MOVE_TEXT = 33 -ID_MOVE_TEXT_OVERWRITE = 34 - -class MainWindow(wx.Frame): - """ Main Window; derived from wx.Frame """ - - def __init__(self, parent): - wx.Frame.__init__(self, parent, wx.ID_ANY, "Rank Panda", size = default_size) - - self.SetFont(wx.Font(10,wx.FONTFAMILY_MODERN,wx.FONTSTYLE_NORMAL,wx.FONTWEIGHT_NORMAL, faceName="DejaVu Sans")) - - -# DEBUG - if DEBUG: - self.core = CoreWrapper.CoreWrapper("Debug", 128, [(1, 4)], [(1, 1)]) - self.filename = None - else: - d = GUIDialogs.ProgramOptionsDialog(self) - while True: - start = d.ShowModal() - - if start == 0: # user requested new - self.filename = None - f = GUIDialogs.SongCreationDialog(self) - if f.ShowModal() == 0: - try: - self.core = CoreWrapper.CoreWrapper(*f.output) - break - except NameError as exception: - e = wx.MessageDialog(self, "Exception thrown while creating song!\nRank Panda will now close", "Song Creation Exception", wx.OK) - e.ShowModal() - e.Destroy() - self.Close(True) # exit since we couldn't create a song - return - else: - pass - f.Destroy() - elif start == 1: # user requested open - loop = True - loopOuter = True - self.core = CoreWrapper.CoreWrapper("Dummy", 10, [(1, 4)], [(1, 1)]) - - while loop: - dirname = '' - e = wx.FileDialog(self, "Open File", dirname, "", "*.panda", wx.OPEN) - if e.ShowModal() == wx.ID_OK: - self.filename = os.path.join(e.GetDirectory(), e.GetFilename()) - if self.core.Load(self.filename) == -1: # load failed - f = wx.MessageDialog(self, "Unable to open file!", "File Access Exception", wx.OK) - f.ShowModal() - f.Destroy() - else: - loop = False - loopOuter = False - else: - loop = False - e.Destroy() - if not loopOuter: - break -# if start == -1: # user requested exit - else: - self.Close(True) - return - - d.Destroy() -# DEBUG - -# hopefully this will fix our flickering problems... -#self.SetDoubleBuffered(True) - -#self.CreateStatusBar(2) -#self.SetStatusText("Try these: Exit, Open, Help, About", 1) - - - # begin menubar code - filemenu = wx.Menu() - filemenu.Append(ID_OPEN, "&Open...", "Open something") - filemenu.Append(ID_NEW, "&New...", "Create a new whatsit") -# TODO(astory): find out if the following line is intentionally this way. - filemenu.Append(ID_EDIT, "E&dit...", "Edit current whatsit") - filemenu.Append(ID_CLOSE, "&Close", "Close current whatsit") - filemenu.AppendSeparator() - filemenu.Append(ID_SAVE, "&Save", "Save current whatsit") - filemenu.Append(ID_SAVE_AS, "Save &As...", "Save current whatsit as something else") - filemenu.AppendSeparator() - filemenu.Append(ID_EXPORT_PDF, "&Export to PDF...", "Export current whatsit to PDF") - filemenu.Append(ID_EXPORT_IND, "Export &Individual Ranks", "Export ranks to PDF") - filemenu.AppendSeparator() - filemenu.Append(ID_EXIT, "E&xit", "Click to exit") - -# editmenu = wx.Menu() -# editmenu.Append(ID_UNDO, "&Undo", "Undo something") -# editmenu.Append(ID_REDO, "&Redo", "Undo an undo") -# editmenu.AppendSeparator() -# editmenu.Append(ID_CUT, "Cut", "Cut something") -# editmenu.Append(ID_COPY, "&Copy", "Copy something") -# editmenu.Append(ID_PASTE, "&Paste", "Paste something") -# editmenu.AppendSeparator() -# editmenu.Append(ID_FIND, "&Find...", "Find something") -# editmenu.Append(ID_FIND_REPLACE, "Find and Rep&lace...", "Find and replace something") -# editmenu.AppendSeparator() -# settingsmenu = wx.Menu() -# settingsmenu.Append(wx.ID_ANY, "Settings go here!", "Indeed they do!") -# editmenu.AppendSubMenu(settingsmenu, "&Settings", "Settings menu") - - helpmenu = wx.Menu() - helpmenu.Append(ID_HELP, "&Help", "No help for you!") - helpmenu.AppendSeparator() - helpmenu.Append(ID_ABOUT, "&About", "About this program") - - movemenu = wx.Menu() - movemenu.Append(ID_MOVE_NEW, "&New...", "Create a new whatsit") - movemenu.Append(ID_MOVE_IMPORT, "&Import ranks...", "Import ranks") - movemenu.AppendSeparator() - movemenu.Append(ID_MOVE_SPLIT, "&Split...", "...") - movemenu.Append(ID_MOVE_MERGE, "&Merge with next...", "...") - movemenu.Append(ID_MOVE_SHIFT, "S&hift...", "...") - movemenu.AppendSeparator() - movemenu.Append(ID_MOVE_TEXT, "Set &Text...", "...") - movemenu.Append(ID_MOVE_TEXT_OVERWRITE, "Set Text &Overwrite...", "...") - - menubar = wx.MenuBar() - menubar.Append(filemenu, "&File") -# menubar.Append(editmenu, "&Edit") - menubar.Append(helpmenu, "&Help") - menubar.Append(movemenu, "&Move") - - self.SetMenuBar(menubar) - # end menubar code - - # begin event code - wx.EVT_MENU(self, ID_EXIT, self.OnExit) - wx.EVT_MENU(self, ID_EDIT, self.EditSong) - wx.EVT_MENU(self, ID_SAVE, self.OnSave) - wx.EVT_MENU(self, ID_SAVE_AS, self.OnSaveAs) - wx.EVT_MENU(self, ID_OPEN, self.OnOpen) - wx.EVT_MENU(self, ID_HELP, self.OnHelp) - wx.EVT_MENU(self, ID_ABOUT, self.OnAbout) - wx.EVT_MENU(self, ID_NEW, self.CreateSong) - wx.EVT_MENU(self, ID_MOVE_NEW, self.CreateMove) - wx.EVT_MENU(self, ID_MOVE_IMPORT, self.OnMoveImport) - wx.EVT_MENU(self, ID_MOVE_SPLIT, self.OnMoveSplit) - wx.EVT_MENU(self, ID_MOVE_MERGE, self.OnMoveMerge) - wx.EVT_MENU(self, ID_MOVE_SHIFT, self.OnMoveShift) - wx.EVT_MENU(self, ID_MOVE_TEXT, self.OnMoveText) - wx.EVT_MENU(self, ID_MOVE_TEXT_OVERWRITE, self.OnMoveTextOverwrite) - wx.EVT_MENU(self, ID_EXPORT_PDF, self.OnExport) - wx.EVT_MENU(self, ID_EXPORT_IND, self.OnIndividualExport) - # end event code - - # begin main window code - self.panel = wx.Panel(self, wx.ID_ANY) - self.panel.Bind(wx.EVT_CHAR, self.OnKey) - - self.field = GUIField.Field(self.panel, ID_FIELD, wx.BORDER_RAISED, self) - self.field.Bind(wx.EVT_PAINT, self.field.OnPaint) - self.field.Bind(wx.EVT_SIZE, self.field.OnResize) - self.field.Bind(wx.EVT_LEFT_DOWN, self.field.OnLeftClick) - self.field.Bind(wx.EVT_RIGHT_DOWN, self.field.OnRightClick) - self.field.Bind(wx.EVT_LEFT_UP, self.field.OnLeftUnclick) - self.field.Bind(wx.EVT_RIGHT_UP, self.field.OnRightUnclick) - self.field.Bind(wx.EVT_MOTION, self.field.OnMouseMove) - self.field.Bind(wx.EVT_LEAVE_WINDOW, self.field.OnMouseExit) - self.field.Bind(wx.EVT_CHAR, self.OnKey) -#self.field.Bind(wx.EVT_KILL_FOCUS, lambda x: self.field.SetFocus()) -#self.field.SetFocus() - -# self.statusBarShowLabel = wx.StaticText(self, wx.ID_ANY, "SHOW NAME") - self.statusBarSongLabel = wx.StaticText(self.panel, wx.ID_ANY, "") - self.statusBarMoveLabel = wx.StaticText(self.panel, wx.ID_ANY, "") -# self.statusBarCountLabel = wx.StaticText(self.panel, wx.ID_ANY, "") - - self.statusBar = wx.BoxSizer(wx.HORIZONTAL) - self.statusBar.Add((1,1), 1, wx.EXPAND) -# self.statusBar.Add(statusBarShowLabel, 2, wx.EXPAND) - self.statusBar.Add(self.statusBarSongLabel, 0, wx.ALIGN_CENTRE) - self.statusBar.Add((100,1), 1, wx.EXPAND) - self.statusBar.Add(self.statusBarMoveLabel, 0, wx.ALIGN_CENTRE) -# self.statusBar.Add((150,1), 0, wx.EXPAND) # reserve 150px for statusBarMoveLabel -# self.statusBar.Add(self.statusBarCountLabel, 2, wx.EXPAND) - self.statusBar.Add((1,1), 1, wx.EXPAND) - - self.RefreshStatusBar() - - self.fieldpanel = wx.BoxSizer(wx.VERTICAL) - self.fieldpanel.Add(self.statusBar, 0, wx.ALIGN_CENTER) - self.fieldpanel.Add(self.field, 1, wx.EXPAND) - - - - self.rankNameUnicode = wx.Button(self.panel, wx.ID_ANY, "Unicode") - self.rankNameUnicode.Bind(wx.EVT_BUTTON, self.OnRankNameUnicode) - self.holdRankButton = wx.BitmapButton(self.panel, wx.ID_ANY, wx.Bitmap('icons/holdicon.png')) - self.holdRankButton.Bind(wx.EVT_BUTTON, self.OnHoldRank) - self.curveRankButton = wx.BitmapButton(self.panel, wx.ID_ANY, wx.Bitmap('icons/curveicon.png')) - self.curveRankButton.Bind(wx.EVT_BUTTON, self.OnCurveRank) - self.straightRankButton = wx.BitmapButton(self.panel, wx.ID_ANY, wx.Bitmap('icons/zigzagicon.png')) - self.straightRankButton.Bind(wx.EVT_BUTTON, self.OnStraightRank) - self.switchEndpointsButton = wx.BitmapButton(self.panel, wx.ID_ANY, wx.Bitmap('icons/switchendpointsicon.png')) - self.switchEndpointsButton.Bind(wx.EVT_BUTTON, self.OnSwitchEndpoints) - self.switchLabelButton = wx.BitmapButton(self.panel, wx.ID_ANY, wx.Bitmap('icons/switchlabelicon.png')) - self.switchLabelButton.Bind(wx.EVT_BUTTON, self.OnSwitchLabel) -# TODO(astory): discuss whether this button should exist or not -# self.deleteRankButton = wx.BitmapButton(self.panel, wx.ID_ANY, wx.Bitmap('icons/deleteicon.png')) -# self.deleteRankButton.Bind(wx.EVT_BUTTON, self.OnDeleteRank) - self.snapEndButton = wx.BitmapButton(self.panel, wx.ID_ANY, wx.Bitmap('icons/snaptoendicon.png')) - self.snapEndButton.Bind(wx.EVT_BUTTON, self.OnSnapEnd) - self.snapBeginButton = wx.BitmapButton(self.panel, wx.ID_ANY, wx.Bitmap('icons/snaptobeginicon.png')) - self.snapBeginButton.Bind(wx.EVT_BUTTON, self.OnSnapBegin) - self.displayAtCountButton = wx.BitmapButton(self.panel, wx.ID_ANY, wx.Bitmap('icons/displayicon.png')) - self.displayAtCountButton.Bind(wx.EVT_BUTTON, self.OnDisplayAtCount) - - self.toolbar = wx.BoxSizer(wx.HORIZONTAL) - self.toolbar.Add(self.rankNameUnicode, 0, wx.EXPAND) - self.toolbar.Add(self.holdRankButton, 0, wx.EXPAND) - self.toolbar.Add(self.curveRankButton, 0, wx.EXPAND) - self.toolbar.Add(self.straightRankButton, 0, wx.EXPAND) - self.toolbar.Add(self.switchEndpointsButton, 0, wx.EXPAND) - self.toolbar.Add(self.switchLabelButton, 0, wx.EXPAND) -# TODO(astory): discuss whether this button should exist or not -# self.toolbar.Add(self.deleteRankButton, 0, wx.EXPAND) - self.toolbar.Add(self.snapEndButton, 0, wx.EXPAND) - self.toolbar.Add(self.snapBeginButton, 0, wx.EXPAND) - self.toolbar.Add(self.displayAtCountButton, 0, wx.EXPAND) - - self.fieldbar = GUITimeBar.TimeBar(self.panel, wx.ID_ANY, self) - self.fieldbar.Bind(wx.EVT_PAINT, self.fieldbar.OnPaint) - self.fieldbar.Bind(wx.EVT_SIZE, self.fieldbar.OnResize) - self.fieldbar.Bind(wx.EVT_LEFT_DOWN, self.fieldbar.OnLeftClick) - self.fieldbar.Bind(wx.EVT_RIGHT_DOWN, self.fieldbar.OnRightClick) #right clicks dont actually do anything, ATM. - self.fieldbar.Bind(wx.EVT_LEFT_UP, self.fieldbar.OnLeftUnclick) - self.fieldbar.Bind(wx.EVT_RIGHT_UP, self.fieldbar.OnRightUnclick) - self.fieldbar.Bind(wx.EVT_MOTION, self.fieldbar.OnMouseMove) - self.fieldbar.Bind(wx.EVT_LEAVE_WINDOW, self.fieldbar.OnMouseExit) -# self.fieldbar.Bind(wx.EVT_CHAR, self.fieldbar.OnKey) - - self.moveSetList = wx.ImageList(GUIField.MINI_FIELD_SIZE[0], GUIField.MINI_FIELD_SIZE[1], False, 0) - self.moveList = wx.ListCtrl(self.panel, ID_MOVE_LIST, style = wx.LC_SINGLE_SEL | wx.LC_ICON | wx.LC_ALIGN_TOP) - self.moveList.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnMoveSelect) - self.moveList.AssignImageList(self.moveSetList, wx.IMAGE_LIST_NORMAL) - - self.moveListCaption = wx.StaticText(self.panel, wx.ID_ANY, "Sets/Moves") - - self.sidepanel = wx.BoxSizer(wx.VERTICAL) - self.sidepanel.Add(self.moveListCaption, 0, wx.ALIGN_CENTRE) - self.sidepanel.Add(self.moveList, 1, wx.EXPAND) - self.sidepanel.Add((GUIField.MINI_FIELD_SIZE[0] * 1.15, 1), 0, wx.ALIGN_CENTRE) - - self.toppanel = wx.BoxSizer(wx.HORIZONTAL) - self.toppanel.Add(self.fieldpanel, 4, wx.EXPAND) - self.toppanel.Add(wx.StaticLine(self.panel, style = wx.LI_VERTICAL), 0, wx.ALL | wx.EXPAND) - self.toppanel.Add(self.sidepanel, 0, wx.EXPAND) - - self.commandListCaption = wx.StaticText(self.panel, wx.ID_ANY, "Ranks: ") - self.commandListRankCaption = wx.StaticText(self.panel, wx.ID_ANY, "") - - self.commandListCaptionPanel = wx.BoxSizer(wx.HORIZONTAL) - self.commandListCaptionPanel.Add((1,1), 1, wx.EXPAND) - self.commandListCaptionPanel.Add(self.commandListCaption, 20, wx.EXPAND) - self.commandListCaptionPanel.Add(self.commandListRankCaption, 5, wx.EXPAND) - self.commandListCaptionPanel.Add((1,1), 1, wx.EXPAND) - - self.commandList = wx.ListBox(self.panel, ID_COMMAND_LIST, style = wx.LB_SINGLE) - self.commandList.Bind(wx.EVT_LISTBOX, self.OnCommandSelect) - - self.commandButtonMoveUp = wx.Button(self.panel, ID_COMMAND_BUTTON_MOVE_UP, u"\u2191") - self.commandButtonMoveUp.Bind(wx.EVT_BUTTON, lambda event: self.OnCommandButtonMove(event, True)) - self.commandButtonMoveDown = wx.Button(self.panel, ID_COMMAND_BUTTON_MOVE_DOWN, u"\u2193") - self.commandButtonMoveDown.Bind(wx.EVT_BUTTON, lambda event: self.OnCommandButtonMove(event, False)) - self.commandButtonRename = wx.Button(self.panel, ID_COMMAND_BUTTON_SPLIT, "Rename") - self.commandButtonRename.Bind(wx.EVT_BUTTON, self.OnCommandButtonRename) - self.commandButtonSplit = wx.Button(self.panel, ID_COMMAND_BUTTON_SPLIT, "Split") - self.commandButtonSplit.Bind(wx.EVT_BUTTON, self.OnCommandButtonSplit) - self.commandButtonMerge = wx.Button(self.panel, ID_COMMAND_BUTTON_MERGE, "Merge") - self.commandButtonMerge.Bind(wx.EVT_BUTTON, self.OnCommandButtonMerge) - self.commandButtonDelete = wx.Button(self.panel, ID_COMMAND_BUTTON_DELETE, "Delete") - self.commandButtonDelete.Bind(wx.EVT_BUTTON, self.OnCommandButtonDelete) - - self.commandListButtonPanel = wx.BoxSizer(wx.VERTICAL) - self.commandListButtonPanel.Add(self.commandButtonMoveUp, 5, wx.EXPAND) - self.commandListButtonPanel.Add(self.commandButtonMoveDown, 5, wx.EXPAND) - self.commandListButtonPanel.Add(self.commandButtonRename, 5, wx.EXPAND) - self.commandListButtonPanel.Add(self.commandButtonSplit, 5, wx.EXPAND) - self.commandListButtonPanel.Add(self.commandButtonMerge, 5, wx.EXPAND) - self.commandListButtonPanel.Add(self.commandButtonDelete, 5, wx.EXPAND) - - self.commandListPanel = wx.BoxSizer(wx.HORIZONTAL) - self.commandListPanel.Add(self.commandList, 5, wx.EXPAND) - self.commandListPanel.Add(self.commandListButtonPanel, 2, wx.EXPAND) - - self.bottompanelcentre = wx.BoxSizer(wx.VERTICAL) - self.bottompanelcentre.Add(self.commandListCaptionPanel, 0, wx.ALIGN_CENTRE) - self.bottompanelcentre.Add(self.commandListPanel, 20, wx.EXPAND) - -# self.rankNameListCaption = wx.StaticText(self.panel, wx.ID_ANY, "Ranks: ") - self.rankNameList = GUIRankList.RankList(self.panel, wx.ID_ANY, self) - self.rankNameList.Bind(wx.EVT_PAINT, self.rankNameList.OnPaint) - self.rankNameList.Bind(wx.EVT_SIZE, self.rankNameList.OnResize) - -# self.rankNameListPanel = wx.BoxSizer(wx.HORIZONTAL) -# self.rankNameListPanel.Add(self.rankNameListCaption, 0, wx.ALIGN_RIGHT) -# self.rankNameListPanel.Add(self.rankNameList, 0, wx.ALIGN_LEFT) - - self.rankNameListBox = wx.StaticBox(self.panel, wx.ID_ANY, "Ranks") - self.rankNameListPanel = wx.StaticBoxSizer(self.rankNameListBox, wx.HORIZONTAL) - self.rankNameListPanel.Add(self.rankNameList, 1, wx.EXPAND) - - self.rankNamePanel = wx.BoxSizer(wx.HORIZONTAL) - self.rankNamePanel.Add(self.rankNameListPanel, 1, wx.EXPAND) - - #TODO Make this a static Module level or Class level variable. - self.commandAddChoices = ['MT', 'Hlt', 'FM', 'BM', 'RS', 'LS', 'Flat'] - self.commandAddButtons = [] - - self.commandAddSpecialChoices = ['GT', 'PW', 'Exp', 'Cond', 'DTP', 'FTA', 'Curv'] - self.commandAddSpecialButtons = [] - - self.commandAddButtonsPanel = wx.BoxSizer(wx.HORIZONTAL) - - self.commandAddSpecialButtonsBox = wx.StaticBox(self.panel, wx.ID_ANY, "Special Commands") - self.commandAddSpecialButtonsPanel = wx.StaticBoxSizer(self.commandAddSpecialButtonsBox, wx.HORIZONTAL) - - for i in range(len(self.commandAddChoices)): - self.commandAddButtons.append(wx.ToggleButton(self.panel, wx.ID_ANY, self.commandAddChoices[i])) -# VERY HACKISH: we have the argument j and give it the default value of i instead of just doing -# lambda event: self.OnCommandAddButtons(event, i) -# because that way, we establish a link to the variable i, so every binding will be using the same value for i -# By setting j to a default value of i, we implicitly create a new variable and set it to the value of i at that time, -# thus circumventing this problem - self.commandAddButtons[i].Bind(wx.EVT_TOGGLEBUTTON, lambda event, j = i: self.OnCommandAddButtons(event, j)) - self.commandAddButtonsPanel.Add(self.commandAddButtons[i], 1, wx.EXPAND) - - self.commandAddButtons[0].SetValue(True) # make sure at least one thing is always selected - self.commandAddButtonSelected = 0 # keep track of which is selected - - for i in range(len(self.commandAddSpecialChoices)): - self.commandAddSpecialButtons.append(wx.Button(self.panel, wx.ID_ANY, self.commandAddSpecialChoices[i])) -# VERY HACKISH: we have the argument j and give it the default value of i instead of just doing -# lambda event: self.OnCommandAddButtons(event, i) -# because that way, we establish a link to the variable i, so every binding will be using the same value for i -# By setting j to a default value of i, we implicitly create a new variable and set it to the value of i at that time, -# thus circumventing this problem - self.commandAddSpecialButtons[i].Bind(wx.EVT_BUTTON, lambda event, j = i: self.OnCommandAddSpecialButtons(event, j)) - self.commandAddSpecialButtonsPanel.Add(self.commandAddSpecialButtons[i], 1, wx.EXPAND) - - self.commandLengthCaption = wx.StaticText(self.panel, wx.ID_ANY, "Steps: ") - self.commandLengthText = wx.TextCtrl(self.panel, wx.ID_ANY, "", size = (35, -1)) - self.commandAddButton = wx.Button(self.panel, wx.ID_ANY, "Add >>", style = wx.BU_EXACTFIT) - self.commandAddButton.Bind(wx.EVT_BUTTON, self.OnCommandAdd) - - self.commandLengthPanel = wx.BoxSizer(wx.HORIZONTAL) - self.commandLengthPanel.Add(self.commandLengthCaption, 0, wx.ALIGN_CENTRE) - self.commandLengthPanel.Add(self.commandLengthText, 0, wx.ALIGN_CENTRE) - self.commandLengthPanel.Add((1,1), 1, wx.EXPAND) - self.commandLengthPanel.Add(self.commandAddButton, 0, wx.ALIGN_CENTRE) - - self.commandAddButtonsBox = wx.StaticBox(self.panel, wx.ID_ANY, "Simple Commands") - self.commandAddButtonsPanelPanel = wx.StaticBoxSizer(self.commandAddButtonsBox, wx.HORIZONTAL) - self.commandAddButtonsPanelPanel.Add(self.commandAddButtonsPanel, 1, wx.EXPAND) - self.commandAddButtonsPanelPanel.Add((5,1), 0, wx.ALIGN_CENTRE) - self.commandAddButtonsPanelPanel.Add(wx.StaticLine(self.panel, style = wx.LI_VERTICAL), 0, wx.ALL | wx.EXPAND) - self.commandAddButtonsPanelPanel.Add((5,1), 0, wx.ALIGN_CENTRE) - self.commandAddButtonsPanelPanel.Add(self.commandLengthPanel, 0, wx.ALIGN_CENTRE) - - self.bottompanelleft = wx.BoxSizer(wx.VERTICAL) - self.bottompanelleft.Add(self.rankNamePanel, 0, wx.EXPAND) -# self.bottompanelleft.Add((1,1), 1, wx.EXPAND) - self.bottompanelleft.Add(self.commandAddButtonsPanelPanel, 0, wx.ALL | wx.EXPAND) - self.bottompanelleft.Add(self.commandAddSpecialButtonsPanel, 0, wx.ALL | wx.EXPAND) - self.bottompanelleft.Add((1,1), 1, wx.EXPAND) - - self.animationCaption = wx.StaticText(self.panel, wx.ID_ANY, "Animation Controls") - - self.playButton = wx.BitmapButton(self.panel, wx.ID_ANY, wx.Bitmap('icons/playicon.png')) - self.playButton.Bind(wx.EVT_BUTTON, self.OnAnimationBegin) - self.stopButton = wx.BitmapButton(self.panel, wx.ID_ANY, wx.Bitmap('icons/stopicon.png')) - self.stopButton.Bind(wx.EVT_BUTTON, self.OnAnimationEnd) - self.addWaypointButton = wx.BitmapButton(self.panel, wx.ID_ANY, wx.Bitmap('icons/addwaypointicon.png')) - self.addWaypointButton.Bind(wx.EVT_BUTTON, self.OnAddWaypoint) - self.removeWaypointButton = wx.BitmapButton(self.panel, wx.ID_ANY, wx.Bitmap('icons/removewaypointicon.png')) - self.removeWaypointButton.Bind(wx.EVT_BUTTON, self.OnRemoveWaypoint) - - self.bottompanelrightbuttons = wx.FlexGridSizer(2, 2) - self.bottompanelrightbuttons.Add(self.playButton, 0, wx.EXPAND) - self.bottompanelrightbuttons.Add(self.stopButton, 0, wx.EXPAND) - self.bottompanelrightbuttons.Add(self.addWaypointButton, 0, wx.EXPAND) - self.bottompanelrightbuttons.Add(self.removeWaypointButton, 0, wx.EXPAND) - - self.bottompanelright = wx.BoxSizer(wx.VERTICAL) - self.bottompanelright.Add(self.animationCaption, 0, wx.ALIGN_CENTRE) - self.bottompanelright.Add(self.bottompanelrightbuttons, 0, wx.ALIGN_CENTRE) - - self.bottompanel = wx.BoxSizer(wx.HORIZONTAL) -# self.bottompanel.Add((10,1), 0, wx.ALIGN_CENTRE) - self.bottompanel.Add(self.bottompanelleft, 3, wx.EXPAND) - self.bottompanel.Add(wx.StaticLine(self.panel, style = wx.LI_VERTICAL), 0, wx.ALL | wx.EXPAND) - self.bottompanel.Add(self.bottompanelcentre, 2, wx.EXPAND) - self.bottompanel.Add(wx.StaticLine(self.panel, style = wx.LI_VERTICAL), 0, wx.ALL | wx.EXPAND) - self.bottompanel.Add(self.bottompanelright, 1, wx.EXPAND) -# self.bottompanel.Add((1,1), 1, wx.EXPAND) # TODO -# self.bottompanel.Add(self.bottomcontrolleft, 0, wx.ALIGN_CENTRE) -# self.bottompanel.Add(self.bottomline, 0, wx.ALIGN_CENTRE) -# self.bottompanel.Add(self.commandList, 2, wx.EXPAND) - - self.mainpanel = wx.BoxSizer(wx.VERTICAL) - self.mainpanel.Add(self.toppanel, 15, wx.EXPAND) - self.mainpanel.Add(wx.StaticLine(self.panel), 0, wx.ALL | wx.EXPAND) - self.mainpanel.Add(self.toolbar, 0, wx.EXPAND) - self.mainpanel.Add(wx.StaticLine(self.panel), 0, wx.ALL | wx.EXPAND) - self.mainpanel.Add(self.fieldbar, 0, wx.EXPAND) - self.mainpanel.Add(wx.StaticLine(self.panel), 0, wx.ALL | wx.EXPAND) - self.mainpanel.Add(self.bottompanel, 5, wx.EXPAND) - - self.panel.SetSizer(self.mainpanel) -#self.SetAutoLayout(1) - # end main window code - - -# ensure that self intercepts all keypress events -# self.field.Bind(wx.EVT_KILL_FOCUS, lambda x: self.field.SetFocus()) -# self.field.SetFocus() - -# DEBUG - if DEBUG: - self.core.MoveAdded(1, 16, None) - self.core.MoveAdded(17, 32, None) -# DEBUG - - self.RefreshTitleBar() - self.RefreshMoveList() - self.RefreshStatusBar() - self.Show(True) - -# begin variables - - self.filename = None # this is the name of the currently-open file; None if not yet saved - self.animranks = None # list of animated rank locations when animating; None when not - self.animtimer = wx.Timer(self, ID_ANIM_TIMER) - wx.EVT_TIMER(self, ID_ANIM_TIMER, self.OnAnimationTimer) - - - def RefreshTitleBar(self): - if self.filename is None: - self.SetTitle("Rank Panda") - else: - self.SetTitle("Rank Panda (" + self.filename + ")") - - def RefreshMoveList(self): - self.moveList.Freeze() - self.moveList.ClearAll() - moves = self.core.GetMoves() - self.moveSetList.RemoveAll() - - i = 0 - - for m in moves: - item = wx.ListItem() - item.SetId(i) - item.SetText(m[1]) - item.SetImage(i) - - self.moveSetList.Add(self.field.RenderSet(self.core.GetRanks(moveNumber = i))) #(Brady) changed from GetRanksGivenMove(i) to GetRanks(moveNumber=i) - - self.moveList.InsertItem(item) - - i += 1 - - curr = self.core.GetCurrentMove()[0] - self.moveList.SetItemState(curr, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED) - self.moveList.Thaw() - - def RefreshCurrentMove(self): - curr = self.core.GetCurrentMove()[0] - self.moveSetList.Replace(curr, self.field.RenderSet(self.core.GetRanks(moveNumber = curr))) - self.moveList.RefreshItem(curr) - - def RefreshRankList(self): - ranks = self.core.GetRanks() - selected = self.core.GetSelectedRanks() - - rankLabels = [] - selectedRankLabels = [] - - i = 0 - - for r in ranks: - if r[1] is not None: #checks the name - isSelected = False - - for s in selected: #(Brady) This seems inefficient, try to fix - if r[0] == s[0]: - isSelected = True - - label = r[1] - if self.core.IsRankHeld(r[0]): - label += '*' - - label += ' ' - - rankLabels.append(label) - - if isSelected: - selectedRankLabels.append(i) - - i += 1 - - self.rankNameList.SetRanks(rankLabels, selectedRankLabels) - - def RefreshCommandList(self): - self.commandList.Clear() - ranks = self.core.GetSelectedRanks() - self.commandListRankCaption.SetLabel("") - - label = "" - for r in ranks: - if r[1] is not None: - label += r[1] - if self.core.IsRankHeld(r[0]): - label += '*' - label += ' ' - - self.commandListRankCaption.SetLabel(label) - - commands = self.core.GetCommands() - for c in commands: -# DEBUG - if DEBUG: - self.commandList.Append(c[0] + " " + str(c[1])) - else: - self.commandList.Append(c[0] + " " + str(int(round(c[1])))) -# DEBUG - selected = self.commandList.GetSelections() - self.core.SetListOfSelectedCommandNumbers(selected) - - def RefreshStatusBar(self): - self.statusBarSongLabel.SetLabel(self.core.GetSong()) - - currMove = self.core.GetCurrentMove() - info = self.core.GetMoveInfo(currMove[0]) - if currMove[2] == 1: - count = "count" - else: - count = "counts" - - if int(info[0]) == int(info[1]): - measure = "Measure" - else: - measure = "Measures" - - if currMove is not None: - self.statusBarMoveLabel.SetLabel(currMove[1] + ": " + str(currMove[2]) + " " + count + " (" + measure + " " + str(int(info[0])) + u"\u2013" + str(int(info[1])) + ")") -# self.statusBarCountLabel.SetLabel(str(currMove[2])) - else: - self.statusBarMoveLabel.SetLabel("") -# self.statusBarCountLabel.SetLabel("") - - - def CreateSong(self, event): - d = GUIDialogs.SongCreationDialog(self) - if d.ShowModal() == 0: - try: - core = CoreWrapper.CoreWrapper(*d.output) - except NameError as exception: - e = wx.MessageDialog(self, "Exception thrown while creating song!\nSong NOT created.\nException: " + str(exception), "Song Creation Exception", wx.OK) - e.ShowModal() - e.Destroy() - else: - self.core = core - self.field.Refresh(False) - self.fieldbar.Refresh(False) - self.RefreshMoveList() - self.RefreshRankList() - self.RefreshCommandList() - self.RefreshStatusBar() - - def EditSong(self, event): #try not having event. - d = GUIDialogs.SongCreationDialog(self) - if d.ShowModal() == 0: - try: - self.core.EditSongInfo(*d.output) - except NameError as exception: - e = wx.MessageDialog(self, "Exception thrown while creating song!\nSong NOT created.\nException: " + str(exception), "Song Edit Exception", wx.OK) - e.ShowModal() - e.Destroy() - else: - self.core = core - self.field.Refresh(False) - self.fieldbar.Refresh(False) - self.RefreshMoveList() - self.RefreshRankList() - self.RefreshCommandList() - self.RefreshStatusBar() - - def CreateMove(self, event, beginMeasure = "", endMeasure = ""): - moves = self.core.GetMoves() - moveNames = [] - - for m in moves: - moveNames.append(m[1]) - - d = GUIDialogs.MoveCreationDialog(self, moveNames, beginMeasure, endMeasure) - if d.ShowModal() == 0: - m = self.core.MoveAdded(*d.output) - importTarget = d.output2 - if importTarget >= m: # check if our insertion of a new move affected the index of the move to insert from, and correct - importTarget += 1 - - if m is None: - e = wx.MessageDialog(self, "New move overlaps with pre-existing moves!\nMove NOT created.", "Move Creation Exception", wx.OK) - e.ShowModal() - e.Destroy() - elif importTarget != -1: - self.core.ImportRankLocation(importTarget, m) - - self.field.Refresh(False) - self.fieldbar.Refresh(False) - self.RefreshMoveList() - self.RefreshRankList() - self.RefreshCommandList() - self.RefreshStatusBar() - - d.Destroy() - - def OnMoveSelect(self, event): - index = event.m_itemIndex - self.core.ChangeMove(index) - self.field.Refresh(False) - self.fieldbar.Refresh(False) - self.RefreshRankList() - self.RefreshCommandList() - self.RefreshStatusBar() - - def OnMoveImport(self, event): - move = self.core.GetCurrentMove() - - if move is None: - return - - moves = self.core.GetMoves() - moveNames = [] - - for m in moves: - moveNames.append(m[1]) - - d = GUIDialogs.ImportRanksDialog(self, moveNames) - if d.ShowModal() == 0: - self.core.ImportRankLocation(d.output, move[0]) - - self.field.Refresh(False) - self.RefreshMoveList() - self.RefreshRankList() - self.RefreshCommandList() - - d.Destroy() - - def OnMoveSplit(self, event): - move = self.core.GetCurrentMove() - - if move is None: - return - - d = wx.GetNumberFromUser("", "At count:", "Split Move", 1, 1, move[2], self) - if d != -1: - self.core.MoveEdited('Split', [move[0], d]) - - self.fieldbar.Refresh(False) - self.RefreshMoveList() - self.RefreshStatusBar() - - def OnMoveMerge(self, event): - move = self.core.GetCurrentMove() - - if move is None: - return - - self.core.MoveEdited('Merge', [move[0]]) - - self.fieldbar.Refresh(False) - self.RefreshMoveList() - self.RefreshStatusBar() - - def OnMoveShift(self, event): - move = self.core.GetCurrentMove() - - if move is None: - return - - d = wx.GetTextFromUser("Shift by how many counts:", "Shift Move", "0", self) - - try: - n = int(d) - except Exception: - d = wx.MessageDialog(self, "Invalid entry for number of counts to shift by!", "Parse Error", wx.OK) - d.ShowModal() - d.Destroy() - return - - self.fieldbar.Refresh(False) - self.RefreshMoveList() - self.RefreshStatusBar() - -#TODO -#self.core.MoveEdited('Shift', [move[0], n]) - self.RefreshMoveList() - self.RefreshStatusBar() - - def OnMoveText(self, event): - move = self.core.GetCurrentMove() - - if move is None: - return - - text = self.core.GetMoveText() - if text is None: - text = "" - - d = GUIDialogs.AddText(self, text, "Entering Text for " + move[1]) - if d.ShowModal() == 0: - if d.output == "": - self.core.SetMoveText(None) - else: - self.core.SetMoveText(d.output) - d.Destroy() - - def OnMoveTextOverwrite(self, event): - move = self.core.GetCurrentMove() - - if move is None: - return - - text = self.core.GetMoveTextOverwrite() - if text is None: - text = "" - - d = GUIDialogs.AddText(self, text, "Entering Overwrite Text for " + move[1]) - if d.ShowModal() == 0: - if d.output == "": - self.core.SetMoveTextOverwrite(None) - else: - self.core.SetMoveTextOverwrite(d.output) - d.Destroy() - - def OnCommandSelect(self, event): # TODO allow handling multiple contiguous selections - index = event.GetSelection() - self.core.SetListOfSelectedCommandNumbers([index]) - self.field.Refresh(False) - - def OnCommandAddButtons(self, event, i): -# don't allow deselecting of selected button (just so something is selected at all times) - self.commandAddButtons[i].SetValue(True) - self.commandAddButtonSelected = i # keep track of which is selected -# set all of the other buttons to disabled - for j in range(len(self.commandAddChoices)): - if i != j: - self.commandAddButtons[j].SetValue(False) - - def BitmapGet(self): - return self.field.PrintBitmap() - - def OnCommandAddSpecialButtons(self, event, i): - ranks = self.core.GetSelectedRanks() - #if len(ranks) != 1: - #d = wx.MessageDialog(self, "Currently, only adding commands to a single rank at a time is supported.", "Selection Error", wx.OK) - #d.ShowModal() - #d.Destroy() - #return - - #orig. code - #n=0 - #while(n 0: - self.RefreshCommandList() - self.commandList.SetSelection(selected[0] - 1) - elif not dir: # moved down - if selected[0] < self.commandList.GetCount() - 1: - self.RefreshCommandList() - self.commandList.SetSelection(selected[0] + 1) - - def OnCommandButtonRename(self, event): - selected = self.commandList.GetSelections() - if len(selected) != 1: - return - - label = self.core.GetCommands()[selected[0]][0] - d = wx.GetTextFromUser("New name:", "Rename Command", label, self) - if d != "": - ranks = self.core.GetSelectedRanks() - self.core.CommandEdited(ranks[0][0], selected[0], d) - self.RefreshCommandList() - - def OnCommandButtonSplit(self, event): - selected = self.commandList.GetSelections() - if len(selected) != 1: - return - - d = wx.GetNumberFromUser("", "At count:", "Split Command", 1, 1, self.core.GetCommands()[selected[0]][1], self) - if d != -1: - ranks = self.core.GetSelectedRanks() - self.core.CommandSplit(ranks[0][0], selected[0], d) - self.RefreshCommandList() - - def OnCommandButtonMerge(self, event): - selected = self.commandList.GetSelections() - if len(selected) != 1: - return - - ranks = self.core.GetSelectedRanks() - self.core.CommandMerge(ranks[0][0], selected[0]) - self.RefreshCommandList() - - def OnCommandButtonDelete(self, event): - selected = self.commandList.GetSelections() - if len(selected) != 1: - return - -# d = wx.MessageBox("Are you sure?", "Delete Command", wx.YES_NO, self) -# if d == wx.YES: -# ranks = self.core.GetSelectedRanks() -# if len(ranks) != 1: -# c = wx.MessageBox("Deleting commands for multiple ranks not yet implemented...", "Delete Command", wx.OK, self) -# c.ShowModal() -# c.destroy() -# else: -# self.core.CommandDeleted(ranks[0][0], selected[0]) - - ranks = self.core.GetSelectedRanks() - if len(ranks) != 1: - c = wx.MessageBox("Deleting commands for multiple ranks not yet implemented...", "Delete Command", wx.OK, self) - c.ShowModal() - c.destroy() - else: - self.core.CommandDeleted(ranks[0][0], selected[0]) - - self.RefreshCommandList() - -# TODO - def OnExit(self, event): - """ Exit menu item pressed """ - self.Close(True) - - def OnOpen(self, event): - """ Load file """ - dirname = '' - - while True: - d = wx.FileDialog(self, "Open File", dirname, "", "*.panda", wx.OPEN) - if d.ShowModal() == wx.ID_OK: - self.filename = os.path.join(d.GetDirectory(), d.GetFilename()) - if self.core.Load(self.filename) == -1: # load failed - e = wx.MessageDialog(self, "Unable to open file!", "File Access Exception", wx.OK) - e.ShowModal() - e.Destroy() - else: - self.RefreshTitleBar() - self.field.Refresh(False) - self.fieldbar.Refresh(False) - self.RefreshMoveList() - self.RefreshRankList() - self.RefreshCommandList() - self.RefreshStatusBar() - - break - else: - break - d.Destroy() - - def OnSave(self, event): - """ Save current file under same name (same as Save As if no current file) """ - if self.filename == None: - self.OnSaveAs(event) - else: - self.core.Save(self.filename) - - def OnSaveAs(self, event): - """ Save current file under new name """ - dirname = '' - d = wx.FileDialog(self, "Save File", dirname, "", "*.panda", wx.SAVE) - if d.ShowModal() == wx.ID_OK: - self.filename = os.path.join(d.GetDirectory(), d.GetFilename()) - self.core.Save(self.filename) -# TODO check for and strip off .panda? - d.Destroy() - - self.RefreshTitleBar() - - def OnSavePdf(self, event): - """ Save current file under new name """ - dirname = '' - d = wx.FileDialog(self, "Save File", dirname, "", "*.pdf", wx.SAVE) - if d.ShowModal() == wx.ID_OK: - self.filename = os.path.join(d.GetDirectory(), d.GetFilename()) - d.Destroy() - return self.filename - - def OnExport(self, event): - moveNames=[] - i=0 - while(i>", style = wx.BU_EXACTFIT) + self.commandAddButton.Bind(wx.EVT_BUTTON, self.OnCommandAdd) + + self.commandLengthPanel = wx.BoxSizer(wx.HORIZONTAL) + self.commandLengthPanel.Add(self.commandLengthCaption, 0, wx.ALIGN_CENTRE) + self.commandLengthPanel.Add(self.commandLengthText, 0, wx.ALIGN_CENTRE) + self.commandLengthPanel.Add((1,1), 1, wx.EXPAND) + self.commandLengthPanel.Add(self.commandAddButton, 0, wx.ALIGN_CENTRE) + + self.commandAddButtonsBox = wx.StaticBox(self.panel, wx.ID_ANY, "Simple Commands") + self.commandAddButtonsPanelPanel = wx.StaticBoxSizer(self.commandAddButtonsBox, wx.HORIZONTAL) + self.commandAddButtonsPanelPanel.Add(self.commandAddButtonsPanel, 1, wx.EXPAND) + self.commandAddButtonsPanelPanel.Add((5,1), 0, wx.ALIGN_CENTRE) + self.commandAddButtonsPanelPanel.Add(wx.StaticLine(self.panel, style = wx.LI_VERTICAL), 0, wx.ALL | wx.EXPAND) + self.commandAddButtonsPanelPanel.Add((5,1), 0, wx.ALIGN_CENTRE) + self.commandAddButtonsPanelPanel.Add(self.commandLengthPanel, 0, wx.ALIGN_CENTRE) + + self.bottompanelleft = wx.BoxSizer(wx.VERTICAL) + self.bottompanelleft.Add(self.rankNamePanel, 0, wx.EXPAND) +# self.bottompanelleft.Add((1,1), 1, wx.EXPAND) + self.bottompanelleft.Add(self.commandAddButtonsPanelPanel, 0, wx.ALL | wx.EXPAND) + self.bottompanelleft.Add(self.commandAddSpecialButtonsPanel, 0, wx.ALL | wx.EXPAND) + self.bottompanelleft.Add((1,1), 1, wx.EXPAND) + + self.animationCaption = wx.StaticText(self.panel, wx.ID_ANY, "Animation Controls") + + self.playButton = wx.BitmapButton(self.panel, wx.ID_ANY, wx.Bitmap('icons/playicon.png')) + self.playButton.Bind(wx.EVT_BUTTON, self.OnAnimationBegin) + self.stopButton = wx.BitmapButton(self.panel, wx.ID_ANY, wx.Bitmap('icons/stopicon.png')) + self.stopButton.Bind(wx.EVT_BUTTON, self.OnAnimationEnd) + self.addWaypointButton = wx.BitmapButton(self.panel, wx.ID_ANY, wx.Bitmap('icons/addwaypointicon.png')) + self.addWaypointButton.Bind(wx.EVT_BUTTON, self.OnAddWaypoint) + self.removeWaypointButton = wx.BitmapButton(self.panel, wx.ID_ANY, wx.Bitmap('icons/removewaypointicon.png')) + self.removeWaypointButton.Bind(wx.EVT_BUTTON, self.OnRemoveWaypoint) + + self.bottompanelrightbuttons = wx.FlexGridSizer(2, 2) + self.bottompanelrightbuttons.Add(self.playButton, 0, wx.EXPAND) + self.bottompanelrightbuttons.Add(self.stopButton, 0, wx.EXPAND) + self.bottompanelrightbuttons.Add(self.addWaypointButton, 0, wx.EXPAND) + self.bottompanelrightbuttons.Add(self.removeWaypointButton, 0, wx.EXPAND) + + self.bottompanelright = wx.BoxSizer(wx.VERTICAL) + self.bottompanelright.Add(self.animationCaption, 0, wx.ALIGN_CENTRE) + self.bottompanelright.Add(self.bottompanelrightbuttons, 0, wx.ALIGN_CENTRE) + + self.bottompanel = wx.BoxSizer(wx.HORIZONTAL) +# self.bottompanel.Add((10,1), 0, wx.ALIGN_CENTRE) + self.bottompanel.Add(self.bottompanelleft, 3, wx.EXPAND) + self.bottompanel.Add(wx.StaticLine(self.panel, style = wx.LI_VERTICAL), 0, wx.ALL | wx.EXPAND) + self.bottompanel.Add(self.bottompanelcentre, 2, wx.EXPAND) + self.bottompanel.Add(wx.StaticLine(self.panel, style = wx.LI_VERTICAL), 0, wx.ALL | wx.EXPAND) + self.bottompanel.Add(self.bottompanelright, 1, wx.EXPAND) +# self.bottompanel.Add((1,1), 1, wx.EXPAND) # TODO +# self.bottompanel.Add(self.bottomcontrolleft, 0, wx.ALIGN_CENTRE) +# self.bottompanel.Add(self.bottomline, 0, wx.ALIGN_CENTRE) +# self.bottompanel.Add(self.commandList, 2, wx.EXPAND) + + self.mainpanel = wx.BoxSizer(wx.VERTICAL) + self.mainpanel.Add(self.toppanel, 15, wx.EXPAND) + self.mainpanel.Add(wx.StaticLine(self.panel), 0, wx.ALL | wx.EXPAND) + self.mainpanel.Add(self.toolbar, 0, wx.EXPAND) + self.mainpanel.Add(wx.StaticLine(self.panel), 0, wx.ALL | wx.EXPAND) + self.mainpanel.Add(self.fieldbar, 0, wx.EXPAND) + self.mainpanel.Add(wx.StaticLine(self.panel), 0, wx.ALL | wx.EXPAND) + self.mainpanel.Add(self.bottompanel, 5, wx.EXPAND) + + self.panel.SetSizer(self.mainpanel) +#self.SetAutoLayout(1) + # end main window code + + +# ensure that self intercepts all keypress events +# self.field.Bind(wx.EVT_KILL_FOCUS, lambda x: self.field.SetFocus()) +# self.field.SetFocus() + +# DEBUG + if DEBUG: + self.core.MoveAdded(1, 16, None) + self.core.MoveAdded(17, 32, None) +# DEBUG + + self.RefreshTitleBar() + self.RefreshMoveList() + self.RefreshStatusBar() + self.Show(True) + +# begin variables + + self.filename = None # this is the name of the currently-open file; None if not yet saved + self.animranks = None # list of animated rank locations when animating; None when not + self.animtimer = wx.Timer(self, ID_ANIM_TIMER) + wx.EVT_TIMER(self, ID_ANIM_TIMER, self.OnAnimationTimer) + + + def RefreshTitleBar(self): + if self.filename is None: + self.SetTitle("Rank Panda") + else: + self.SetTitle("Rank Panda (" + self.filename + ")") + + def RefreshMoveList(self): + self.moveList.Freeze() + self.moveList.ClearAll() + moves = self.core.GetMoves() + self.moveSetList.RemoveAll() + + i = 0 + + for m in moves: + item = wx.ListItem() + item.SetId(i) + item.SetText(m[1]) + item.SetImage(i) + + self.moveSetList.Add(self.field.RenderSet(self.core.GetRanks(moveNumber = i))) + + self.moveList.InsertItem(item) + + i += 1 + + curr = self.core.GetCurrentMove()[0] + self.moveList.SetItemState(curr, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED) + self.moveList.Thaw() + + def RefreshCurrentMove(self): + curr = self.core.GetCurrentMove()[0] + self.moveSetList.Replace(curr, self.field.RenderSet(self.core.GetRanks(moveNumber = curr))) + self.moveList.RefreshItem(curr) + + def RefreshRankList(self): + ranks = self.core.GetRanks() + selected = self.core.GetSelectedRanks() + + rankLabels = [] + selectedRankLabels = [] + + i = 0 + + for r in ranks: + if r[1] is not None: #checks the name + isSelected = False + + for s in selected: #(Brady) This seems inefficient, try to fix + if r[0] == s[0]: + isSelected = True + + label = r[1] + if self.core.IsRankHeld(r[0]): + label += '*' + + label += ' ' + + rankLabels.append(label) + + if isSelected: + selectedRankLabels.append(i) + + i += 1 + + self.rankNameList.SetRanks(rankLabels, selectedRankLabels) + + def RefreshCommandList(self): + self.commandList.Clear() + ranks = self.core.GetSelectedRanks() + self.commandListRankCaption.SetLabel("") + + label = "" + for r in ranks: + if r[1] is not None: + label += r[1] + if self.core.IsRankHeld(r[0]): + label += '*' + label += ' ' + + self.commandListRankCaption.SetLabel(label) + + commands = self.core.GetCommands() + for c in commands: +# DEBUG + if DEBUG: + self.commandList.Append(c[0] + " " + str(c[1])) + else: + self.commandList.Append(c[0] + " " + str(int(round(c[1])))) +# DEBUG + selected = self.commandList.GetSelections() + self.core.SetListOfSelectedCommandNumbers(selected) + + def RefreshStatusBar(self): + self.statusBarSongLabel.SetLabel(self.core.GetSong()) + + currMove = self.core.GetCurrentMove() + info = self.core.GetMoveInfo(currMove[0]) + if currMove[2] == 1: + count = "count" + else: + count = "counts" + + if int(info[0]) == int(info[1]): + measure = "Measure" + else: + measure = "Measures" + + if currMove is not None: + self.statusBarMoveLabel.SetLabel(currMove[1] + ": " + str(currMove[2]) + " " + count + " (" + measure + " " + str(int(info[0])) + u"\u2013" + str(int(info[1])) + ")") +# self.statusBarCountLabel.SetLabel(str(currMove[2])) + else: + self.statusBarMoveLabel.SetLabel("") +# self.statusBarCountLabel.SetLabel("") + + + def CreateSong(self, event): + d = GUIDialogs.SongCreationDialog(self) + if d.ShowModal() == 0: + try: + core = CoreWrapper.CoreWrapper(*d.output) + except NameError as exception: + e = wx.MessageDialog(self, "Exception thrown while creating song!\nSong NOT created.\nException: " + str(exception), "Song Creation Exception", wx.OK) + e.ShowModal() + e.Destroy() + else: + self.core = core + self.field.Refresh(False) + self.fieldbar.Refresh(False) + self.RefreshMoveList() + self.RefreshRankList() + self.RefreshCommandList() + self.RefreshStatusBar() + + def EditSong(self, event): #try not having event. + d = GUIDialogs.SongCreationDialog(self) + if d.ShowModal() == 0: + try: + self.core.EditSongInfo(*d.output) + except NameError as exception: + e = wx.MessageDialog(self, "Exception thrown while creating song!\nSong NOT created.\nException: " + str(exception), "Song Edit Exception", wx.OK) + e.ShowModal() + e.Destroy() + else: + self.core = core + self.field.Refresh(False) + self.fieldbar.Refresh(False) + self.RefreshMoveList() + self.RefreshRankList() + self.RefreshCommandList() + self.RefreshStatusBar() + + def CreateMove(self, event, beginMeasure = "", endMeasure = ""): + moves = self.core.GetMoves() + moveNames = [] + + for m in moves: + moveNames.append(m[1]) + + d = GUIDialogs.MoveCreationDialog(self, moveNames, beginMeasure, endMeasure) + if d.ShowModal() == 0: + m = self.core.MoveAdded(*d.output) + importTarget = d.output2 + if importTarget >= m: # check if our insertion of a new move affected the index of the move to insert from, and correct + importTarget += 1 + + if m is None: + e = wx.MessageDialog(self, "New move overlaps with pre-existing moves!\nMove NOT created.", "Move Creation Exception", wx.OK) + e.ShowModal() + e.Destroy() + elif importTarget != -1: + self.core.ImportRankLocation(importTarget, m) + + self.field.Refresh(False) + self.fieldbar.Refresh(False) + self.RefreshMoveList() + self.RefreshRankList() + self.RefreshCommandList() + self.RefreshStatusBar() + + d.Destroy() + + def OnMoveSelect(self, event): + index = event.m_itemIndex + self.core.ChangeMove(index) + self.field.Refresh(False) + self.fieldbar.Refresh(False) + self.RefreshRankList() + self.RefreshCommandList() + self.RefreshStatusBar() + + def OnMoveImport(self, event): + move = self.core.GetCurrentMove() + + if move is None: + return + + moves = self.core.GetMoves() + moveNames = [] + + for m in moves: + moveNames.append(m[1]) + + d = GUIDialogs.ImportRanksDialog(self, moveNames) + if d.ShowModal() == 0: + self.core.ImportRankLocation(d.output, move[0]) + + self.field.Refresh(False) + self.RefreshMoveList() + self.RefreshRankList() + self.RefreshCommandList() + + d.Destroy() + + def OnMoveSplit(self, event): + move = self.core.GetCurrentMove() + + if move is None: + return + + d = wx.GetNumberFromUser("", "At count:", "Split Move", 1, 1, move[2], self) + if d != -1: + self.core.MoveEdited('Split', [move[0], d]) + + self.fieldbar.Refresh(False) + self.RefreshMoveList() + self.RefreshStatusBar() + + def OnMoveMerge(self, event): + move = self.core.GetCurrentMove() + + if move is None: + return + + self.core.MoveEdited('Merge', [move[0]]) + + self.fieldbar.Refresh(False) + self.RefreshMoveList() + self.RefreshStatusBar() + + def OnMoveShift(self, event): + move = self.core.GetCurrentMove() + + if move is None: + return + + d = wx.GetTextFromUser("Shift by how many counts:", "Shift Move", "0", self) + + try: + n = int(d) + except Exception: + d = wx.MessageDialog(self, "Invalid entry for number of counts to shift by!", "Parse Error", wx.OK) + d.ShowModal() + d.Destroy() + return + + self.fieldbar.Refresh(False) + self.RefreshMoveList() + self.RefreshStatusBar() + +#TODO +#self.core.MoveEdited('Shift', [move[0], n]) + self.RefreshMoveList() + self.RefreshStatusBar() + + def OnMoveText(self, event): + move = self.core.GetCurrentMove() + + if move is None: + return + + text = self.core.GetMoveText() + if text is None: + text = "" + + d = GUIDialogs.AddText(self, text, "Entering Text for " + move[1]) + if d.ShowModal() == 0: + if d.output == "": + self.core.SetMoveText(None) + else: + self.core.SetMoveText(d.output) + d.Destroy() + + def OnMoveTextOverwrite(self, event): + move = self.core.GetCurrentMove() + + if move is None: + return + + text = self.core.GetMoveTextOverwrite() + if text is None: + text = "" + + d = GUIDialogs.AddText(self, text, "Entering Overwrite Text for " + move[1]) + if d.ShowModal() == 0: + if d.output == "": + self.core.SetMoveTextOverwrite(None) + else: + self.core.SetMoveTextOverwrite(d.output) + d.Destroy() + + def OnCommandSelect(self, event): # TODO allow handling multiple contiguous selections + index = event.GetSelection() + self.core.SetListOfSelectedCommandNumbers([index]) + self.field.Refresh(False) + + def OnCommandAddButtons(self, event, i): +# don't allow deselecting of selected button (just so something is selected at all times) + self.commandAddButtons[i].SetValue(True) + self.commandAddButtonSelected = i # keep track of which is selected +# set all of the other buttons to disabled + for j in range(len(self.commandAddChoices)): + if i != j: + self.commandAddButtons[j].SetValue(False) + + def BitmapGet(self): + return self.field.PrintBitmap() + + def OnCommandAddSpecialButtons(self, event, i): + ranks = self.core.GetSelectedRanks() + #if len(ranks) != 1: + #d = wx.MessageDialog(self, "Currently, only adding commands to a single rank at a time is supported.", "Selection Error", wx.OK) + #d.ShowModal() + #d.Destroy() + #return + +# give a message that some ranks are not locked instead of doing nothing. + unlocked = [r for r in ranks if not self.core.IsRankHeld(r[0])] + if unlocked != []: + d = wx.MessageDialog(self, "Rank(s) " + ", ".join([r[1] for r in unlocked]) + " are not locked. Could not add command.", "Unlocked Ranks", wx.OK) + d.ShowModal() + #d.Destroy() + return + +# TODO check label and stuff + type = self.commandAddSpecialChoices[i] + + selected = self.commandList.GetSelections() + index = self.commandList.GetCount() + if len(selected) == 1: + index = selected[0] + + if type == "GT": + f = GUIDialogs.GateTurnDialog(self) + + if f.ShowModal() == 0: + (dir, pt, length) = f.output + if dir == 0: + type = "GTCW" + else: + type = "GTCCW" + + type += str(pt) + + for r in ranks: + self.core.CommandAdded(r[0], type, index, length, None, None) + + f.Destroy() + + elif type == "PW": + f = GUIDialogs.PinwheelDialog(self) + + if f.ShowModal() == 0: + (dir, length) = f.output + if dir == 0: + type = "PWCW" + else: + type = "PWCCW" + + for r in ranks: + self.core.CommandAdded(r[0], type, index, length, None, None) + + f.Destroy() + + elif type == "Exp": + f = GUIDialogs.ExpandDialog(self) + + if f.ShowModal() == 0: + (pt, length) = f.output + type += str(pt) + + for r in ranks: + self.core.CommandAdded(r[0], type, index, length, None, None) + + f.Destroy() + + elif type == "Cond": + f = GUIDialogs.CondenseDialog(self) + + if f.ShowModal() == 0: + (pt, length) = f.output + type += str(pt) + + for r in ranks: + self.core.CommandAdded(r[0], type, index, length, None, None) + + f.Destroy() + + elif type == "DTP" or type == "Curv": + f = GUIDialogs.DTPDialog(self) + + if f.ShowModal() == 0: + length = f.output + + DTPranks = self.core.BeginAddingDTP(index, length, None, type == "Curv") + if DTPranks is not None: + d = GUIDialogs.AddDTPDialog(self, self, ranks[0][1], length, DTPranks) + d.ShowModal() + d.Destroy() + self.core.FinalizeAddingDTP() + + f.Destroy() + + elif type == "FTA": + f = GUIDialogs.FTADialog(self) + + if f.ShowModal() == 0: + endpoint = f.output[0] == 1 + length = f.output[1] + + if endpoint: + FTAranks = self.core.BeginAddingFTA1(index, length, None) + else: + FTAranks = self.core.BeginAddingFTA0(index, length, None) + + if FTAranks is not None: + d = GUIDialogs.AddFTADialog(self, self, endpoint, ranks[0][1], length, FTAranks) + d.ShowModal() + d.Destroy() + + if endpoint: + self.core.FinalizeAddingFTA1() + else: + self.core.FinalizeAddingFTA0() + + f.Destroy() + + else: + pass + + self.field.Refresh(False) + self.RefreshCommandList() + + def OnCommandAdd(self, event): + ranks = self.core.GetSelectedRanks() + #if len(ranks) != 1: + #d = wx.MessageDialog(self, "Currently, only adding commands to a single rank at a time is supported.", "Selection Error", wx.OK) + #d.ShowModal() + #d.Destroy() + #return + + # add dialog to tell you its not locked. + unlocked = [r for r in ranks if not self.core.IsRankHeld(r[0])] + if unlocked != []: + d = wx.MessageDialog(self, "Ranks " + ", ".join([r[1] for r in unlocked])\ + + " are not locked. Could not add command.", "Unlocked Ranks", wx.OK) + d.ShowModal() + d.Destroy() + return + + try: + length = int(self.commandLengthText.GetValue()) + except Exception: + d = wx.MessageDialog(self, "Invalid entry for Command Steps!", "Parse Error", wx.OK) + d.ShowModal() + d.Destroy() + return + +# we assume that at most one button will be selected at a time, which is reasonable + type = self.commandAddChoices[self.commandAddButtonSelected] + + selected = self.commandList.GetSelections() + index = self.commandList.GetCount() + if len(selected) == 1: + index = selected[0] + + if type == "Hlt": + n=0 + while(n 0: + self.RefreshCommandList() + self.commandList.SetSelection(selected[0] - 1) + elif not dir: # moved down + if selected[0] < self.commandList.GetCount() - 1: + self.RefreshCommandList() + self.commandList.SetSelection(selected[0] + 1) + + def OnCommandButtonRename(self, event): + selected = self.commandList.GetSelections() + if len(selected) != 1: + return + + label = self.core.GetCommands()[selected[0]][0] + d = wx.GetTextFromUser("New name:", "Rename Command", label, self) + if d != "": + ranks = self.core.GetSelectedRanks() + self.core.CommandEdited(ranks[0][0], selected[0], d) + self.RefreshCommandList() + + def OnCommandButtonSplit(self, event): + selected = self.commandList.GetSelections() + if len(selected) != 1: + return + + d = wx.GetNumberFromUser("", "At count:", "Split Command", 1, 1, self.core.GetCommands()[selected[0]][1], self) + if d != -1: + ranks = self.core.GetSelectedRanks() + self.core.CommandSplit(ranks[0][0], selected[0], d) + self.RefreshCommandList() + + def OnCommandButtonMerge(self, event): + selected = self.commandList.GetSelections() + if len(selected) != 1: + return + + ranks = self.core.GetSelectedRanks() + self.core.CommandMerge(ranks[0][0], selected[0]) + self.RefreshCommandList() + + def OnCommandButtonDelete(self, event): + selected = self.commandList.GetSelections() + if len(selected) != 1: + return + +# d = wx.MessageBox("Are you sure?", "Delete Command", wx.YES_NO, self) +# if d == wx.YES: +# ranks = self.core.GetSelectedRanks() +# if len(ranks) != 1: +# c = wx.MessageBox("Deleting commands for multiple ranks not yet implemented...", "Delete Command", wx.OK, self) +# c.ShowModal() +# c.destroy() +# else: +# self.core.CommandDeleted(ranks[0][0], selected[0]) + + ranks = self.core.GetSelectedRanks() + if len(ranks) != 1: + c = wx.MessageBox("Deleting commands for multiple ranks not yet implemented...", "Delete Command", wx.OK, self) + c.ShowModal() + c.destroy() + else: + self.core.CommandDeleted(ranks[0][0], selected[0]) + + self.RefreshCommandList() + +# TODO + def OnExit(self, event): + """ Exit menu item pressed """ + self.Close(True) + + def OnOpen(self, event): + """ Load file """ + dirname = '' + + while True: + d = wx.FileDialog(self, "Open File", dirname, "", "*.panda", wx.OPEN) + if d.ShowModal() == wx.ID_OK: + self.filename = os.path.join(d.GetDirectory(), d.GetFilename()) + if self.core.Load(self.filename) == -1: # load failed + e = wx.MessageDialog(self, "Unable to open file!", "File Access Exception", wx.OK) + e.ShowModal() + e.Destroy() + else: + self.RefreshTitleBar() + self.field.Refresh(False) + self.fieldbar.Refresh(False) + self.RefreshMoveList() + self.RefreshRankList() + self.RefreshCommandList() + self.RefreshStatusBar() + + break + else: + break + d.Destroy() + + def OnSave(self, event): + """ Save current file under same name (same as Save As if no current file) """ + if self.filename == None: + self.OnSaveAs(event) + else: + self.core.Save(self.filename) + + def OnSaveAs(self, event): + """ Save current file under new name """ + dirname = '' + d = wx.FileDialog(self, "Save File", dirname, "", "*.panda", wx.SAVE) + if d.ShowModal() == wx.ID_OK: + self.filename = os.path.join(d.GetDirectory(), d.GetFilename()) + self.core.Save(self.filename) +# TODO check for and strip off .panda? + d.Destroy() + + self.RefreshTitleBar() + + def OnSavePdf(self, event): + """ Save current file under new name """ + dirname = '' + d = wx.FileDialog(self, "Save File", dirname, "", "*.pdf", wx.SAVE) + if d.ShowModal() == wx.ID_OK: + self.filename = os.path.join(d.GetDirectory(), d.GetFilename()) + d.Destroy() + return self.filename + + def OnExport(self, event): + moveNames=[] + i=0 + while(i oldend: # if there is a gap between this move and the previous one - self.DrawMove(oldend, begin, "", GAP_BORDER_COLOUR, GAP_COLOUR, GAP_BORDER_COLOUR, dc) - self.gaps.append((oldendm, m1 - 1, oldend, begin)) - self.DrawMove(begin, end, i[1], MOVE_BORDER_COLOUR, MOVE_COLOUR, MOVE_NAME_COLOUR, dc) - oldend = end - oldendm = m2 + 1 - - i = self.main.core.GetCurrentMove() - (m1, m2, begin, end) = self.main.core.GetMoveInfo(i[0]) - begin -= 1 # since counts start at 1 not 0 - # we don't decrement end because we want the move to be drawn to the end of its last count - self.DrawMove(begin, end, i[1], SELECTED_MOVE_BORDER_COLOUR, SELECTED_MOVE_COLOUR, SELECTED_MOVE_NAME_COLOUR, dc) - - def DrawWaypoints(self, dc): - for wp in self.main.core.GetListOfWayPoints(): - self.DrawWaypoint(wp, WAYPOINT_COLOUR, dc) - - if self.animatePt != -1: - self.DrawWaypoint([self.animatePt], ANIMATE_COLOUR, dc) - - def DrawText(self, dc): -# dc.SetTextForeground(TEXT_COLOUR) - dc.SetTextForeground(self.fgColour) - - textRect = wx.Rect(0, MOVE_HEIGHT + WAYPOINT_HEIGHT, self.GetRect().GetWidth(), TEXT_HEIGHT) - - if self.animatePt != -1: - dc.DrawImageLabel("Animating @ Count: " + str(self.animatePt), wx.NullBitmap, textRect, wx.ALIGN_CENTRE) - - if self.mouseX != -1: - count = self.mouseX / self.step - dc.DrawImageLabel("Count: " + str(int(round(count))), wx.NullBitmap, textRect, wx.ALIGN_LEFT | wx.ALIGN_CENTRE_VERTICAL) - - if self.mouseY > -1 and self.mouseY <= MOVE_HEIGHT: - ret = self.PickMove(count) - if ret is not None: - (m, m1, m2, begin, end, i) = ret - if begin == end: - count = "Count" - else: - count = "Counts" - - if int(m1) == int(m2): - measure = "Measure" - else: - measure = "Measures" - - dc.DrawImageLabel(m[1] + " [" + count + ": " + str(begin) + u"\u2013" + str(end) + ", " + measure + ": " + str(int(m1)) + u"\u2013" + str(int(m2)) + "]", wx.NullBitmap, textRect, wx.ALIGN_RIGHT | wx.ALIGN_CENTRE_VERTICAL) - else: - gap = self.PickGap(count) - if gap is not None: - (m1, m2, begin, end) = gap - if begin == end: - count = "Count" - else: - count = "Counts" - - if int(m1) == int(m2): - measure = "Measure" - else: - measure = "Measures" - - dc.DrawImageLabel("[" + count + ": " + str(begin) + u"\u2013" + str(end) + ", " + measure + ": " + str(int(m1)) + u"\u2013" + str(int(m2)) + "]", wx.NullBitmap, textRect, wx.ALIGN_RIGHT | wx.ALIGN_CENTRE_VERTICAL) - - elif self.mouseY > MOVE_HEIGHT and self.mouseY <= MOVE_HEIGHT + WAYPOINT_HEIGHT: - wp = self.PickWaypoint(count) - if wp is not None: - dc.DrawImageLabel("Waypoint[Count: " + str(wp[0]) + ", Time: " + str(wp[1]) + "]", wx.NullBitmap, textRect, wx.ALIGN_RIGHT | wx.ALIGN_CENTRE_VERTICAL) - - def DrawMove(self, begin, end, name, borderColour, moveColour, nameColour, dc): - dc.SetPen(wx.Pen(borderColour, MOVE_BORDER_WIDTH)) - dc.SetBrush(wx.Brush(moveColour)) - dc.SetTextForeground(nameColour) - - moveRect = wx.Rect(begin * self.step, 0, (end - begin) * self.step, MOVE_HEIGHT) - dc.DrawRoundedRectangleRect(moveRect, MOVE_BORDER_CORNER_RADIUS) - if name != "" and moveRect.GetWidth() >= dc.GetFullTextExtent(name)[0]: - dc.DrawImageLabel(name, wx.NullBitmap, moveRect, wx.ALIGN_CENTRE) - - def DrawWaypoint(self, wp, colour, dc): - dc.SetPen(wx.Pen(colour, 0)) - dc.SetBrush(wx.Brush(colour)) - - x = wp[0] * self.step; - list = [] - list.append(wx.Point(x - WAYPOINT_WIDTH / 2.0, MOVE_HEIGHT + WAYPOINT_HEIGHT)) - list.append(wx.Point(x + WAYPOINT_WIDTH / 2.0, MOVE_HEIGHT + WAYPOINT_HEIGHT)) - list.append(wx.Point(x, MOVE_HEIGHT)) - - dc.DrawPolygon(list) - - def PickMove(self, count): - i = 0 - for m in self.main.core.GetMoves(): - (m1, m2, begin, end) = self.main.core.GetMoveInfo(m[0]) - if count >= begin and count <= end: - return (m, m1, m2, begin, end, i) - i += 1 - - return None - - def PickGap(self, count): - for m in self.gaps: - (m1, m2, begin, end) = m - if count >= begin and count <= end: - return (m1, m2, begin, end) - - return None - - def PickWaypoint(self, count): - for wp in self.main.core.GetListOfWayPoints(): - if abs(wp[0] - count) <= WAYPOINT_SELECT_RADIUS: - return wp - - return None - - def OnResize(self, event): - (w, h) = self.GetSizeTuple() - self.rect = wx.Rect(0, 0, w, h) - self.step = float(w) / self.main.core.GetTotalCounts() - - self.Refresh(False) - - def AnimateCount(self, count): # tell the timebar that we are animating and are at the given count (-1 for stop animating) - self.animatePt = count - self.Refresh(False) - - def OnLeftClick(self, event): - count = self.mouseX / self.step - - if self.mouseY > -1 and self.mouseY <= MOVE_HEIGHT: - ret = self.PickMove(count) - if ret is not None: - self.main.moveList.SetItemState(ret[5], wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED) - else: - gap = self.PickGap(count) - if gap is not None: - (m1, m2, begin, end) = gap - self.main.CreateMove(None, str(int(m1)), str(int(m2))) - - self.Refresh(False) - - def OnRightClick(self, event): - pass - - def OnLeftUnclick(self, event): - pass - - def OnRightUnclick(self, event): - pass - - def OnMouseMove(self, event): - self.mouseX = event.m_x - self.mouseY = event.m_y - self.Refresh(False) - - def OnMouseExit(self, event): - self.mouseX = -1 - self.mouseY = -1 - self.Refresh(False) +#!/usr/bin/env python +#GUI Time Bar: renders the song time bar + +import wx +#import CoreWrapper + +# TODO: maybe fix height and draw waypoints under moves and have 'status bar' at bottom? + +# constants +TOTAL_HEIGHT = 50.0 +MOVE_HEIGHT = 20.0 +WAYPOINT_HEIGHT = 10.0 +TEXT_HEIGHT = 20.0 + +MOVE_BORDER_WIDTH = 2.0 +MOVE_BORDER_CORNER_RADIUS = 5.0 + +EMPTY_COLOUR = wx.Colour(0, 0, 0) + +GAP_COLOUR = wx.Colour(127, 0, 0) +GAP_BORDER_COLOUR = wx.Colour(255, 0, 0) + +MOVE_COLOUR = wx.Colour(0, 127, 127) +MOVE_BORDER_COLOUR = wx.Colour(127, 127, 0) +MOVE_NAME_COLOUR = wx.Colour(127, 127, 0) + +SELECTED_MOVE_COLOUR = wx.Colour(0, 191, 191) +SELECTED_MOVE_BORDER_COLOUR = wx.Colour(255, 255, 0) +SELECTED_MOVE_NAME_COLOUR = wx.Colour(255, 255, 0) + +WAYPOINT_WIDTH = 10.0 +WAYPOINT_SELECT_RADIUS = 2.0 + +WAYPOINT_COLOUR = wx.Colour(0, 0, 255) +SELECTED_WAYPOINT_COLOUR = wx.Colour(255, 0, 0) +ANIMATE_COLOUR = wx.Colour(0, 255, 0) + +TEXT_COLOUR = wx.Colour(255, 255, 255) + +class TimeBar(wx.Panel): + + + def __init__(self, parent, id, main): + wx.Panel.__init__(self, parent, id, size = (-1, 50)) + self.main = main + + self.bgColour = self.main.panel.GetBackgroundColour() + self.fgColour = self.main.panel.GetForegroundColour() + + self.gaps = [] +# gaps will be stored in the format (beginMeasure, endMeasure, beginCount, endCount) + self.animatePt = -1 + self.mouseX = -1 + + self.OnResize(None) + + def OnPaint(self, event): + self.Draw() + bardc = wx.PaintDC(self) + bardc.DrawBitmap(self.paintBitmap, 0, 0, False) # manual double-buffering since nothing else seems to work... + + def Draw(self): + self.paintBitmap = wx.EmptyBitmap(*self.GetSizeTuple()) + dc = wx.MemoryDC() + dc.SetFont(wx.Font(10,wx.FONTFAMILY_MODERN,wx.FONTSTYLE_NORMAL,wx.FONTWEIGHT_NORMAL, faceName="DejaVu Sans")) + dc.SelectObject(self.paintBitmap) + +# dc.SetBackground(wx.Brush(EMPTY_COLOUR)) + dc.SetBackground(wx.Brush(self.bgColour)) + dc.Clear() + + self.DrawMoves(dc) + self.DrawWaypoints(dc) + self.DrawText(dc) + +# TODO add in draw waypoint functionality + + def DrawMoves(self, dc): + oldend = 0 # end of previous move (need this for gap detection) + oldendm = 1 # end of previous move in measures (need this so we can display beginning/ending measures of gaps + for i in self.main.core.GetMoves(): + (m1, m2, begin, end) = self.main.core.GetMoveInfo(i[0]) + begin -= 1 # since counts start at 1 not 0 + # we don't decrement end because we want the move to be drawn to the end of its last count + if begin > oldend: # if there is a gap between this move and the previous one + self.DrawMove(oldend, begin, "", GAP_BORDER_COLOUR, GAP_COLOUR, GAP_BORDER_COLOUR, dc) + self.gaps.append((oldendm, m1 - 1, oldend, begin)) + self.DrawMove(begin, end, i[1], MOVE_BORDER_COLOUR, MOVE_COLOUR, MOVE_NAME_COLOUR, dc) + oldend = end + oldendm = m2 + 1 + + i = self.main.core.GetCurrentMove() + (m1, m2, begin, end) = self.main.core.GetMoveInfo(i[0]) + begin -= 1 # since counts start at 1 not 0 + # we don't decrement end because we want the move to be drawn to the end of its last count + self.DrawMove(begin, end, i[1], SELECTED_MOVE_BORDER_COLOUR, SELECTED_MOVE_COLOUR, SELECTED_MOVE_NAME_COLOUR, dc) + + def DrawWaypoints(self, dc): + for wp in self.main.core.GetListOfWayPoints(): + self.DrawWaypoint(wp, WAYPOINT_COLOUR, dc) + + if self.animatePt != -1: + self.DrawWaypoint([self.animatePt], ANIMATE_COLOUR, dc) + + def DrawText(self, dc): +# dc.SetTextForeground(TEXT_COLOUR) + dc.SetTextForeground(self.fgColour) + + textRect = wx.Rect(0, MOVE_HEIGHT + WAYPOINT_HEIGHT, self.GetRect().GetWidth(), TEXT_HEIGHT) + + if self.animatePt != -1: + dc.DrawImageLabel("Animating @ Count: " + str(self.animatePt), wx.NullBitmap, textRect, wx.ALIGN_CENTRE) + + if self.mouseX != -1: + count = self.mouseX / self.step + dc.DrawImageLabel("Count: " + str(int(round(count))), wx.NullBitmap, textRect, wx.ALIGN_LEFT | wx.ALIGN_CENTRE_VERTICAL) + + if self.mouseY > -1 and self.mouseY <= MOVE_HEIGHT: + ret = self.PickMove(count) + if ret is not None: + (m, m1, m2, begin, end, i) = ret + if begin == end: + count = "Count" + else: + count = "Counts" + + if int(m1) == int(m2): + measure = "Measure" + else: + measure = "Measures" + + dc.DrawImageLabel(m[1] + " [" + count + ": " + str(begin) + u"\u2013" + str(end) + ", " + measure + ": " + str(int(m1)) + u"\u2013" + str(int(m2)) + "]", wx.NullBitmap, textRect, wx.ALIGN_RIGHT | wx.ALIGN_CENTRE_VERTICAL) + else: + gap = self.PickGap(count) + if gap is not None: + (m1, m2, begin, end) = gap + if begin == end: + count = "Count" + else: + count = "Counts" + + if int(m1) == int(m2): + measure = "Measure" + else: + measure = "Measures" + + dc.DrawImageLabel("[" + count + ": " + str(begin) + u"\u2013" + str(end) + ", " + measure + ": " + str(int(m1)) + u"\u2013" + str(int(m2)) + "]", wx.NullBitmap, textRect, wx.ALIGN_RIGHT | wx.ALIGN_CENTRE_VERTICAL) + + elif self.mouseY > MOVE_HEIGHT and self.mouseY <= MOVE_HEIGHT + WAYPOINT_HEIGHT: + wp = self.PickWaypoint(count) + if wp is not None: + dc.DrawImageLabel("Waypoint[Count: " + str(wp[0]) + ", Time: " + str(wp[1]) + "]", wx.NullBitmap, textRect, wx.ALIGN_RIGHT | wx.ALIGN_CENTRE_VERTICAL) + + def DrawMove(self, begin, end, name, borderColour, moveColour, nameColour, dc): + dc.SetPen(wx.Pen(borderColour, MOVE_BORDER_WIDTH)) + dc.SetBrush(wx.Brush(moveColour)) + dc.SetTextForeground(nameColour) + + moveRect = wx.Rect(begin * self.step, 0, (end - begin) * self.step, MOVE_HEIGHT) + dc.DrawRoundedRectangleRect(moveRect, MOVE_BORDER_CORNER_RADIUS) + if name != "" and moveRect.GetWidth() >= dc.GetFullTextExtent(name)[0]: + dc.DrawImageLabel(name, wx.NullBitmap, moveRect, wx.ALIGN_CENTRE) + + def DrawWaypoint(self, wp, colour, dc): + dc.SetPen(wx.Pen(colour, 0)) + dc.SetBrush(wx.Brush(colour)) + + x = wp[0] * self.step; + list = [] + list.append(wx.Point(x - WAYPOINT_WIDTH / 2.0, MOVE_HEIGHT + WAYPOINT_HEIGHT)) + list.append(wx.Point(x + WAYPOINT_WIDTH / 2.0, MOVE_HEIGHT + WAYPOINT_HEIGHT)) + list.append(wx.Point(x, MOVE_HEIGHT)) + + dc.DrawPolygon(list) + + def PickMove(self, count): + i = 0 + for m in self.main.core.GetMoves(): + (m1, m2, begin, end) = self.main.core.GetMoveInfo(m[0]) + if count >= begin and count <= end: + return (m, m1, m2, begin, end, i) + i += 1 + + return None + + def PickGap(self, count): + for m in self.gaps: + (m1, m2, begin, end) = m + if count >= begin and count <= end: + return (m1, m2, begin, end) + + return None + + def PickWaypoint(self, count): + for wp in self.main.core.GetListOfWayPoints(): + if abs(wp[0] - count) <= WAYPOINT_SELECT_RADIUS: + return wp + + return None + + def OnResize(self, event): + (w, h) = self.GetSizeTuple() + self.rect = wx.Rect(0, 0, w, h) + self.step = float(w) / self.main.core.GetTotalCounts() + + self.Refresh(False) + + def AnimateCount(self, count): # tell the timebar that we are animating and are at the given count (-1 for stop animating) + self.animatePt = count + self.Refresh(False) + + def OnLeftClick(self, event): + count = self.mouseX / self.step + + if self.mouseY > -1 and self.mouseY <= MOVE_HEIGHT: + ret = self.PickMove(count) + if ret is not None: + self.main.moveList.SetItemState(ret[5], wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED) + else: + gap = self.PickGap(count) + if gap is not None: + (m1, m2, begin, end) = gap + self.main.CreateMove(None, str(int(m1)), str(int(m2))) + + self.Refresh(False) + + def OnRightClick(self, event): + pass + + def OnLeftUnclick(self, event): + pass + + def OnRightUnclick(self, event): + pass + + def OnMouseMove(self, event): + self.mouseX = event.m_x + self.mouseY = event.m_y + self.Refresh(False) + + def OnMouseExit(self, event): + self.mouseX = -1 + self.mouseY = -1 + self.Refresh(False) diff --git a/RankPanda/Move.py b/RankPanda/Move.py index 6cc7e0d..55d2ae7 100755 --- a/RankPanda/Move.py +++ b/RankPanda/Move.py @@ -1,309 +1,309 @@ -import RankIDGen -import Rank -import pprint - - -# TODO(astory): look into python attributes. If we're going to go with -# heavyweight java-style classes, we might as well let python do the heavy -# lifting and code generation. -class Move(object): - # TODO(astory): optional arguments - def __init__(self, startCount, length, prior, following, number): - self._startCount = startCount - self._length = length - self._idRankIndex = dict() #maps rank ID to rank object - self._nameRankIndex = dict() #maps rank name to rank object - self._RankIDGen = RankIDGen.RankIDGen() - self._name = None - self._number = None - self.SetNumber(number) - self._prior = prior - self._following = following - self._listOfActiveRanks = [] #selected ranks - self._listOfActiveCommands = [] #selected commands - self._moveText = None - self._moveTextOverwrite = None - - def GetNameRankIndex(self): - return self._nameRankIndex - - def GetStartCount(self): - return self._startCount - - def SetStartCount(self, startCount): - self._startCount = startCount - - def GetListOfActiveCommands(self): - return self._listOfActiveCommands - - def GetSelectedRanks(self): - return self._listOfActiveRanks - - def SetSelectedRanks(self, newList): - self._listOfActiveRanks = newList - - def GetLength(self): - return self._length - - def SetLength(self, newLength): - """Set length of move to newLength and update all ranks' commands.""" - self._length = newLength - self.UpdateAllRanksCommandList() - - def GetRankIDGen(self): - return self._RankIDGen - - def GetNumber(self): - return self._number - - def SetNumber(self, number): - if (number is not None and - ((self._name is None) or - (self._name == ('Move ' + str(self._number))))): - self._name = ('Move ' + str(number)) #If the move has a nonstandard name (not 'Move' + num), dont change it. - self._number = number - - def GetName(self): - return self._name - - def SetName(self, name): - self._name = name - - def GetPrior(self): - return self._prior - - def SetPrior(self, newPrior): - """Set the prior move to this move and update all ranks' commands.""" - self._prior = newPrior - self.UpdateAllRanksCommandList() - - def GetAllRanks(self): - return self._idRankIndex.values() - - def GetAllNamedRanks(self): - return self._nameRankIndex.values() - - # Shortcut - def UpdateAllRanksCommandList(self): - i = 0 - items = self._nameRankIndex.items() - while (i < len(items)): - items[i][1].UpdateCommandList() - i = i + 1 - - def GetFollowing(self): - return self._following - - def SetFollowing(self, following): - self._following = following - - # TODO(astory): make name an optional argument - def CreateRank(self, location, name=None): - """Create a new rank for this move. - - Creates a new rank and adds it to the IDRankIndex. Names the rank if a - name is given. - """ - r = Rank.Rank(location, self) - self._idRankIndex[r.GetID()] = r - if (name is not None): - self.NameRank(r.GetID(), name) - r.UpdateCommandList() - return r - - def NameRank(self, ID, name): - """Set the name of a rank in this move.""" - if (self.LookUpName(name) is None): #doesnt allow you to change name? - r = self._idRankIndex[ID] - if (r.GetName() in self._nameRankIndex): - del self._nameRankIndex[r.GetName()] - r.SetName(name) - self._nameRankIndex[name] = r - - # Deletes a rank. Note that this will also need to reset the command list - # for the rank in the following move, if applicable. - def DeleteRank(self, id): - """Delete a rank, and update the following move's corresponding rank.""" - r = self._idRankIndex[id] - # TODO(astory): if r in _listOfActiveRanks - if (self._listOfActiveRanks.count(r) != 0): - self._listOfActiveRanks.remove(r) - del self._idRankIndex[id] - name = r.GetName() - if (name is not None): - del self._nameRankIndex[name] - if (self._following is not None): - if (self._following.LookUpName(name) is not None): - self._following.LookUpName(name).UpdateCommandList() - - # Should be self-explanatory. - def DeleteAllRanks(self): - i = 0 - items = self._idRankIndex.items() - while (i < len(items)): - ID = items[i][0] - self.DeleteRank(ID) - i = i + 1 - - # TODO(astory): this is ugly, and also possibly crash-laden. FIXME - def MergeWithPrior(self): - """Merge this move with the previous one. - - See song.MergeMoves() for more documentation. Also resets the prior and - following references of moves immediately before and after the merged - moves. - - Returns: - The new move created - """ - prior = self.GetPrior() - priorprior = prior.GetPrior() - following = self.GetFollowing() - newMove = Move(prior._startCount, - (prior._length + self._length), - priorprior, - following, - prior.GetNumber()) - if (priorprior is not None): - priorprior.SetFollowing(newMove) - if (following is not None): - following.SetPrior(newMove) - PriorIDMarkedIndex = dict() - i = 0 - Iterator = prior._idRankIndex.items() - l = len(Iterator) - while (i < l): - PriorIDMarkedIndex[Iterator[i][0]] = False - i = i + 1 - i = 0 - Iterator = self._idRankIndex.items() - l = len(Iterator) - while (i < l): - rank = Iterator[i][1] - name = rank.GetName() - newMove.CreateRank(rank.GetEndLocation(), name=name) - if (name is not None): - oldRank = prior.LookUpName(name) - if (priorprior is not None): - oldoldRank = priorprior.LookUpName(name) - else: - oldoldRank = None - newRank = newMove.LookUpName(name) - newRank.hold = rank.hold - if (oldRank is not None): - newRank.hold = (rank.hold or oldRank.hold) - oldID = oldRank.GetID() - PriorIDMarkedIndex[oldID] = True - if (oldoldRank is not None): - if (newRank.hold): - newRank.SetCommandList( - oldRank.GetCommandList().extend( - rank.GetCommandList())) - else: - newRank.UpdateCommandList() - i = i + 1 - i = 0 - Iterator = PriorIDMarkedIndex.items() - l = len(Iterator) - while (i < l): - if (Iterator[i][1]): - pass - else: - ID = Iterator[i][0] - rank = prior.LookUpID(ID) - name = rank.GetName() - newMove.CreateRank(rank.GetEndLocation(), name=name) - newRank = newMove.LookUpName(name) - newRank.hold = rank.hold - if (name is not None): - if (priorprior is not None): - oldoldRank = priorprior.LookUpName(name) - if (oldoldRank is not None): - commandList = rank.GetCommantList() - commandList.append(Command.MarkTime(self._length, rank.GetEndLocation())) - newRank.SetCommandList(commandList) - if (following is not None): - followingRank = following.LookUpName(name) - if (followingRank is not None): - followingRank.UpdateCommandList() - newRank.UpdateCommandList() - i = i + 1 - return newMove - - # TODO(astory): really? return a list? FIXME - def Split(self, count): - """Split this move at count. - - Also resets the prior and following references of moves immediately - before and after the split move. - - Returns: - A list of the two new moves created - """ - newMoveFirst = Move(self._startCount, count, self._prior, - None, self.GetNumber()) - newMoveSecond = Move((self._startCount + count), (self._length - count), - newMoveFirst, self._following, - (self.GetNumber() + 1)) - newMoveFirst.SetFollowing(newMoveSecond) - if (self._prior is not None): - self._prior.SetFollowing(newMoveFirst) - if (self._following is not None): - self._following.SetPrior(newMoveSecond) - i = 0 - Iterator = self._idRankIndex.items() - l = len(Iterator) - while (i < l): - rank = Iterator[i][1] - name = rank.GetName() - newMoveFirst.CreateRank(rank.GetCalculatedLocation(count), name=name) - newMoveSecond.CreateRank(rank.GetEndLocation(), name=name) - if ((rank.hold) and (rank.GetName() is not None) and - (self._prior.LookUpName(name) is not None)): - newRank1 = newMoveFirst.LookUpName(name) - newRank2 = newMoveSecond.LookUpName(name) - newRank1.hold = rank.hold - newRank2.hold = rank.hold - i = 0 - tot = count - while (tot > 0): - tot = tot - rank.GetCommandList()[i].GetLength() - i = i + 1 - i = i - 1 - tot = tot + rank.GetCommandList()[i].GetLength() - beginLoc = rank.GetCommandList()[i - 1].GetEndLocation() - splitCommand = rank.GetCommandList()[i].Split(tot, beginLoc) - firstCommandList = rank.GetCommandList()[0:(i - 1)] - firstCommandList.append(splitCommand[0]) - secondCommandList = [splitCommand[1]] - secondCommandList.extend(rank.GetCommandList()[(i + 1):-1]) - newRank1.SetCommandList(firstCommandList) - newRank2.SetCommandList(secondCommandList) - i = i + 1 - return [newMoveFirst, newMoveSecond] - - def LookUpName(self, name): - """Return the rank with name in this move, or None.""" - if (name in self._nameRankIndex): - return self._nameRankIndex[name] - else: - return None - - def LookUpID(self, ID): - """Return the rank with ID in this move, or None.""" - if (ID in self._idRankIndex): - return self._idRankIndex[ID] - else: - return None - - def GetMoveText(self): - return self._moveText - - def SetMoveText(self, moveText): - self._moveText = moveText - - def GetMoveTextOverwrite(self): - return self._moveTextOverwrite - - def SetMoveTextOverwrite(self, moveTextOverwrite): - self._moveTextOverwrite = moveTextOverwrite +import RankIDGen +import Rank +import pprint + + +# TODO(astory): look into python attributes. If we're going to go with +# heavyweight java-style classes, we might as well let python do the heavy +# lifting and code generation. +class Move(object): + # TODO(astory): optional arguments + def __init__(self, startCount, length, prior, following, number): + self._startCount = startCount + self._length = length + self._idRankIndex = dict() #maps rank ID to rank object + self._nameRankIndex = dict() #maps rank name to rank object + self._RankIDGen = RankIDGen.RankIDGen() + self._name = None + self._number = None + self.SetNumber(number) + self._prior = prior + self._following = following + self._listOfActiveRanks = [] #selected ranks + self._listOfActiveCommands = [] #selected commands + self._moveText = None + self._moveTextOverwrite = None + + def GetNameRankIndex(self): + return self._nameRankIndex + + def GetStartCount(self): + return self._startCount + + def SetStartCount(self, startCount): + self._startCount = startCount + + def GetListOfActiveCommands(self): + return self._listOfActiveCommands + + def GetSelectedRanks(self): + return self._listOfActiveRanks + + def SetSelectedRanks(self, newList): + self._listOfActiveRanks = newList + + def GetLength(self): + return self._length + + def SetLength(self, newLength): + """Set length of move to newLength and update all ranks' commands.""" + self._length = newLength + self.UpdateAllRanksCommandList() + + def GetRankIDGen(self): + return self._RankIDGen + + def GetNumber(self): + return self._number + + def SetNumber(self, number): + if (number is not None and + ((self._name is None) or + (self._name == ('Move ' + str(self._number))))): + self._name = ('Move ' + str(number)) #If the move has a nonstandard name (not 'Move' + num), dont change it. + self._number = number + + def GetName(self): + return self._name + + def SetName(self, name): + self._name = name + + def GetPrior(self): + return self._prior + + def SetPrior(self, newPrior): + """Set the prior move to this move and update all ranks' commands.""" + self._prior = newPrior + self.UpdateAllRanksCommandList() + + def GetAllRanks(self): + return self._idRankIndex.values() + + def GetAllNamedRanks(self): + return self._nameRankIndex.values() + + # Shortcut + def UpdateAllRanksCommandList(self): + i = 0 + items = self._nameRankIndex.items() + while (i < len(items)): + items[i][1].UpdateCommandList() + i = i + 1 + + def GetFollowing(self): + return self._following + + def SetFollowing(self, following): + self._following = following + + # TODO(astory): make name an optional argument + def CreateRank(self, location, name=None): + """Create a new rank for this move. + + Creates a new rank and adds it to the IDRankIndex. Names the rank if a + name is given. + """ + r = Rank.Rank(location, self) + self._idRankIndex[r.GetID()] = r + if (name is not None): + self.NameRank(r.GetID(), name) + r.UpdateCommandList() + return r + + def NameRank(self, ID, name): + """Set the name of a rank in this move.""" + if (self.LookUpName(name) is None): #doesnt allow you to change name? + r = self._idRankIndex[ID] + if (r.GetName() in self._nameRankIndex): + del self._nameRankIndex[r.GetName()] + r.SetName(name) + self._nameRankIndex[name] = r + + # Deletes a rank. Note that this will also need to reset the command list + # for the rank in the following move, if applicable. + def DeleteRank(self, id): + """Delete a rank, and update the following move's corresponding rank.""" + r = self._idRankIndex[id] + # TODO(astory): if r in _listOfActiveRanks + if (self._listOfActiveRanks.count(r) != 0): + self._listOfActiveRanks.remove(r) + del self._idRankIndex[id] + name = r.GetName() + if (name is not None): + del self._nameRankIndex[name] + if (self._following is not None): + if (self._following.LookUpName(name) is not None): + self._following.LookUpName(name).UpdateCommandList() + + # Should be self-explanatory. + def DeleteAllRanks(self): + i = 0 + items = self._idRankIndex.items() + while (i < len(items)): + ID = items[i][0] + self.DeleteRank(ID) + i = i + 1 + + # TODO(astory): this is ugly, and also possibly crash-laden. FIXME + def MergeWithPrior(self): + """Merge this move with the previous one. + + See song.MergeMoves() for more documentation. Also resets the prior and + following references of moves immediately before and after the merged + moves. + + Returns: + The new move created + """ + prior = self.GetPrior() + priorprior = prior.GetPrior() + following = self.GetFollowing() + newMove = Move(prior._startCount, + (prior._length + self._length), + priorprior, + following, + prior.GetNumber()) + if (priorprior is not None): + priorprior.SetFollowing(newMove) + if (following is not None): + following.SetPrior(newMove) + PriorIDMarkedIndex = dict() + i = 0 + Iterator = prior._idRankIndex.items() + l = len(Iterator) + while (i < l): + PriorIDMarkedIndex[Iterator[i][0]] = False + i = i + 1 + i = 0 + Iterator = self._idRankIndex.items() + l = len(Iterator) + while (i < l): + rank = Iterator[i][1] + name = rank.GetName() + newMove.CreateRank(rank.GetEndLocation(), name=name) + if (name is not None): + oldRank = prior.LookUpName(name) + if (priorprior is not None): + oldoldRank = priorprior.LookUpName(name) + else: + oldoldRank = None + newRank = newMove.LookUpName(name) + newRank.hold = rank.hold + if (oldRank is not None): + newRank.hold = (rank.hold or oldRank.hold) + oldID = oldRank.GetID() + PriorIDMarkedIndex[oldID] = True + if (oldoldRank is not None): + if (newRank.hold): + newRank.SetCommandList( + oldRank.GetCommandList().extend( + rank.GetCommandList())) + else: + newRank.UpdateCommandList() + i = i + 1 + i = 0 + Iterator = PriorIDMarkedIndex.items() + l = len(Iterator) + while (i < l): + if (Iterator[i][1]): + pass + else: + ID = Iterator[i][0] + rank = prior.LookUpID(ID) + name = rank.GetName() + newMove.CreateRank(rank.GetEndLocation(), name=name) + newRank = newMove.LookUpName(name) + newRank.hold = rank.hold + if (name is not None): + if (priorprior is not None): + oldoldRank = priorprior.LookUpName(name) + if (oldoldRank is not None): + commandList = rank.GetCommantList() + commandList.append(Command.MarkTime(self._length, rank.GetEndLocation())) + newRank.SetCommandList(commandList) + if (following is not None): + followingRank = following.LookUpName(name) + if (followingRank is not None): + followingRank.UpdateCommandList() + newRank.UpdateCommandList() + i = i + 1 + return newMove + + # TODO(astory): really? return a list? FIXME + def Split(self, count): + """Split this move at count. + + Also resets the prior and following references of moves immediately + before and after the split move. + + Returns: + A list of the two new moves created + """ + newMoveFirst = Move(self._startCount, count, self._prior, + None, self.GetNumber()) + newMoveSecond = Move((self._startCount + count), (self._length - count), + newMoveFirst, self._following, + (self.GetNumber() + 1)) + newMoveFirst.SetFollowing(newMoveSecond) + if (self._prior is not None): + self._prior.SetFollowing(newMoveFirst) + if (self._following is not None): + self._following.SetPrior(newMoveSecond) + i = 0 + Iterator = self._idRankIndex.items() + l = len(Iterator) + while (i < l): + rank = Iterator[i][1] + name = rank.GetName() + newMoveFirst.CreateRank(rank.GetCalculatedLocation(count), name=name) + newMoveSecond.CreateRank(rank.GetEndLocation(), name=name) + if ((rank.hold) and (rank.GetName() is not None) and + (self._prior.LookUpName(name) is not None)): + newRank1 = newMoveFirst.LookUpName(name) + newRank2 = newMoveSecond.LookUpName(name) + newRank1.hold = rank.hold + newRank2.hold = rank.hold + i = 0 + tot = count + while (tot > 0): + tot = tot - rank.GetCommandList()[i].GetLength() + i = i + 1 + i = i - 1 + tot = tot + rank.GetCommandList()[i].GetLength() + beginLoc = rank.GetCommandList()[i - 1].GetEndLocation() + splitCommand = rank.GetCommandList()[i].Split(tot, beginLoc) + firstCommandList = rank.GetCommandList()[0:(i - 1)] + firstCommandList.append(splitCommand[0]) + secondCommandList = [splitCommand[1]] + secondCommandList.extend(rank.GetCommandList()[(i + 1):-1]) + newRank1.SetCommandList(firstCommandList) + newRank2.SetCommandList(secondCommandList) + i = i + 1 + return [newMoveFirst, newMoveSecond] + + def LookUpName(self, name): + """Return the rank with name in this move, or None.""" + if (name in self._nameRankIndex): + return self._nameRankIndex[name] + else: + return None + + def LookUpID(self, ID): + """Return the rank with ID in this move, or None.""" + if (ID in self._idRankIndex): + return self._idRankIndex[ID] + else: + return None + + def GetMoveText(self): + return self._moveText + + def SetMoveText(self, moveText): + self._moveText = moveText + + def GetMoveTextOverwrite(self): + return self._moveTextOverwrite + + def SetMoveTextOverwrite(self, moveTextOverwrite): + self._moveTextOverwrite = moveTextOverwrite diff --git a/RankPanda/Point.py b/RankPanda/Point.py index 1bdfa5f..f36fd8b 100755 --- a/RankPanda/Point.py +++ b/RankPanda/Point.py @@ -1,63 +1,63 @@ -import math - -"""A simple point module. - -Exported Methods: - -Distance -- Euclidian distance between two points - -Exported Classes: - -Point -- a simple point class with an x and y position. -""" - -def Distance(p1, p2): - """Get the Euclidean distance between two points""" - return math.sqrt((p1.x - p2.x)**2 + (p1.y - p2.y)**2) - -# TODO(astory): differentiate slopes and points. They shouldn't be represented -# with the same class because not all of the functions make sense. -class Point(object): - """ A simple point class with an x and y position. - - This class simply represents a point. It's often an x,y coordinate - on-screen, thought it's also sometimes a pair of slopes (dx/dt and dy/dt). - - Possible modification: Get rid of this and convert to all wx.Point perhaps? - - Public functions: - CompareTo -- sees if two points are within epsilon of each other - Clone -- produce a deep copy. - """ - def __init__(self, x, y): - self.x = x - self.y = y - - def CompareTo(self, p1, epsilon=0.5): - """Compare two points, and return if they are within epsilon - - Args: - p1: the other point to which to compare - epsilon: the minimum distance for equality. - - Returns: - A boolean, whether the points are within epsilon of each other or - not. - """ - return Distance(self, p1) < epsilon - - def __eq__(self, other): - return self.CompareTo(other, epsilon=0.00001) - - def __ne__(self, other): - return not self.__eq__(other) - - def Clone(self): - """Produce a deep copy of this point""" - return Point(self.x, self.y) - - def __repr__(self): - return ('Point with x = ' + str(self.x) + ', y = ' + str(self.y)) - - def __str__(self): - return self.__repr__() +import math + +"""A simple point module. + +Exported Methods: + +Distance -- Euclidian distance between two points + +Exported Classes: + +Point -- a simple point class with an x and y position. +""" + +def Distance(p1, p2): + """Get the Euclidean distance between two points""" + return math.sqrt((p1.x - p2.x)**2 + (p1.y - p2.y)**2) + +# TODO(astory): differentiate slopes and points. They shouldn't be represented +# with the same class because not all of the functions make sense. +class Point(object): + """ A simple point class with an x and y position. + + This class simply represents a point. It's often an x,y coordinate + on-screen, thought it's also sometimes a pair of slopes (dx/dt and dy/dt). + + Possible modification: Get rid of this and convert to all wx.Point perhaps? + + Public functions: + CompareTo -- sees if two points are within epsilon of each other + Clone -- produce a deep copy. + """ + def __init__(self, x, y): + self.x = x + self.y = y + + def CompareTo(self, p1, epsilon=0.5): + """Compare two points, and return if they are within epsilon + + Args: + p1: the other point to which to compare + epsilon: the minimum distance for equality. + + Returns: + A boolean, whether the points are within epsilon of each other or + not. + """ + return Distance(self, p1) < epsilon + + def __eq__(self, other): + return self.CompareTo(other, epsilon=0.00001) + + def __ne__(self, other): + return not self.__eq__(other) + + def Clone(self): + """Produce a deep copy of this point""" + return Point(self.x, self.y) + + def __repr__(self): + return ('Point with x = ' + str(self.x) + ', y = ' + str(self.y)) + + def __str__(self): + return self.__repr__() diff --git a/RankPanda/Printer.py b/RankPanda/Printer.py index 2a33bd5..2346a41 100644 --- a/RankPanda/Printer.py +++ b/RankPanda/Printer.py @@ -1,170 +1,170 @@ -# Testing reportlab - -#import reportlab -import os -from reportlab.pdfgen import canvas -from reportlab.lib.units import inch, cm -from reportlab.lib.pagesizes import LETTER -from reportlab.lib.fonts import addMapping -from reportlab.pdfbase import pdfmetrics, ttfonts -import PIL -from PIL import Image -import CoreWrapper -import GUIField - -#register DejaVuSansMono -pdfmetrics.registerFont(ttfonts.TTFont('DejaVuSansMono', 'DejaVuSansMono.ttf')) -addMapping('DejaVuSansMono', 0, 0, 'DejaVuSansMono') - -#constants -page_size = (LETTER[1],LETTER[0]) #landscape of letter standard -field_size = (9*inch, 5*inch) -field_height = 3*inch -widthHeightRatio= GUIField.FIELD_LENGTH_STEPS/GUIField.FIELD_WIDTH_STEPS - -top_margin = page_size[1] - inch -bottom_margin = inch -left_margin = inch -right_margin = page_size[0] - inch -frame_width = right_margin - left_margin - -def splitString(commStr, charLim, splitChar): - strLength=0 - aStr="" - bStr=commStr - i=0 - while (strLength<(charLim-2)): - partStr=bStr.partition(splitChar) - aStrNew = aStr + partStr[0] + splitChar - bStrNew = partStr[2] - strLength=len(aStrNew) - if(strLength charactersPerCol): - splStr=splitString(commString, charactersPerCol, ",") - if (k == columns-1): textObj.textLine(splStr[0]) - else: textObj.textOut(splStr[0]) - if (splStr[1] != ""): - if(i+columns > len(stringList)): - j=0 - lenStrL=len(stringList) - while(j < (i+columns-lenStrL+1)): - stringList.insert(len(stringList),"") - j=j+1 - stringList.insert(i+columns, " " + splStr[1]) - i=i+1 - else: - if (k == columns-1): textObj.textLine(commString) - else: textObj.textOut(commString) - i=i+1 - k=k+1 - textObj.setXPos((right_margin-left_margin)/columns) - - -def printDrill(song, stringPath, moveNames, commandStrings, fontSize, columnsArr, measureInfo, movetexts): - - if (not (stringPath.endswith(".pdf"))): - stringPath=stringPath + ".pdf" - - if (os.path.exists(stringPath)): - if (os.path.isfile(stringPath)): - #check if open? - os.remove(stringPath) - - canv = canvas.Canvas(stringPath, page_size) - i=0 - while (i charactersPerCol): + splStr=splitString(commString, charactersPerCol, ",") + if (k == columns-1): textObj.textLine(splStr[0]) + else: textObj.textOut(splStr[0]) + if (splStr[1] != ""): + if(i+columns > len(stringList)): + j=0 + lenStrL=len(stringList) + while(j < (i+columns-lenStrL+1)): + stringList.insert(len(stringList),"") + j=j+1 + stringList.insert(i+columns, " " + splStr[1]) + i=i+1 + else: + if (k == columns-1): textObj.textLine(commString) + else: textObj.textOut(commString) + i=i+1 + k=k+1 + textObj.setXPos((right_margin-left_margin)/columns) + + +def printDrill(song, stringPath, moveNames, commandStrings, fontSize, columnsArr, measureInfo, movetexts): + + if (not (stringPath.endswith(".pdf"))): + stringPath=stringPath + ".pdf" + + if (os.path.exists(stringPath)): + if (os.path.isfile(stringPath)): + #check if open? + os.remove(stringPath) + + canv = canvas.Canvas(stringPath, page_size) + i=0 + while (i 0: - # if the last one is MT - if (self._commandList[l - 1].GetName() == 'MT'): - i = l - 1 - # go backwards until you run out of commands or it's not a MT - while ((i >= 0) and (self._commandList[i].GetName() == 'MT')): - i = i - 1 - # if you didn't reach the beginning - if (i >= 0): - # reset to the first end MT - i = i + 1 - # remove all the trailing MTs - while (len(self._commandList) > i): - self._commandList.pop() - else: # we reached the beginning - # slap on a mark time for the right length - self._commandList =\ - [Commands.MarkTime(self._move.GetLength(),\ - self._commandList[-1].GetEndLocation())] - # Add a marktime to the end to fill in the gaps - total = self.CalculateTotalCountsOfCommands() - if (total < self._move.GetLength()): - self._commandList.append(\ - Commands.MarkTime(\ - self._move.GetLength() - total,\ - self._commandList[-1].GetEndLocation())) - - def GetPrior(self): - """Returns this rank from the previous move if it exists, else, None""" - if (self._name is None): - return None - p = self._move.GetPrior() - if (p is None): - return None - return (p.LookUpName(self._name)) - - def GetFollowing(self): - """Returns this rank from the next move if it exists, else, None""" - if (self._name is None): - return None - p = self._move.GetFollowing() - if (p is None): - return None - return (p.LookUpName(self._name)) - - - # Starting from the prior Rank's _endLocation, go through each Command in - # the Command List, generating the end location of each, until you've used - # up enough counts to hit the given count value. Note that this may not be - # the entire command list. This doesn't actually make any permanent - # changes. - - # Return None if you can't calcuclate the location (because of no - # command list) - def GetCalculatedLocation(self, count): - """Returns this rank's location at count count of the move, which may - not be the end location if count < the number of counts of commands""" - priorRank = self.GetPrior() - if (priorRank is None): - return self.GetEndLocation() - # TODO(astory): this should be condensible, do so after writing tests - if (len(self._commandList) == 0): - return self.GetEndLocation() - if (count > self.CalculateTotalCountsOfCommands()): - return self.GetEndLocation() - curLocation = priorRank.GetEndLocation() - i = 0 - countsLeft = count - while (countsLeft > 0): - curCommand = self._commandList[i] - # TODO(astory): consider speeding up by adding whole length of - # command if it fits in our count total - if (countsLeft >= curCommand.GetLength()): - curLocation = curCommand.CalcLocation(curCommand.GetLength(), curLocation) - else: - curLocation = curCommand.CalcLocation(countsLeft, curLocation) - countsLeft = countsLeft - curCommand.GetLength() - i = i + 1 - return curLocation - - def GetEndLocation(self): - """Returns the rank's final location""" - return copy.deepcopy(self._endLocation) - - # TODO(astory): this might be superfluous since GetCalculatedLocation never - # returns none AFAICT - def GetLocationAtCount(self, count): - """Returns the rank's location at the given count""" - loc = self.GetCalculatedLocation(count) - if (loc is None): - loc = self.GetEndLocation() - return loc - - - # TODO(astory): clean - # Used when a rank is moved around. If self.hold == false and - # self.grabbed == false, re-generate the command list. - - # Should never be called! -# def SetEndLocation(self, newEndLocation): -# pass - - - - # Adds the given command to the command list in the desired location - # Number should be the number in the command list that the new command - # will occupy. So, if you're inserting before the fourth command, - # number should be 3 (because it's 0-indexed.) - def AddBasicCommand(self, commandName, number, length, name): - """Insert a command of type commandName at index number of the command - list, for length counts with name name, but only if the rank is locked, - and this rank is represented in a previous command""" - if ((not self.hold) or (self.GetPrior() is None)): - return - if (number == 0): - beginLocation = self.GetPrior().GetEndLocation() - else: - beginLocation = self._commandList[number - 1].GetEndLocation() - - #(Brady) simplified this by adding a dictionary to hash names to the function - newCommand = commandHash[commandName](length, beginLocation) #May want to catch a KeyError in case of typo or something... - - self._commandList.insert(number, newCommand) - if (name is not None): - newCommand.SetName(name) - self.UpdateCommandList() - - # TODO(astory): merge into the previous with optional arguments - def AddDTP(self, number, length, name, endLocation): - """Add a DTP, which requires an end location as well""" - if ((not self.hold) or (self.GetPrior() is None)): - return - if (number == 0): - beginLocation = self.GetPrior().GetEndLocation() - else: - beginLocation = self._commandList[number - 1].GetEndLocation() - newCommand = Commands.DTP(length, beginLocation, endLocation) - self._commandList.insert(number, newCommand) - if (name is not None): - newCommand.SetName(name) - self.UpdateCommandList() - - # TODO(astory): merge into the previous with optional arguments - def AddCurve(self, number, length, name, endLocation): - """Add a curve, which requires an end location""" - if ((not self.hold) or (self.GetPrior() is None)): - return - if (number == 0): - beginLocation = self.GetPrior().GetEndLocation() - else: - beginLocation = self._commandList[number - 1].GetEndLocation() - newCommand = Commands.Curve(length, beginLocation, endLocation) - self._commandList.insert(number, newCommand) - if (name is not None): - newCommand.SetName(name) - self.UpdateCommandList() - - # TODO(astory): merge into the previous with optional arguments - def AddFTA1(self, number, length, name, endLocation, listOfFTAPoints): - """Add an FTA at 1""" - if ((not self.hold) or (self.GetPrior() is None)): - return - if (number == 0): - beginLocation = self.GetPrior().GetEndLocation() - else: - beginLocation = self._commandList[number - 1].GetEndLocation() - newCommand = Commands.FTA1(length, beginLocation, endLocation, listOfFTAPoints) - self._commandList.insert(number, newCommand) - if (name is not None): - newCommand.SetName(name) - self.UpdateCommandList() - - # TODO(astory): merge into the previous with optional arguments - def AddFTA0(self, number, length, name, endLocation, listOfFTAPoints): - """Add an FTA at 0""" - if ((not self.hold) or (self.GetPrior() is None)): - return - if (number == 0): - beginLocation = self.GetPrior().GetEndLocation() - else: - beginLocation = self._commandList[number - 1].GetEndLocation() - newCommand = Commands.FTA0(length, beginLocation, endLocation, listOfFTAPoints) - self._commandList.insert(number, newCommand) - if (name is not None): - newCommand.SetName(name) - self.UpdateCommandList() - - def MoveCommandUp(self, commandNumber): - """Move commandNumber command up the command list""" - if ((not self.hold) or (self.GetPrior() is None) or (commandNumber == 0)): - return - command = self._commandList.pop(commandNumber) - self._commandList.insert(commandNumber - 1, command) - self.UpdateCommandList() - - def MoveCommandDown(self, commandNumber): - """Move commandNumber command down the command list""" - if ((not self.hold) or (self.GetPrior() is None)\ - or (commandNumber == (len(self._commandList) - 1))): - return - command = self._commandList.pop(commandNumber) - self._commandList.insert(commandNumber + 1, command) - self.UpdateCommandList() - - def DeleteCommand(self, commandNumber): - """Remove commandNumber command""" - if ((not self.hold) or (self.GetPrior() is None)): - return - self._commandList.pop(commandNumber) - tot = 0 - i = 0 - while (i < len(self._commandList)): - tot = tot + self._commandList[i].GetLength() - i = i + 1 - self.UpdateCommandList() - # TODO(astory): clean up -# if (tot < self._move.GetLength()): -# if (self._commandList[-1].GetName() == "MarkTime"): -# tempLength = self._commandList[-1].GetLength() -# self._commandList.pop(len(self._commandList) - 1) -# self.AddBasicCommand("MarkTime", len(self._commandList), (self._move.GetLength() - tot + tempLength), "MarkTime") -# else: -# self.AddBasicCommand("MarkTime", len(self._commandList), (self._move.GetLength() - tot), "MarkTime") - - def ReNameCommand(self, commandNumber, newName): - """Change the name of commandNumber command to newName""" - if ((not self.hold) or (self.GetPrior() is None)): - return - self._commandList[commandNumber].SetName(newName) - - def SplitCommand(self, commandNumber, count): - """Split commandNumber command at count""" - if ((not self.hold) or (self.GetPrior() is None)): - return - commandToSplit = self._commandList.pop(commandNumber) - if (commandNumber == 0): - newCommands = commandToSplit.Split(\ - count, self.GetPrior().GetEndLocation()) - else: - newCommands = commandToSplit.Split(\ - count, self._commandList[commandNumber - 1].GetEndLocation()) - self._commandList.insert(commandNumber, newCommands[1]) - self._commandList.insert(commandNumber, newCommands[0]) - - def MergeCommands(self, firstCommandNumber): - """Merge firstCommandNumber and the following command together""" - if ((not self.hold) or (self.GetPrior() is None)): - return - if ((len(self._commandList)) > firstCommandNumber + 1): - return - firstCommand = self._commandList[firstCommandNumber] - secondCommand = self._commandList[secondCommandNumber] - newCommand = firstCommand.MergeWithFollowing(secondCommand) - if (newCommand is not None): - self._commandList.pop(firstCommandNumber) - self._commandList[firstCommandNumber] = newCommand - - # TODO(astory): why do we need this? - def GetCalculatedBeginLocation(self, frontCount): - """Calculate the beginning location from the end location and the - commands""" - count = self._move.GetLength() - frontCount - tot = 0 - i = 0 - resid = 0 - while (tot < count): - curLength = self._commandList[i].GetLength() - if (curLength + tot >= count): - resid = count - tot - else: - i = i + 1 - tot = tot + curLength - curLocation = self._endLocation - curLocation = self._commandList[i].CalcBeginLocation(resid, curLocation) - i = i - 1 - while (i >= 0): - curLocation =\ - self._commandList[i].CalcBeginLocation(\ - self._commandList[i].GetLength(), curLocation) - i = i - 1 - return curLocation - - def AddSplinePoint(self, number): - """Add a spline point to the rank after point number. Does not add a - spline point after the final point""" - listOfPoints = self._endLocation.GetListOfPoints() - if (number == len(listOfPoints) - 1): - return - p = self._endLocation.GetPointAtT(0.5, number) - listOfPoints.insert(number + 1, p) - self._endLocation.SetListOfPoints(listOfPoints, None) - - def DeleteSplinePoint(self, number): - """Removes number point if it is not one of the last two""" - listOfPoints = self._endLocation.GetListOfPoints() - if (len(listOfPoints) > 2): - listOfPoints.pop(number) - self._endLocation.SetListOfPoints(listOfPoints, None) - - # TODO(astory): this is never used - def DeleteAllSplinePoints(self): - """Removes all points except the two end points""" - listOfPoints = self._endLocation.GetListOfPoints() - newList = (listOfPoints[0], listOfPoints[-1]) - self._endLocation.SetListOfPoints(newList, None) - - def SnapEndLocation(self): - """Set each command's end location and the rank's end location to as - calculated based on the command and the previous move""" - if (self.GetPrior() is None): - return - self._endLocation = self.GetCalculatedLocation(self._move.GetLength()) - self.UpdateCommandList() - - # Starting from self._endLocation, work backwards through the command list. - # Snap each command to the requisite beginning location, and snap the - # _endLocation of the rank of the prior move as well. - def SnapBeginLocation(self): - """Set each command's beginning location and the rank's end location to - as calculated based on the command and the previous move""" - prior = self.GetPrior() - if (self.GetPrior() is None): - return - prior._endLocation = self.GetCalculatedBeginLocation(0) - prior.UpdateCommandList() - - # TODO(astory): convert to wrappers to a parameterized function - def SetStraight(self): - """Set the rank and all of its commands to be non-curved""" - self._endLocation.SetCurved(False) - i = 0 - while (i < len(self._commandList)): - self._commandList[i].GetEndLocation().SetCurved(False) - i = i + 1 - - def SetCurved(self): - """Set the rank and all of its commands to be curved""" - self._endLocation.SetCurved(True) - i = 0 - while (i < len(self._commandList)): - self._commandList[i].GetEndLocation().SetCurved(True) - i = i + 1 - - # TODO(astory): rewrite as a list comprehension - def CalculateCountFromCommandNumber(self, commandNumber): - """Returns the count at which commandNumber command starts""" - if (commandNumber == 0): - return 0 - i = 0 - accum = 0 - while (i < commandNumber): - accum = accum + self._commandList[i].GetLength() - i = i + 1 - return accum - - # TODO(astory): rewrite as a list comprehension - def CalculateTotalCountsOfCommands(self): - """Return the sum of lengths of the commands""" - i = 0 - accum = 0 - while (i < len(self._commandList)): - accum = accum + self._commandList[i].GetLength() - i = i + 1 - return accum - - # TODO(astory): break into own file? - def _GenerateCommandList(self, beginLocation, endLocation, length): - """Generate a list of commands to take the rank from beginLocation to - endLocation in length counts""" - # The algorithm: - # First, check to see if this is a 'special case'. If so, even if the - # rank is curved, it can be treated as a straight line. Otherwise, - # flatten the rank. The number of counts taken is equal to the length - # of the furthest spline point from that straight line. Then, use the - # straight line generator to get form one straight line to another, and - # then recurve, if nececssary. As a final step, append a MarkTime. - # This step may be able to be skipped, as the UpdateCommandList - # function, the only thing calling this, should append it automatically - # if necessary. - if (self._IsSpecialCaseCMDGen(beginLocation, endLocation)): - return self._GenerateCommandListStraightLine(\ - beginLocation, endLocation, length, [])[0] - commandListSoFar = [] - beginPointList = beginLocation.GetListOfPoints() - endPointList = endLocation.GetListOfPoints() - if (not beginLocation.IsStraight()): - i = 1 - lengthsBeginMax = 0 - while (i < (len(beginPointList) - 1)): - valsBegin = self._CalcLengthsHelper(beginLocation, i) - lengthsBeginMax = max(lengthsBeginMax, valsBegin[0]) - i = i + 1 - newCommand = Commands.Flatten(lengthsBeginMax, beginLocation) - commandListSoFar.append(newCommand) - length = length - newCommand.GetLength() - beginLocation = newCommand.GetEndLocation() - - # TODO(astory): assign into tuple - commandListSoFarTuple =\ - self._GenerateCommandListStraightLine(\ - beginLocation,\ - endLocation,\ - length,\ - commandListSoFar) - commandListSoFar = commandListSoFarTuple[0] - length = commandListSoFarTuple[1] - beginLocation = commandListSoFarTuple[2] - - if (not endLocation.IsStraight()): - i = 1 - lengthsEndMax = 0 - while (i < (len(endPointList) - 1)): - valsEnd = self._CalcLengthsHelper(endLocation, i) - lengthsEndMax = max(lengthsEndMax, valsEnd[0]) - i = i + 1 - newCommand = Commands.Curve(lengthsEndMax, beginLocation, endLocation) - commandListSoFar.append(newCommand) - length = length - newCommand.GetLength() - beginLocation = newCommand.GetEndLocation() - - if (length > 0): - newCommand = Commands.MarkTime(length, endLocation) - commandListSoFar.append(newCommand) - length = length - newCommand.GetLength() - - return commandListSoFar - - def _IsSpecialCaseCMDGen(self, beginLocation, endLocation): - """Return true if expand, condense, GT, PW, and slides are enough to - perform the relocation""" - # The purpose of this method is to determine if a rank can be gotten - # from one RankLocation to another with just Expand, Condense, GT, PW, - # RS/LS, and FM/BM. I find the length from each spline point to the - # straight line connecting the first and last point. If these lengths - # are the same, then it is this special case and return True. - beginPointList = beginLocation.GetListOfPoints() - endPointList = endLocation.GetListOfPoints() - if ((len(beginPointList) == 2)\ - or (len(endPointList) == 2)\ - or (len(beginPointList) != len(endPointList))): - return False - i = 1 - while (i < (len(beginPointList) - 1)): - valsBegin = self._CalcLengthsHelper(beginLocation, i) - valsEnd = self._CalcLengthsHelper(endLocation, i) - if ((valsBegin[0] != valsEnd[0]) or (valsBegin[1] != valsEnd[1])): - return False - i = i + 1 - return True - - # Takes in a RankLocation and a number, namely the point number of which - # you wish to learn the length of. Assume that the RankLocation looks - # like: - # . - # / \ - # / \ - # . \ . - # \ / - # \ / - # . - - # This method should calculate the following lengths: (the added lines): - - # . - # /|\ - # / | \ - # . - \ - . - # \ | / - # \|/ - # . - - # This helps to determine if one (possibly curved) RankLocation is a - # translation of another, meaning you can get from one to another - # using just Expand, Condense, GT, PW, RS/LS, FM/BM - - # TODO(astory): should be moved to a util since it doesn't use self. - # TODO(astory): figure out what t is - def _CalcLengthsHelper(self, location, number): - """Calculates the length between the endpoints of the location""" - pointList = location.GetListOfPoints() - x0 = pointList[0].x - y0 = pointList[0].y - x1 = pointList[-1].x - y1 = pointList[-1].y - xp = pointList[number].x - yp = pointList[number].y - t = (((xp - x0)*(x1 - x0))\ - + ((yp - y0)*(y1 - y0)))/(((y1 - y0)*(y1 - y0))\ - + ((x1 - x0)*(x1 - x0))) - yt = y0 + t*(y1 - y0) - xt = x0 + t*(x1 - x0) - pointLineLength = math.sqrt(((xp - xt)*(xp - xt))\ - + ((yp - yt)*(yp - yt))) - return[pointLineLength, t] - - # Algorithm: - # If you go from straight rank to straight rank, do the following: - # Expand/Condense to be the correct length - # GT/PW to be the correct orientation - # RS/LS - # FM/BM - # Use a heuristic to figure out whether to Condense0 or Condense1, - # Same with GT/PW. - def _GenerateCommandListStraightLine(self, beginLocation, endLocation,\ - length, commandListSoFar): - begin0 = beginLocation.GetListOfPoints()[0] #(Brady) "Left" end - begin1 = beginLocation.GetListOfPoints()[-1] #(Brady) "Right" end (i think...) - end0 = endLocation.GetListOfPoints()[0] - end1 = endLocation.GetListOfPoints()[-1] - endMid = endLocation.GetMidPoint() - beginMid = beginLocation.GetMidPoint() - #LMD change to round - beginLength = int(round(math.sqrt(\ - (begin1.x - begin0.x)**2 + (begin1.y - begin0.y)**2))) - #LMD change to round - endLength = int(round(math.sqrt(\ - (end1.x - end0.x)**2 + (end1.y - end0.y)**2))) - #(Brady) Pretty sure this assumes that in usually ranks are 16 steps long (which is generally true) - if (beginLength != 16): - newMid1x = (begin1.x - begin0.x)*(8/beginLength) + begin0.x - newMid1y = (begin1.y - begin0.y)*(8/beginLength) + begin0.y - newMid0x = (begin0.x - begin1.x)*(8/beginLength) + begin1.x - newMid0y = (begin0.y - begin1.y)*(8/beginLength) + begin1.y - length0 = math.sqrt((endMid.x - newMid0x)*(endMid.x - newMid0x)\ - + (endMid.y - newMid0y)*(endMid.y - newMid0y)) - length1 = math.sqrt((endMid.x - newMid1x)*(endMid.x - newMid1x)\ - + (endMid.y - newMid1y)*(endMid.y - newMid1y)) - if (beginLength > 16): - if (length0 > length1): - newCommand = Commands.Condense1((beginLength - 16),\ - beginLocation) - else: - newCommand = Commands.Condense0((beginLength - 16),\ - beginLocation) - else: - if (length0 > length1): - newCommand = Commands.Expand1((16 - beginLength),\ - beginLocation) - else: - newCommand = Commands.Expand0((16 - beginLength),\ - beginLocation) - commandListSoFar.append(newCommand) - length = length - newCommand.GetLength() - beginLocation = newCommand.GetEndLocation() - begin0 = beginLocation.GetListOfPoints()[0] - begin1 = beginLocation.GetListOfPoints()[-1] - beginMid = beginLocation.GetMidPoint() - beginLength = 16 - - beginAng = math.atan2((begin1.y - begin0.y),(begin1.x - begin0.x)) - endAng = math.atan2((end1.y - end0.y),(end1.x - end0.x)) - if (beginAng != endAng): - c = math.cos(endAng - beginAng) - s = math.sin(endAng - beginAng) - newMid1x = (c*(beginMid.x - begin0.x) - s*(beginMid.y - begin0.y))\ - + begin0.x - newMid1y = (s*(beginMid.x - begin0.x) + c*(beginMid.y - begin0.y))\ - + begin0.y - newMid0x = (c*(beginMid.x - begin1.x) - s*(beginMid.y - begin1.y))\ - + begin1.x - newMid0y = (s*(beginMid.x - begin1.x) + c*(beginMid.y - begin1.y))\ - + begin1.y -#####################################################################LMD fix - #angDiff is CCW angle change from beginning to end angle - angDiff= endAng-beginAng - if (endAng < beginAng): angDiff=angDiff+360 - if (angDiff<180): stepsPW = angDiff*(16/math.pi) - else: stepsPW = (360-angDiff)*(16/math.pi) - - length0 = math.fabs(endMid.x - newMid0x)\ - + math.fabs(endMid.y - newMid0y) + 2*stepsPW - length1 = math.fabs(endMid.x - newMid1x)\ - + math.fabs(endMid.y - newMid1y) + 2*stepsPW - lengthPW = math.fabs(endMid.x - beginMid.x)\ - + math.fabs(endMid.y - beginMid.y) + stepsPW - - if (angDiff<180): - #CCW - if (lengthPW <= length0): - if (lengthPW <= length1): - newCommand = Commands.PWCCW(stepsPW, beginLocation) - else: - newCommand = Commands.GTCCW1(2*stepsPW, beginLocation) - else: - if (length0 <= length1): - newCommand = Commands.GTCCW0(2*stepsPW, beginLocation) - else: - newCommand = Commands.GTCCW1(2*stepsPW, beginLocation) - else: - #CW - if (lengthPW <= length0): - if (lengthPW <= length1): - newCommand = Commands.PWCW(stepsPW, beginLocation) - else: - newCommand = Commands.GTCW1(2*stepsPW, beginLocation) - else: - if (length0 <= length1): - newCommand = Commands.GTCW0(2*stepsPW, beginLocation) - else: - newCommand = Commands.GTCW1(2*stepsPW, beginLocation) -############################################################################### - commandListSoFar.append(newCommand) - length = length - newCommand.GetLength() - beginLocation = newCommand.GetEndLocation() - begin0 = beginLocation.GetListOfPoints()[0] - begin1 = beginLocation.GetListOfPoints()[-1] - beginMid = beginLocation.GetMidPoint() - - movePoint0 = True - NumSteps0 = math.fabs(end0.x - begin0.x) + math.fabs(end0.y - begin0.y) - NumSteps1 = math.fabs(end1.x - begin1.x) + math.fabs(end1.y - begin1.y) - if (NumSteps0 > NumSteps1): - movePoint0 = False - - if(movePoint0): - if (end0.x > begin0.x): - newCommand = Commands.LeftSlide(\ - end0.x - begin0.x, beginLocation) - commandListSoFar.append(newCommand) - length = length - newCommand.GetLength() - beginLocation = newCommand.GetEndLocation() - begin0 = beginLocation.GetListOfPoints()[0] - begin1 = beginLocation.GetListOfPoints()[-1] - beginMid = beginLocation.GetMidPoint() - elif (end0.x < begin0.x): - newCommand = Commands.RightSlide(\ - begin0.x - end0.x, beginLocation) - commandListSoFar.append(newCommand) - length = length - newCommand.GetLength() - beginLocation = newCommand.GetEndLocation() - begin0 = beginLocation.GetListOfPoints()[0] - begin1 = beginLocation.GetListOfPoints()[-1] - beginMid = beginLocation.GetMidPoint() - if (end0.y > begin0.y): - newCommand = Commands.BackMarch(\ - end0.y - begin0.y, beginLocation) - commandListSoFar.append(newCommand) - length = length - newCommand.GetLength() - beginLocation = newCommand.GetEndLocation() - begin0 = beginLocation.GetListOfPoints()[0] - begin1 = beginLocation.GetListOfPoints()[-1] - beginMid = beginLocation.GetMidPoint() - elif (end0.y < begin0.y): - newCommand = Commands.ForwardMarch(\ - begin0.y - end0.y, beginLocation) - commandListSoFar.append(newCommand) - length = length - newCommand.GetLength() - beginLocation = newCommand.GetEndLocation() - begin0 = beginLocation.GetListOfPoints()[0] - begin1 = beginLocation.GetListOfPoints()[-1] - beginMid = beginLocation.GetMidPoint() - else: - if (end1.x > begin1.x): - newCommand = Commands.LeftSlide(\ - end1.x - begin1.x, beginLocation) - commandListSoFar.append(newCommand) - length = length - newCommand.GetLength() - beginLocation = newCommand.GetEndLocation() - begin0 = beginLocation.GetListOfPoints()[0] - begin1 = beginLocation.GetListOfPoints()[-1] - beginMid = beginLocation.GetMidPoint() - elif (end1.x < begin1.x): - newCommand = Commands.RightSlide(\ - begin1.x - end1.x, beginLocation) - commandListSoFar.append(newCommand) - length = length - newCommand.GetLength() - beginLocation = newCommand.GetEndLocation() - begin0 = beginLocation.GetListOfPoints()[0] - begin1 = beginLocation.GetListOfPoints()[-1] - beginMid = beginLocation.GetMidPoint() - if (end1.y > begin1.y): - newCommand = Commands.BackMarch(\ - end1.y - begin1.y, beginLocation) - commandListSoFar.append(newCommand) - length = length - newCommand.GetLength() - beginLocation = newCommand.GetEndLocation() - begin0 = beginLocation.GetListOfPoints()[0] - begin1 = beginLocation.GetListOfPoints()[-1] - beginMid = beginLocation.GetMidPoint() - elif (end1.y < begin1.y): - newCommand = Commands.ForwardMarch(\ - begin1.y - end1.y, beginLocation) - commandListSoFar.append(newCommand) - length = length - newCommand.GetLength() - beginLocation = newCommand.GetEndLocation() - begin0 = beginLocation.GetListOfPoints()[0] - begin1 = beginLocation.GetListOfPoints()[-1] - beginMid = beginLocation.GetMidPoint() - - if (endLength != 16): - if (endLength < 16): - if (movePoint0): - newCommand = Commands.Condense1(\ - (16 - endLength), beginLocation) - else: - newCommand = Commands.Condense0(\ - (16 - endLength), beginLocation) - else: - if (movePoint0): - newCommand = Commands.Expand1(\ - (endLength - 16), beginLocation) - else: - newCommand = Commands.Expand0(\ - (endLength - 16), beginLocation) - commandListSoFar.append(newCommand) - length = length - newCommand.GetLength() - beginLocation = newCommand.GetEndLocation() - begin0 = beginLocation.GetListOfPoints()[0] - begin1 = beginLocation.GetListOfPoints()[-1] - beginMid = beginLocation.GetMidPoint() - beginLength = 16 - - return (commandListSoFar, length, beginLocation) - - def GetLabelLocation(self): - """Returns the location of this rank's label""" - return self._labelLocation - - def SwitchLabelLocation(self): - """Twiddle the location of this rank's label""" - self._labelLocation = not self._labelLocation - - def SwitchEndpoints(self): - """Switch the endpoints""" - self._endLocation.SwitchEndpoints() - self.UpdateCommandList() - if (self.GetFollowing() is not None): - self.GetFollowing().UpdateCommandList() +import copy +import math +import Commands +import Point +import pprint + +# matches command names to function references to call in AddBasicCommand() +commandHash = {"MarkTime": Commands.MarkTime, "MT": Commands.MarkTime, \ + "ForwardMarch":Commands.ForwardMarch, "FM":Commands.ForwardMarch, + "BackMarch":Commands.BackMarch, "BM":Commands.BackMarch, \ + "RightSlide":Commands.RightSlide, "RS":Commands.RightSlide, + "LeftSlide":Commands.LeftSlide, "LS":Commands.LeftSlide, \ + "GTCCW0":Commands.GTCCW0, "GTCW0":Commands.GTCW0, \ + "GTCCW1":Commands.GTCCW1, "GTCW1":Commands.GTCW1, \ + "PWCCW":Commands.PWCCW, "PWCW":Commands.PWCW, \ + "Expand0":Commands.Expand0, "Exp0":Commands.Expand0, \ + "Expand1":Commands.Expand1, "Exp1":Commands.Expand1,\ + "Condense0":Commands.Condense0, "Cond0":Commands.Condense0, \ + "Condense1":Commands.Condense1, "Cond1":Commands.Condense1,\ + "Flatten":Commands.Flatten, "Flat":Commands.Flatten} + +# TODO(Brady) Have an option to display (and to edit) the information of a rank +# such as length (steps), rotation (degrees hor/vert), instrument, etc. +# Also make rank labels draggable (fairly minor importance) +class Rank(object): + def __init__(self, endLocation, move): + self._endLocation = endLocation + self._id = move.GetRankIDGen().GetID() + self._name = None + self._commandList = [] + #TODO(brady): try keeping just one rank for the whole song to help + #carry changes through. Perhaps keep a basic version of this class + #for each move but make them a child of the rank for whole song. + self._move = move + # TODO(astory): why isn't this called 'locked'? + self.hold = False + self.grabbed = False + self.grabbedPoint = None + self.instrument = None + self.listOfSelectedCommandNumbers = [] + self._labelLocation = True + + + def GetName(self): + """Simply returns the name of the rank.""" + return self._name + + + def SetName(self, name): + """Sets this rank's _name field. Also, auto-generates the command list + (if applicable).""" + self._name = name + self.UpdateCommandList() + if (self.GetFollowing() is not None): + self.GetFollowing().UpdateCommandList() + + + + def GetID(self): + """Returns the rank's _id.""" + return self._id + + def GetCommandList(self): + """Returns a copy of the rank's Command List.""" + return copy.deepcopy(self._commandList) + + def SetCommandList(self, commandList): + """Set the rank's command list""" + self._commandList = commandList + + + # The next few methods are used for grabbing a rank and moving it. + # Necessary because although a rank's _endLocation will be changing as you + # drag it around (so that it can be seen to move), there's no need to + # re-generate the command list every time. + def RankGrabbed(self): + """Start grabbing a rank""" + self.grabbed = True + def RankDragged(self, dx, dy): + """Move the rank by dx and dy and update the list of points, but don't + update the command list""" + i = 0 + listOfPoints = self._endLocation.GetListOfPoints() + newPoints = [] + l = len(listOfPoints) + #Perhaps use list comprehension? + #newPoints = [Point.Point(p.x + dx, p.y +dy) for p in listOfPoints] + while (i < l): + newPoints.append(\ + Point.Point(listOfPoints[i].x + dx, listOfPoints[i].y + dy)) + i = i + 1 + self._endLocation.SetListOfPoints(\ + newPoints, self._endLocation.GetListOfSlopes()) + def RankDropped(self): + """Stop grabbing a rank and update the command list for its new + location""" + self.grabbed = False + self.UpdateCommandList() + if (self.GetFollowing() is not None): + self.GetFollowing().UpdateCommandList() + + # Similar to above. However, unless the rank is a straight line, it will + # need to be re-splined every timestep (again for drawing purposes.) + # This should be ok; the spline algorithm takes time linear in the number + # of spline points. + # TODO(astory): storing the grabbed point in Rank seems like bad design, it + # would be better to have the user of these functions store the point and + # manipulate them that way; grabbed would need to be a counter + def PointGrabbed(self, number): + """Start grabbing a (spline) point""" + self.grabbed = True + self.grabbedPoint = number + def PointDragged(self, dx, dy): + """Move a point by dx and dy, but don't update the command list for its + rank""" + listOfPoints = self._endLocation.GetListOfPoints() + listOfPoints[self.grabbedPoint] =\ + Point.Point(listOfPoints[self.grabbedPoint].x + dx,\ + listOfPoints[self.grabbedPoint].y + dy) + self._endLocation.SetListOfPoints(\ + listOfPoints, self._endLocation.GetListOfSlopes()) + def PointDropped(self): + """Stop dragging a point and update the command list for the new + location""" + self.grabbed = False + self.grabbedPoint = None + self.UpdateCommandList() + if (self.GetFollowing() is not None): + self.GetFollowing().UpdateCommandList() + + # When this is called, check to see if Rank.hold == true and if the + # prior move has a rank of the same name. If not, go ahead + # and auto-generate the command list. + def UpdateCommandList(self): + """If this rank is in the previous move, and the rank is not locked, + regenerate the command list. If it is locked, update the command list + for the new location, but don't change the commands.""" + if (self.GetPrior() is not None): + if (self.hold): + i = 0 + l = len(self._commandList) + priorLocation = self.GetPrior().GetEndLocation() + while (i < l): + self._commandList[i].SnapEndLocation(priorLocation) + priorLocation = self._commandList[i].GetEndLocation() + i = i + 1 + self.FixTrailingMT() + else: + self._commandList =\ + self._GenerateCommandList(self.GetPrior().GetEndLocation(),\ + self._endLocation, self._move.GetLength()) + else: + self._commandList = [] + + def FixTrailingMT(self): + """Merge duplicate trailing mark times, and fill in space to end of move + with a single mark time""" + # TODO(astory): this method is written poorly. Fix. + # TODO(astory): make general so that we can use whatever end-move + # command we want + l = len(self._commandList) + # if there are commands + if l > 0: + # if the last one is MT + if (self._commandList[l - 1].GetName() == 'MT'): + i = l - 1 + # go backwards until you run out of commands or it's not a MT + while ((i >= 0) and (self._commandList[i].GetName() == 'MT')): + i = i - 1 + # if you didn't reach the beginning + if (i >= 0): + # reset to the first end MT + i = i + 1 + # remove all the trailing MTs + while (len(self._commandList) > i): + self._commandList.pop() + else: # we reached the beginning + # slap on a mark time for the right length + self._commandList =\ + [Commands.MarkTime(self._move.GetLength(),\ + self._commandList[-1].GetEndLocation())] + # Add a marktime to the end to fill in the gaps + total = self.CalculateTotalCountsOfCommands() + if (total < self._move.GetLength()): + self._commandList.append(\ + Commands.MarkTime(\ + self._move.GetLength() - total,\ + self._commandList[-1].GetEndLocation())) + + def GetPrior(self): + """Returns this rank from the previous move if it exists, else, None""" + if (self._name is None): + return None + p = self._move.GetPrior() + if (p is None): + return None + return (p.LookUpName(self._name)) + + #TODO(Brady) This whole framework of matching positions to the next move seems + #wierd and buggy. try to keep locations in current move, or connect some other way? + def GetFollowing(self): + """Returns this rank from the next move if it exists, else, None""" + if (self._name is None): + return None + p = self._move.GetFollowing() + if (p is None): + return None + return (p.LookUpName(self._name)) + + + # Starting from the prior Rank's _endLocation, go through each Command in + # the Command List, generating the end location of each, until you've used + # up enough counts to hit the given count value. Note that this may not be + # the entire command list. This doesn't actually make any permanent + # changes. + + # Return None if you can't calcuclate the location (because of no + # command list) + def GetCalculatedLocation(self, count): + """Returns this rank's location at count count of the move, which may + not be the end location if count < the number of counts of commands""" + priorRank = self.GetPrior() + if (priorRank is None): + return self.GetEndLocation() + # TODO(astory): this should be condensible, do so after writing tests + if (len(self._commandList) == 0): + return self.GetEndLocation() + if (count > self.CalculateTotalCountsOfCommands()): + return self.GetEndLocation() + curLocation = priorRank.GetEndLocation() + i = 0 + countsLeft = count + while (countsLeft > 0): + curCommand = self._commandList[i] + # TODO(astory): consider speeding up by adding whole length of + # command if it fits in our count total + if (countsLeft >= curCommand.GetLength()): + curLocation = curCommand.CalcLocation(curCommand.GetLength(), curLocation) + else: + curLocation = curCommand.CalcLocation(countsLeft, curLocation) + countsLeft = countsLeft - curCommand.GetLength() + i = i + 1 + return curLocation + + def GetEndLocation(self): + """Returns the rank's final location""" + return copy.deepcopy(self._endLocation) + + # TODO(astory): this might be superfluous since GetCalculatedLocation never + # returns none AFAICT + def GetLocationAtCount(self, count): + """Returns the rank's location at the given count""" + loc = self.GetCalculatedLocation(count) + if (loc is None): + loc = self.GetEndLocation() + return loc + + + # TODO(astory): clean + # Used when a rank is moved around. If self.hold == false and + # self.grabbed == false, re-generate the command list. + + # Should never be called! +# def SetEndLocation(self, newEndLocation): +# pass + + + + # Adds the given command to the command list in the desired location + # Number should be the number in the command list that the new command + # will occupy. So, if you're inserting before the fourth command, + # number should be 3 (because it's 0-indexed.) + def AddBasicCommand(self, commandName, number, length, name): + """Insert a command of type commandName at index number of the command + list, for length counts with name name, but only if the rank is locked, + and this rank is represented in a previous command""" + if ((not self.hold) or (self.GetPrior() is None)): + return + if (number == 0): + beginLocation = self.GetPrior().GetEndLocation() + else: + beginLocation = self._commandList[number - 1].GetEndLocation() + try: + newCommand = commandHash[commandName](length, beginLocation) + except KeyError: + # assumes command line is open. Rank doesn't have access to the GUI + # to make a dialog box. + print("Unknown command: " + str(commandName)) + + self._commandList.insert(number, newCommand) + if (name is not None): + newCommand.SetName(name) + self.UpdateCommandList() + + # TODO(astory): merge into the previous with optional arguments + def AddDTP(self, number, length, name, endLocation): + """Add a DTP, which requires an end location as well""" + if ((not self.hold) or (self.GetPrior() is None)): + return + if (number == 0): + beginLocation = self.GetPrior().GetEndLocation() + else: + beginLocation = self._commandList[number - 1].GetEndLocation() + newCommand = Commands.DTP(length, beginLocation, endLocation) + self._commandList.insert(number, newCommand) + if (name is not None): + newCommand.SetName(name) + self.UpdateCommandList() + + # TODO(astory): merge into the previous with optional arguments + def AddCurve(self, number, length, name, endLocation): + """Add a curve, which requires an end location""" + if ((not self.hold) or (self.GetPrior() is None)): + return + if (number == 0): + beginLocation = self.GetPrior().GetEndLocation() + else: + beginLocation = self._commandList[number - 1].GetEndLocation() + newCommand = Commands.Curve(length, beginLocation, endLocation) + self._commandList.insert(number, newCommand) + if (name is not None): + newCommand.SetName(name) + self.UpdateCommandList() + + # TODO(astory): merge into the previous with optional arguments + def AddFTA1(self, number, length, name, endLocation, listOfFTAPoints): + """Add an FTA at 1""" + if ((not self.hold) or (self.GetPrior() is None)): + return + if (number == 0): + beginLocation = self.GetPrior().GetEndLocation() + else: + beginLocation = self._commandList[number - 1].GetEndLocation() + newCommand = Commands.FTA1(length, beginLocation, endLocation, listOfFTAPoints) + self._commandList.insert(number, newCommand) + if (name is not None): + newCommand.SetName(name) + self.UpdateCommandList() + + # TODO(astory): merge into the previous with optional arguments + def AddFTA0(self, number, length, name, endLocation, listOfFTAPoints): + """Add an FTA at 0""" + if ((not self.hold) or (self.GetPrior() is None)): + return + if (number == 0): + beginLocation = self.GetPrior().GetEndLocation() + else: + beginLocation = self._commandList[number - 1].GetEndLocation() + newCommand = Commands.FTA0(length, beginLocation, endLocation, listOfFTAPoints) + self._commandList.insert(number, newCommand) + if (name is not None): + newCommand.SetName(name) + self.UpdateCommandList() + + def MoveCommandUp(self, commandNumber): + """Move commandNumber command up the command list""" + if ((not self.hold) or (self.GetPrior() is None) or (commandNumber == 0)): + return + command = self._commandList.pop(commandNumber) + self._commandList.insert(commandNumber - 1, command) + self.UpdateCommandList() + + def MoveCommandDown(self, commandNumber): + """Move commandNumber command down the command list""" + if ((not self.hold) or (self.GetPrior() is None)\ + or (commandNumber == (len(self._commandList) - 1))): + return + command = self._commandList.pop(commandNumber) + self._commandList.insert(commandNumber + 1, command) + self.UpdateCommandList() + + def DeleteCommand(self, commandNumber): + """Remove commandNumber command""" + if ((not self.hold) or (self.GetPrior() is None)): + return + self._commandList.pop(commandNumber) + tot = 0 + i = 0 + while (i < len(self._commandList)): + tot = tot + self._commandList[i].GetLength() + i = i + 1 + self.UpdateCommandList() + # TODO(astory): clean up +# if (tot < self._move.GetLength()): +# if (self._commandList[-1].GetName() == "MarkTime"): +# tempLength = self._commandList[-1].GetLength() +# self._commandList.pop(len(self._commandList) - 1) +# self.AddBasicCommand("MarkTime", len(self._commandList), (self._move.GetLength() - tot + tempLength), "MarkTime") +# else: +# self.AddBasicCommand("MarkTime", len(self._commandList), (self._move.GetLength() - tot), "MarkTime") + + def ReNameCommand(self, commandNumber, newName): + """Change the name of commandNumber command to newName""" + if ((not self.hold) or (self.GetPrior() is None)): + return + self._commandList[commandNumber].SetName(newName) + + def SplitCommand(self, commandNumber, count): + """Split commandNumber command at count""" + if ((not self.hold) or (self.GetPrior() is None)): + return + commandToSplit = self._commandList.pop(commandNumber) + if (commandNumber == 0): + newCommands = commandToSplit.Split(\ + count, self.GetPrior().GetEndLocation()) + else: + newCommands = commandToSplit.Split(\ + count, self._commandList[commandNumber - 1].GetEndLocation()) + self._commandList.insert(commandNumber, newCommands[1]) + self._commandList.insert(commandNumber, newCommands[0]) + + def MergeCommands(self, firstCommandNumber): + """Merge firstCommandNumber and the following command together""" + if ((not self.hold) or (self.GetPrior() is None)): + return + if ((len(self._commandList)) > firstCommandNumber + 1): + return + firstCommand = self._commandList[firstCommandNumber] + secondCommand = self._commandList[secondCommandNumber] + newCommand = firstCommand.MergeWithFollowing(secondCommand) + if (newCommand is not None): + self._commandList.pop(firstCommandNumber) + self._commandList[firstCommandNumber] = newCommand + + # TODO(astory): why do we need this? + def GetCalculatedBeginLocation(self, frontCount): + """Calculate the beginning location from the end location and the + commands""" + count = self._move.GetLength() - frontCount + tot = 0 + i = 0 + resid = 0 + while (tot < count): + curLength = self._commandList[i].GetLength() + if (curLength + tot >= count): + resid = count - tot + else: + i = i + 1 + tot = tot + curLength + curLocation = self._endLocation + curLocation = self._commandList[i].CalcBeginLocation(resid, curLocation) + i = i - 1 + while (i >= 0): + curLocation =\ + self._commandList[i].CalcBeginLocation(\ + self._commandList[i].GetLength(), curLocation) + i = i - 1 + return curLocation + + def AddSplinePoint(self, number): + """Add a spline point to the rank after point number. Does not add a + spline point after the final point""" + listOfPoints = self._endLocation.GetListOfPoints() + if (number == len(listOfPoints) - 1): + return + p = self._endLocation.GetPointAtT(0.5, number) + listOfPoints.insert(number + 1, p) + self._endLocation.SetListOfPoints(listOfPoints, None) + + def DeleteSplinePoint(self, number): + """Removes number point if it is not one of the last two""" + listOfPoints = self._endLocation.GetListOfPoints() + if (len(listOfPoints) > 2): + listOfPoints.pop(number) + self._endLocation.SetListOfPoints(listOfPoints, None) + + # TODO(astory): this is never used + def DeleteAllSplinePoints(self): + """Removes all points except the two end points""" + listOfPoints = self._endLocation.GetListOfPoints() + newList = (listOfPoints[0], listOfPoints[-1]) + self._endLocation.SetListOfPoints(newList, None) + + def SnapEndLocation(self): + """Set each command's end location and the rank's end location to as + calculated based on the command and the previous move""" + if (self.GetPrior() is None): + return + self._endLocation = self.GetCalculatedLocation(self._move.GetLength()) + self.UpdateCommandList() + + # Starting from self._endLocation, work backwards through the command list. + # Snap each command to the requisite beginning location, and snap the + # _endLocation of the rank of the prior move as well. + def SnapBeginLocation(self): + """Set each command's beginning location and the rank's end location to + as calculated based on the command and the previous move""" + prior = self.GetPrior() + if (self.GetPrior() is None): + return + prior._endLocation = self.GetCalculatedBeginLocation(0) + prior.UpdateCommandList() + + # TODO(astory): convert to wrappers to a parameterized function + def SetStraight(self): + """Set the rank and all of its commands to be non-curved""" + self._endLocation.SetCurved(False) + i = 0 + while (i < len(self._commandList)): + self._commandList[i].GetEndLocation().SetCurved(False) + i = i + 1 + + def SetCurved(self): + """Set the rank and all of its commands to be curved""" + self._endLocation.SetCurved(True) + i = 0 + while (i < len(self._commandList)): + self._commandList[i].GetEndLocation().SetCurved(True) + i = i + 1 + + # TODO(astory): rewrite as a list comprehension + def CalculateCountFromCommandNumber(self, commandNumber): + """Returns the count at which commandNumber command starts""" + if (commandNumber == 0): + return 0 + i = 0 + accum = 0 + while (i < commandNumber): + accum = accum + self._commandList[i].GetLength() + i = i + 1 + return accum + + # TODO(astory): rewrite as a list comprehension + def CalculateTotalCountsOfCommands(self): + """Return the sum of lengths of the commands""" + i = 0 + accum = 0 + while (i < len(self._commandList)): + accum = accum + self._commandList[i].GetLength() + i = i + 1 + return accum + + # TODO(astory): break into own file? + def _GenerateCommandList(self, beginLocation, endLocation, length): + """Generate a list of commands to take the rank from beginLocation to + endLocation in length counts""" + # The algorithm: + # First, check to see if this is a 'special case'. If so, even if the + # rank is curved, it can be treated as a straight line. Otherwise, + # flatten the rank. The number of counts taken is equal to the length + # of the furthest spline point from that straight line. Then, use the + # straight line generator to get form one straight line to another, and + # then recurve, if nececssary. As a final step, append a MarkTime. + # This step may be able to be skipped, as the UpdateCommandList + # function, the only thing calling this, should append it automatically + # if necessary. + if (self._IsSpecialCaseCMDGen(beginLocation, endLocation)): + return self._GenerateCommandListStraightLine(\ + beginLocation, endLocation, length, [])[0] + commandListSoFar = [] + beginPointList = beginLocation.GetListOfPoints() + endPointList = endLocation.GetListOfPoints() + if (not beginLocation.IsStraight()): + i = 1 + lengthsBeginMax = 0 + while (i < (len(beginPointList) - 1)): + valsBegin = self._CalcLengthsHelper(beginLocation, i) + lengthsBeginMax = max(lengthsBeginMax, valsBegin[0]) + i = i + 1 + newCommand = Commands.Flatten(lengthsBeginMax, beginLocation) + commandListSoFar.append(newCommand) + length = length - newCommand.GetLength() + beginLocation = newCommand.GetEndLocation() + + # TODO(astory): assign into tuple + commandListSoFarTuple =\ + self._GenerateCommandListStraightLine(\ + beginLocation,\ + endLocation,\ + length,\ + commandListSoFar) + commandListSoFar = commandListSoFarTuple[0] + length = commandListSoFarTuple[1] + beginLocation = commandListSoFarTuple[2] + + if (not endLocation.IsStraight()): + i = 1 + lengthsEndMax = 0 + while (i < (len(endPointList) - 1)): + valsEnd = self._CalcLengthsHelper(endLocation, i) + lengthsEndMax = max(lengthsEndMax, valsEnd[0]) + i = i + 1 + newCommand = Commands.Curve(lengthsEndMax, beginLocation, endLocation) + commandListSoFar.append(newCommand) + length = length - newCommand.GetLength() + beginLocation = newCommand.GetEndLocation() + + if (length > 0): + newCommand = Commands.MarkTime(length, endLocation) + commandListSoFar.append(newCommand) + length = length - newCommand.GetLength() + + return commandListSoFar + + def _IsSpecialCaseCMDGen(self, beginLocation, endLocation): + """Return true if expand, condense, GT, PW, and slides are enough to + perform the relocation""" + # The purpose of this method is to determine if a rank can be gotten + # from one RankLocation to another with just Expand, Condense, GT, PW, + # RS/LS, and FM/BM. I find the length from each spline point to the + # straight line connecting the first and last point. If these lengths + # are the same, then it is this special case and return True. + beginPointList = beginLocation.GetListOfPoints() + endPointList = endLocation.GetListOfPoints() + if ((len(beginPointList) == 2)\ + or (len(endPointList) == 2)\ + or (len(beginPointList) != len(endPointList))): + return False + i = 1 + while (i < (len(beginPointList) - 1)): + valsBegin = self._CalcLengthsHelper(beginLocation, i) + valsEnd = self._CalcLengthsHelper(endLocation, i) + if ((valsBegin[0] != valsEnd[0]) or (valsBegin[1] != valsEnd[1])): + return False + i = i + 1 + return True + + # Takes in a RankLocation and a number, namely the point number of which + # you wish to learn the length of. Assume that the RankLocation looks + # like: + # . + # / \ + # / \ + # . \ . + # \ / + # \ / + # . + + # This method should calculate the following lengths: (the added lines): + + # . + # /|\ + # / | \ + # . - \ - . + # \ | / + # \|/ + # . + + # This helps to determine if one (possibly curved) RankLocation is a + # translation of another, meaning you can get from one to another + # using just Expand, Condense, GT, PW, RS/LS, FM/BM + + # TODO(astory): should be moved to a util since it doesn't use self. + # TODO(astory): figure out what t is + def _CalcLengthsHelper(self, location, number): + """Calculates the length between the endpoints of the location""" + pointList = location.GetListOfPoints() + x0 = pointList[0].x + y0 = pointList[0].y + x1 = pointList[-1].x + y1 = pointList[-1].y + xp = pointList[number].x + yp = pointList[number].y + t = (((xp - x0)*(x1 - x0))\ + + ((yp - y0)*(y1 - y0)))/(((y1 - y0)*(y1 - y0))\ + + ((x1 - x0)*(x1 - x0))) + yt = y0 + t*(y1 - y0) + xt = x0 + t*(x1 - x0) + pointLineLength = math.sqrt(((xp - xt)*(xp - xt))\ + + ((yp - yt)*(yp - yt))) + return[pointLineLength, t] + + # Algorithm: + # If you go from straight rank to straight rank, do the following: + # Expand/Condense to be the correct length + # GT/PW to be the correct orientation + # RS/LS + # FM/BM + # Use a heuristic to figure out whether to Condense0 or Condense1, + # Same with GT/PW. + def _GenerateCommandListStraightLine(self, beginLocation, endLocation,\ + length, commandListSoFar): + begin0 = beginLocation.GetListOfPoints()[0] #(Brady) "Left" end + begin1 = beginLocation.GetListOfPoints()[-1] #(Brady) "Right" end (i think...) + end0 = endLocation.GetListOfPoints()[0] + end1 = endLocation.GetListOfPoints()[-1] + endMid = endLocation.GetMidPoint() + beginMid = beginLocation.GetMidPoint() + #LMD change to round + beginLength = int(round(math.sqrt(\ + (begin1.x - begin0.x)**2 + (begin1.y - begin0.y)**2))) + #LMD change to round + endLength = int(round(math.sqrt(\ + (end1.x - end0.x)**2 + (end1.y - end0.y)**2))) + #(Brady) Pretty sure this assumes that in usually ranks are 16 steps long (which is generally true) + #however should allow for nonstandard lengths + if (beginLength != 16): + newMid1x = (begin1.x - begin0.x)*(8/beginLength) + begin0.x + newMid1y = (begin1.y - begin0.y)*(8/beginLength) + begin0.y + newMid0x = (begin0.x - begin1.x)*(8/beginLength) + begin1.x + newMid0y = (begin0.y - begin1.y)*(8/beginLength) + begin1.y + length0 = math.sqrt((endMid.x - newMid0x)*(endMid.x - newMid0x)\ + + (endMid.y - newMid0y)*(endMid.y - newMid0y)) + length1 = math.sqrt((endMid.x - newMid1x)*(endMid.x - newMid1x)\ + + (endMid.y - newMid1y)*(endMid.y - newMid1y)) + if (beginLength > 16): + if (length0 > length1): + newCommand = Commands.Condense1((beginLength - 16),\ + beginLocation) + else: + newCommand = Commands.Condense0((beginLength - 16),\ + beginLocation) + else: + if (length0 > length1): + newCommand = Commands.Expand1((16 - beginLength),\ + beginLocation) + else: + newCommand = Commands.Expand0((16 - beginLength),\ + beginLocation) + commandListSoFar.append(newCommand) + length = length - newCommand.GetLength() + beginLocation = newCommand.GetEndLocation() + begin0 = beginLocation.GetListOfPoints()[0] + begin1 = beginLocation.GetListOfPoints()[-1] + beginMid = beginLocation.GetMidPoint() + beginLength = 16 + + beginAng = math.atan2((begin1.y - begin0.y),(begin1.x - begin0.x)) + endAng = math.atan2((end1.y - end0.y),(end1.x - end0.x)) + if (beginAng != endAng): + c = math.cos(endAng - beginAng) + s = math.sin(endAng - beginAng) + newMid1x = (c*(beginMid.x - begin0.x) - s*(beginMid.y - begin0.y))\ + + begin0.x + newMid1y = (s*(beginMid.x - begin0.x) + c*(beginMid.y - begin0.y))\ + + begin0.y + newMid0x = (c*(beginMid.x - begin1.x) - s*(beginMid.y - begin1.y))\ + + begin1.x + newMid0y = (s*(beginMid.x - begin1.x) + c*(beginMid.y - begin1.y))\ + + begin1.y +#####################################################################LMD fix + #angDiff is CCW angle change from beginning to end angle + angDiff= endAng-beginAng + if (endAng < beginAng): angDiff=angDiff+360 + if (angDiff<180): stepsPW = angDiff*(16/math.pi) + else: stepsPW = (360-angDiff)*(16/math.pi) + + length0 = math.fabs(endMid.x - newMid0x)\ + + math.fabs(endMid.y - newMid0y) + 2*stepsPW + length1 = math.fabs(endMid.x - newMid1x)\ + + math.fabs(endMid.y - newMid1y) + 2*stepsPW + lengthPW = math.fabs(endMid.x - beginMid.x)\ + + math.fabs(endMid.y - beginMid.y) + stepsPW + + if (angDiff<180): + #CCW + if (lengthPW <= length0): + if (lengthPW <= length1): + newCommand = Commands.PWCCW(stepsPW, beginLocation) + else: + newCommand = Commands.GTCCW1(2*stepsPW, beginLocation) + else: + if (length0 <= length1): + newCommand = Commands.GTCCW0(2*stepsPW, beginLocation) + else: + newCommand = Commands.GTCCW1(2*stepsPW, beginLocation) + else: + #CW + if (lengthPW <= length0): + if (lengthPW <= length1): + newCommand = Commands.PWCW(stepsPW, beginLocation) + else: + newCommand = Commands.GTCW1(2*stepsPW, beginLocation) + else: + if (length0 <= length1): + newCommand = Commands.GTCW0(2*stepsPW, beginLocation) + else: + newCommand = Commands.GTCW1(2*stepsPW, beginLocation) +############################################################################### + commandListSoFar.append(newCommand) + length = length - newCommand.GetLength() + beginLocation = newCommand.GetEndLocation() + begin0 = beginLocation.GetListOfPoints()[0] + begin1 = beginLocation.GetListOfPoints()[-1] + beginMid = beginLocation.GetMidPoint() + + movePoint0 = True + NumSteps0 = math.fabs(end0.x - begin0.x) + math.fabs(end0.y - begin0.y) + NumSteps1 = math.fabs(end1.x - begin1.x) + math.fabs(end1.y - begin1.y) + if (NumSteps0 > NumSteps1): + movePoint0 = False + + if(movePoint0): + if (end0.x > begin0.x): + newCommand = Commands.LeftSlide(\ + end0.x - begin0.x, beginLocation) + commandListSoFar.append(newCommand) + length = length - newCommand.GetLength() + beginLocation = newCommand.GetEndLocation() + begin0 = beginLocation.GetListOfPoints()[0] + begin1 = beginLocation.GetListOfPoints()[-1] + beginMid = beginLocation.GetMidPoint() + elif (end0.x < begin0.x): + newCommand = Commands.RightSlide(\ + begin0.x - end0.x, beginLocation) + commandListSoFar.append(newCommand) + length = length - newCommand.GetLength() + beginLocation = newCommand.GetEndLocation() + begin0 = beginLocation.GetListOfPoints()[0] + begin1 = beginLocation.GetListOfPoints()[-1] + beginMid = beginLocation.GetMidPoint() + if (end0.y > begin0.y): + newCommand = Commands.BackMarch(\ + end0.y - begin0.y, beginLocation) + commandListSoFar.append(newCommand) + length = length - newCommand.GetLength() + beginLocation = newCommand.GetEndLocation() + begin0 = beginLocation.GetListOfPoints()[0] + begin1 = beginLocation.GetListOfPoints()[-1] + beginMid = beginLocation.GetMidPoint() + elif (end0.y < begin0.y): + newCommand = Commands.ForwardMarch(\ + begin0.y - end0.y, beginLocation) + commandListSoFar.append(newCommand) + length = length - newCommand.GetLength() + beginLocation = newCommand.GetEndLocation() + begin0 = beginLocation.GetListOfPoints()[0] + begin1 = beginLocation.GetListOfPoints()[-1] + beginMid = beginLocation.GetMidPoint() + else: + if (end1.x > begin1.x): + newCommand = Commands.LeftSlide(\ + end1.x - begin1.x, beginLocation) + commandListSoFar.append(newCommand) + length = length - newCommand.GetLength() + beginLocation = newCommand.GetEndLocation() + begin0 = beginLocation.GetListOfPoints()[0] + begin1 = beginLocation.GetListOfPoints()[-1] + beginMid = beginLocation.GetMidPoint() + elif (end1.x < begin1.x): + newCommand = Commands.RightSlide(\ + begin1.x - end1.x, beginLocation) + commandListSoFar.append(newCommand) + length = length - newCommand.GetLength() + beginLocation = newCommand.GetEndLocation() + begin0 = beginLocation.GetListOfPoints()[0] + begin1 = beginLocation.GetListOfPoints()[-1] + beginMid = beginLocation.GetMidPoint() + if (end1.y > begin1.y): + newCommand = Commands.BackMarch(\ + end1.y - begin1.y, beginLocation) + commandListSoFar.append(newCommand) + length = length - newCommand.GetLength() + beginLocation = newCommand.GetEndLocation() + begin0 = beginLocation.GetListOfPoints()[0] + begin1 = beginLocation.GetListOfPoints()[-1] + beginMid = beginLocation.GetMidPoint() + elif (end1.y < begin1.y): + newCommand = Commands.ForwardMarch(\ + begin1.y - end1.y, beginLocation) + commandListSoFar.append(newCommand) + length = length - newCommand.GetLength() + beginLocation = newCommand.GetEndLocation() + begin0 = beginLocation.GetListOfPoints()[0] + begin1 = beginLocation.GetListOfPoints()[-1] + beginMid = beginLocation.GetMidPoint() + + if (endLength != 16): + if (endLength < 16): + if (movePoint0): + newCommand = Commands.Condense1(\ + (16 - endLength), beginLocation) + else: + newCommand = Commands.Condense0(\ + (16 - endLength), beginLocation) + else: + if (movePoint0): + newCommand = Commands.Expand1(\ + (endLength - 16), beginLocation) + else: + newCommand = Commands.Expand0(\ + (endLength - 16), beginLocation) + commandListSoFar.append(newCommand) + length = length - newCommand.GetLength() + beginLocation = newCommand.GetEndLocation() + begin0 = beginLocation.GetListOfPoints()[0] + begin1 = beginLocation.GetListOfPoints()[-1] + beginMid = beginLocation.GetMidPoint() + beginLength = 16 + + return (commandListSoFar, length, beginLocation) + + def GetLabelLocation(self): + """Returns the location of this rank's label""" + return self._labelLocation + + def SwitchLabelLocation(self): + """Twiddle the location of this rank's label""" + self._labelLocation = not self._labelLocation + + def SwitchEndpoints(self): + """Switch the endpoints""" + self._endLocation.SwitchEndpoints() + self.UpdateCommandList() + if (self.GetFollowing() is not None): + self.GetFollowing().UpdateCommandList() diff --git a/RankPanda/RankIDGen.py b/RankPanda/RankIDGen.py index 9fa2dd8..354d0a0 100755 --- a/RankPanda/RankIDGen.py +++ b/RankPanda/RankIDGen.py @@ -1,11 +1,11 @@ - - -class RankIDGen(object): - - def __init__(self): - self.nextID = 0 - - def GetID(self): - self.nextID = self.nextID + 1 - return self.nextID - + + +class RankIDGen(object): + + def __init__(self): + self.nextID = 0 + + def GetID(self): + self.nextID = self.nextID + 1 + return self.nextID + diff --git a/RankPanda/RankLocation.py b/RankPanda/RankLocation.py index 63ef584..c8be4ca 100755 --- a/RankPanda/RankLocation.py +++ b/RankPanda/RankLocation.py @@ -1,267 +1,265 @@ -import copy -import Point -import CubicHermiteSpline as CHS -import RankLocation -import Commands -import math - -class RankLocationError(Exception): - """Base class for exceptions in this module""" -class InvalidLocationListError(RankLocationError): - """Exception raised for invalid location lists""" - pass - -def Compare(l1, l2): - """Figure out if two RankLocations represent the same location on the field. - - Simply compare each point of one to each point of the other. - If the slopes are different, it'll still return true. - Slopes should almost always be auto generated; which means that for all - practical purposes this won't affect anything. - """ - if (len(l1._listOfPoints) != len(l2._listOfPoints)): - return False - for (p1, p2) in zip(l1._listOfPoints, l2._listOfPoints): - if p1 != p2: - return False - return True - -def IsListOfPointsLengthZero(pointList): - """Returns true if all the points are on top of each other and the rank - is of length 0. - """ - if not pointList: - return True - p0 = pointList[0] - for p in pointList: - if p != p0: - return False - return True - -class RankLocation(object): - """Class to represent the location of a rank. - - This class represents the location of a rank that can be drawn on the - screen. In part, it serves as an object-oriented wrapper for the - SplineGenerator class. However, it also supports straight-line ranks (only - two points) and ranks that aren't curved/splined (think a zigzag). - """ - - def __init__(self, listOfPoints, curved=True): - """Generate a rank location based on a list of points - - Pass in your list of points upon creation. It'll auto-generate the - slopes; if you'd like to artificially set the slopes list you can do it - later with the SetListOfPoints() function. - """ - self.curved = curved - self._drawingPoints = [] - self._splineFunctions = None - self._listOfSlopes = None - self.SetListOfPoints(listOfPoints, self._listOfSlopes) - - - def SetCurved(self, val): - """Sets if the rank is curved or zigzagged.""" - self.curved = val - self.SetListOfPoints(self._listOfPoints, self._listOfSlopes) - - def GetListOfPoints(self): - return copy.deepcopy(self._listOfPoints) - - def GetListOfDrawingPoints(self): - return self._drawingPoints - - def GetListOfSlopes(self): - return copy.deepcopy(self._listOfSlopes) - - def SetListOfPoints(self, listOfPoints, listOfSlopes): - """Sets the list of points and list of slopes. - - First, we determine if it's a straight line or not, meaning only two - points. - - If None was passed in for the slopes, we generate a slopelist consisting - of Nones, because this is the interface expected by the SplineGenerator - class. - - We then just get the spline functions and drawing points. - - If it's a straight line or a zig-zag, just store the list of points. - The straight lines will be drawn on the GUI end. - """ - if (len(listOfPoints) < 2): - raise InvalidLocationListError("Tried to set a list of %d points" - % len(listOfPoints)) - self._listOfPoints = listOfPoints - - if ((self.curved) and (not self.IsStraight())): - if (listOfSlopes is None): - self._listOfSlopes = [None for _ in listOfPoints] - else: - self._listOfSlopes = listOfSlopes - - self._splineFunctions = \ - CHS.GetSplines(self._listOfPoints, - self._listOfSlopes) - self._drawingPoints = \ - CHS.GetPoints(self._splineFunctions) - else: - self._listOfSlopes = None - self._splineFunctions = None - self._drawingPoints = None - - def IsStraight(self): - """Determine whether the rank is straight: iff there are two points.""" - # TODO(astory): make this be based on collinearity - return len(self._listOfPoints) == 2 - - def Clone(self): - return copy.deepcopy(self) - - def CompareRankLocation(self, l2): - return Compare(self, l2) - - def GetMidPoint(self): - """The midpoint of a line connecting the first and last points.""" - p0 = self._listOfPoints[0] - p1 = self._listOfPoints[len(self._listOfPoints) - 1] - xmid = (p0.x + p1.x)/2 - ymid = (p0.y + p1.y)/2 - return Point.Point(xmid, ymid) - - def _Respline(self): - self._splineFunctions = \ - CHS.GetSplines(self._listOfPoints, - self._listOfSlopes) - - def GetPointAtT(self, t, number): - """Get the number-th point in the rank's location at time t - - A straightforward function - takes in a t value, and a number in the - spline list. Depending on what kind of RankLocation it is, finds the - (x,y) point at that t value. - """ - if (self.IsStraight()): - x = self._listOfPoints[0].x + t*(self._listOfPoints[1].x - self._listOfPoints[0].x) - y = self._listOfPoints[0].y + t*(self._listOfPoints[1].y - self._listOfPoints[0].y) - return Point.Point(x,y) - elif (not self.curved): - pfirst = self._listOfPoints[number] - psecond = self._listOfPoints[number + 1] - x = pfirst.x + t*(psecond.x - pfirst.x) - y = pfirst.y + t*(psecond.y - pfirst.y) - return Point.Point(x,y) - else: - x = CHS.EvalCubic(t, self._splineFunctions[number][0]) - y = CHS.EvalCubic(t, self._splineFunctions[number][1]) - return Point.Point(x,y) - - - def GetInformationAtLengthFraction(self, lengthFraction): - """Return information at a fraction of the length of the rank - - Pass in a fraction of the total length at which you wish to get - information. - If the rank is curved, simply call the equivalent function in the - SplineGenerator. - If not, do basically the same thing that that function does, only it's - easier because everything's a straight line. Find between which two - points the length fraction lies, and then find how far along is needed - to get the requisite length. From there, get x, y, dx, and dy. Return - the same thing that the equivalent function in the SplineGenerator does: - [(x,y), (dx,dy), i]. - """ - - if (not self.IsStraight()): - if (self.curved): - return CHS.GetInformationAtLengthFraction(self._splineFunctions, lengthFraction) - - lengths = self.GetLengths() - totalLength = sum(lengths) - lengthNeeded = lengthFraction * totalLength - - i = 0 - if (lengthNeeded > 0.1): - while (lengthNeeded > 0.1): - lengthNeeded = lengthNeeded - lengths[i] - i = i + 1 - i = i - 1 - lengthNeeded = lengthNeeded + lengths[i] - if (lengthNeeded <= 0.1): - t = 0 - else: - t = lengthNeeded/float(lengths[i]) - x = t*(self._listOfPoints[i + 1].x - self._listOfPoints[i].x) + self._listOfPoints[i].x #SplineGenerator.EvalCubic(t, splineList[i][0]) - y = t*(self._listOfPoints[i + 1].y - self._listOfPoints[i].y) + self._listOfPoints[i].y - dx = (self._listOfPoints[i + 1].x - self._listOfPoints[i].x) - dy = (self._listOfPoints[i + 1].y - self._listOfPoints[i].y) - return [(Point.Point(x, y)),(Point.Point(dx, dy)), i] - - def GetLengthFractions(self): - """Returns the lengths fractions at each point. - - The first point lies at lelngth fraction 0, and the last at length - fraction 1. If a point is exactly in the middle, it's at fraction 0.5. - """ - fracs = [] - lengths = self.GetLengths() - lengthtot = sum(lengths) - i = 0 - fracs.append(0.0) - while (i < len(lengths)): - fracs.append(fracs[i] + lengths[i]/float(lengthtot)) - i = i + 1 - return fracs - - def GetLengths(self): - """Returns the length of each part of the RankLocation. - - Behaves differently depending on RankLocation type. If it's curved, call - the equivalent function in the SplineGenerator. - If not, use the Pythogoran Theorem to find out how long each part is. - """ - if (not self.IsStraight()): - if (self.curved): - return CHS.GetLengths( - self._splineFunctions) - i = 1 - lengths = [] - while (i < len(self._listOfPoints)): - lengths.append( - math.sqrt( - (self._listOfPoints[i - 1].x - self._listOfPoints[i].x) * - (self._listOfPoints[i - 1].x - self._listOfPoints[i].x) + - (self._listOfPoints[i - 1].y - self._listOfPoints[i].y) * - (self._listOfPoints[i - 1].y - self._listOfPoints[i].y))) - i += 1 - return lengths - - - def IsTranslated(self, l2): - """ If the input is a translated version of self, returns the amount - it's been translated by as a Point. Returns input - self. Else, - returns None. - """ - myList = self._listOfPoints - otherList = l2.GetListOfPoints() - if (len(myList) != len(otherList)): - return None - else: - same = True - dx = otherList[0].x - myList[0].x - dy = otherList[0].y - myList[0].y - i = 1 - while ((i < len(myList)) and same): - same = not (((otherList[i].x - myList[i].x) == dx) and ((otherList[i].y - myList[i].y) == dy)) - i = i + 1 - if (same): - return Point.Point(dx, dy) - else: - return None - - def SwitchEndpoints(self): - """Switch the endpoints by reversing the entire list""" - self._listOfPoints.reverse() - self.SetListOfPoints(self._listOfPoints, self._listOfSlopes) - +import copy +import Point +import CubicHermiteSpline as CHS +import RankLocation +import Commands +import math + +class RankLocationError(Exception): + """Base class for exceptions in this module""" +class InvalidLocationListError(RankLocationError): + """Exception raised for invalid location lists""" + pass + +def Compare(l1, l2): + """Figure out if two RankLocations represent the same location on the field. + + Simply compare each point of one to each point of the other. + If the slopes are different, it'll still return true. + Slopes should almost always be auto generated; which means that for all + practical purposes this won't affect anything. + """ + if (len(l1._listOfPoints) != len(l2._listOfPoints)): + return False + for (p1, p2) in zip(l1._listOfPoints, l2._listOfPoints): + if p1 != p2: + return False + return True + +def IsListOfPointsLengthZero(pointList): + """Returns true if all the points are on top of each other and the rank + is of length 0. + """ + if not pointList: + return True + p0 = pointList[0] + for p in pointList: + if p != p0: + return False + return True + +class RankLocation(object): + """Class to represent the location of a rank. + + This class represents the location of a rank that can be drawn on the + screen. In part, it serves as an object-oriented wrapper for the + SplineGenerator class. However, it also supports straight-line ranks (only + two points) and ranks that aren't curved/splined (think a zigzag). + """ + + def __init__(self, listOfPoints, curved=True): + """Generate a rank location based on a list of points + + Pass in your list of points upon creation. It'll auto-generate the + slopes; if you'd like to artificially set the slopes list you can do it + later with the SetListOfPoints() function. + """ + self.curved = curved + self._drawingPoints = [] + self._splineFunctions = None + self._listOfSlopes = None + self.SetListOfPoints(listOfPoints, self._listOfSlopes) + + + def SetCurved(self, val): + """Sets if the rank is curved or zigzagged.""" + self.curved = val + self.SetListOfPoints(self._listOfPoints, self._listOfSlopes) + + def GetListOfPoints(self): + return copy.deepcopy(self._listOfPoints) + + def GetListOfDrawingPoints(self): + return self._drawingPoints + + def GetListOfSlopes(self): + return copy.deepcopy(self._listOfSlopes) + + def SetListOfPoints(self, listOfPoints, listOfSlopes): + """Sets the list of points and list of slopes. + + First, we determine if it's a straight line or not, meaning only two + points. + + If None was passed in for the slopes, we generate a slopelist consisting + of Nones, because this is the interface expected by the SplineGenerator + class. + + We then just get the spline functions and drawing points. + + If it's a straight line or a zig-zag, just store the list of points. + The straight lines will be drawn on the GUI end. + """ + if (len(listOfPoints) < 2): + raise InvalidLocationListError("Tried to set a list of %d points" + % len(listOfPoints)) + self._listOfPoints = listOfPoints + + if ((self.curved) and (not self.IsStraight())): + if (listOfSlopes is None): + self._listOfSlopes = [None for _ in listOfPoints] + else: + self._listOfSlopes = listOfSlopes + + self._splineFunctions = \ + CHS.GetSplines(self._listOfPoints, self._listOfSlopes) + self._drawingPoints = \ + CHS.GetPoints(self._splineFunctions) + else: + self._listOfSlopes = None + self._splineFunctions = None + self._drawingPoints = None + + def IsStraight(self): + """Determine whether the rank is straight: iff there are two points.""" + # TODO(astory): make this be based on collinearity + return len(self._listOfPoints) == 2 + + def Clone(self): + return copy.deepcopy(self) + + def CompareRankLocation(self, l2): + return Compare(self, l2) + + def GetMidPoint(self): + """The midpoint of a line connecting the first and last points.""" + p0 = self._listOfPoints[0] + p1 = self._listOfPoints[len(self._listOfPoints) - 1] + xmid = (p0.x + p1.x)/2 + ymid = (p0.y + p1.y)/2 + return Point.Point(xmid, ymid) + + def _Respline(self): + self._splineFunctions = \ + CHS.GetSplines(self._listOfPoints, + self._listOfSlopes) + + def GetPointAtT(self, t, number): + """Get the number-th point in the rank's location at time t + + A straightforward function - takes in a t value, and a number in the + spline list. Depending on what kind of RankLocation it is, finds the + (x,y) point at that t value. + """ + if (self.IsStraight()): + x = self._listOfPoints[0].x + t*(self._listOfPoints[1].x - self._listOfPoints[0].x) + y = self._listOfPoints[0].y + t*(self._listOfPoints[1].y - self._listOfPoints[0].y) + return Point.Point(x,y) + elif (not self.curved): + pfirst = self._listOfPoints[number] + psecond = self._listOfPoints[number + 1] + x = pfirst.x + t*(psecond.x - pfirst.x) + y = pfirst.y + t*(psecond.y - pfirst.y) + return Point.Point(x,y) + else: + x = CHS.EvalCubic(t, self._splineFunctions[number][0]) + y = CHS.EvalCubic(t, self._splineFunctions[number][1]) + return Point.Point(x,y) + + + def GetInformationAtLengthFraction(self, lengthFraction): + """Return information at a fraction of the length of the rank + + Pass in a fraction of the total length at which you wish to get + information. + If the rank is curved, simply call the equivalent function in the + SplineGenerator. + If not, do basically the same thing that that function does, only it's + easier because everything's a straight line. Find between which two + points the length fraction lies, and then find how far along is needed + to get the requisite length. From there, get x, y, dx, and dy. Return + the same thing that the equivalent function in the SplineGenerator does: + [(x,y), (dx,dy), i]. + """ + + if (not self.IsStraight()): + if (self.curved): + return CHS.GetInformationAtLengthFraction(self._splineFunctions, lengthFraction) + + lengths = self.GetLengths() + totalLength = sum(lengths) + lengthNeeded = lengthFraction * totalLength + + i = 0 + if (lengthNeeded > 0.1): + while (lengthNeeded > 0.1): + lengthNeeded = lengthNeeded - lengths[i] + i = i + 1 + i = i - 1 + lengthNeeded = lengthNeeded + lengths[i] + if (lengthNeeded <= 0.1): + t = 0 + else: + t = lengthNeeded/float(lengths[i]) + x = t*(self._listOfPoints[i + 1].x - self._listOfPoints[i].x) + self._listOfPoints[i].x #SplineGenerator.EvalCubic(t, splineList[i][0]) + y = t*(self._listOfPoints[i + 1].y - self._listOfPoints[i].y) + self._listOfPoints[i].y + dx = (self._listOfPoints[i + 1].x - self._listOfPoints[i].x) + dy = (self._listOfPoints[i + 1].y - self._listOfPoints[i].y) + return [(Point.Point(x, y)),(Point.Point(dx, dy)), i] + + def GetLengthFractions(self): + """Returns the lengths fractions at each point. + + The first point lies at lelngth fraction 0, and the last at length + fraction 1. If a point is exactly in the middle, it's at fraction 0.5. + """ + fracs = [] + lengths = self.GetLengths() + lengthtot = sum(lengths) + i = 0 + fracs.append(0.0) + while (i < len(lengths)): + fracs.append(fracs[i] + lengths[i]/float(lengthtot)) + i = i + 1 + return fracs + + def GetLengths(self): + """Returns the length of each part of the RankLocation. + + Behaves differently depending on RankLocation type. If it's curved, call + the equivalent function in the SplineGenerator. + If not, use the Pythogoran Theorem to find out how long each part is. + """ + if (not self.IsStraight()): + if (self.curved): + return CHS.GetLengths(self._splineFunctions) + i = 1 + lengths = [] + while (i < len(self._listOfPoints)): + lengths.append( + math.sqrt( + (self._listOfPoints[i - 1].x - self._listOfPoints[i].x) * + (self._listOfPoints[i - 1].x - self._listOfPoints[i].x) + (self._listOfPoints[i - 1].y - self._listOfPoints[i].y) * + (self._listOfPoints[i - 1].y - self._listOfPoints[i].y))) + i += 1 + return lengths + + + def IsTranslated(self, l2): + """ If the input is a translated version of self, returns the amount + it's been translated by as a Point. Returns input - self. Else, + returns None. + """ + myList = self._listOfPoints + otherList = l2.GetListOfPoints() + if (len(myList) != len(otherList)): + return None + else: + same = True + dx = otherList[0].x - myList[0].x + dy = otherList[0].y - myList[0].y + i = 1 + while ((i < len(myList)) and same): + same = not (((otherList[i].x - myList[i].x) == dx) and ((otherList[i].y - myList[i].y) == dy)) + i = i + 1 + if (same): + return Point.Point(dx, dy) + else: + return None + + def SwitchEndpoints(self): + """Switch the endpoints by reversing the entire list""" + self._listOfPoints.reverse() + self.SetListOfPoints(self._listOfPoints, self._listOfSlopes) + diff --git a/RankPanda/Song.py b/RankPanda/Song.py index b7c3eb8..e162ccd 100755 --- a/RankPanda/Song.py +++ b/RankPanda/Song.py @@ -347,7 +347,7 @@ def GetRankLocationsAtCount(self, count): return None - + #If the given count is outside of range of waypoints. Or less than 2. def CanAnimate(self, count): if (len(self._wayPointList) < 2): return False @@ -369,8 +369,8 @@ def AnimationBegin(self, count): def AnimationStep(self): if (not self.animating): - print("not self.animating") #(Brady) debug - return (None, 0, 0) #(Brady) added the last 0 to fix error unpacking tuple (try other values to see if it has any effect) + return (None, 0, 0) # TODO(Brady): added the last 0 to fix error + # unpacking tuple (try other values to see if it has any effect) if (self.songLoaded): time = self.timeOffset + pygame.mixer.music.get_pos() count = self.ConvertTimeToCount(time) diff --git a/RankPanda/Spline_test.py b/RankPanda/Spline_test.py new file mode 100644 index 0000000..1639d15 --- /dev/null +++ b/RankPanda/Spline_test.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python +import unittest +import CubicHermiteSpline as CHS + +"""Tests for the CubicHermiteSpline module """ + +class TestSpline(unittest.TestCase): + + def testGetLength(self): + fnlist1 = [[20, 2, -30, (39-2*31/3)], [10, -1, 20, 7]] + fnlist2 = [[8, -3, 20, 1], [80, 4, 2, 1]] + self.assertEquals(CHS.GetLengthHelper(fnlist1, 0, 1, 0.001), CHS._GetLengthHelper(fnlist1, 0, 1, 0.001)) + self.assertEquals(CHS.GetLengthHelper(fnlist2, 0, 1, 0.001), CHS._GetLengthHelper(fnlist2, 0, 1, 0.001)) + + def testGetTVal(self): + pass +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/RankPanda/Tester.py b/RankPanda/Tester.py index de034d7..c164b4b 100755 --- a/RankPanda/Tester.py +++ b/RankPanda/Tester.py @@ -1,211 +1,211 @@ -''' - -Created on Oct 13, 2009 - - - -@author: Adam - -''' - - - -import Song -import Move -import Rank -import Commands -import Point -import RankLocation -import RankIDGen -import CubicHermiteSpline -import CoreWrapper -import copy -import pprint -import pygame -from pygame.locals import * - - -class Tester(object): - def TestPoint1(self): - p1 = Point.Point(0,0) - p2 = Point.Point(10,10) - print(p1.x) - print(p2.y) - - def TestCubicHermiteSpline1(self): - p1 = Point.Point(0,0) - p2 = Point.Point(8, 4) - p3 = Point.Point(16, 0) - splineList = CubicHermiteSpline.SplineGenerator.GetSplines([p1, p2, p3], [None, None, None]) - pointList = CubicHermiteSpline.SplineGenerator.GetPoints(splineList) - print(pointList) - - - def TestSong1(self): - song = Song.Song("DefaultSong", 50, 4) - song.AddMove(1,8) - song.AddMove(9,16) - song.AddMove(4, 12) - Move1 = song.GetCurrentMove() - song.SetCurrentMove(0) - Move0 = song.GetCurrentMove() - print(str(Move0.GetNumber())) - print(str(Move1.GetNumber())) - print(str(Move0.GetFollowing().GetNumber())) - print(str(Move1.GetPrior().GetNumber())) - - location1 = RankLocation.RankLocation([Point.Point(100,100), Point.Point(116,100)]) - location3 = RankLocation.RankLocation([Point.Point(200,200), Point.Point(216, 200)]) - r1 = song.currentMove.CreateRank(location1) - r3 = song.currentMove.CreateRank(location3) - r3ID = r3.GetID() - song.GetCurrentMove().NameRank(r1.GetID(), 'A') - print('ID = ' + str(r1.GetID())) - - song.SetCurrentMove(1) - location2 = RankLocation.RankLocation([Point.Point(100,100), Point.Point(100,116)]) - print("location2's midpoint = " + str(location2.GetMidPoint().x) + ', ' + str(location2.GetMidPoint().y)) - r2 = song.currentMove.CreateRank(location2) - song.GetCurrentMove().NameRank(r1.GetID(), 'A') - - print('ID = ' + str(r2.GetID())) - - cmdLst = r2.GetCommandList() - - i = 0 - while (i < len(cmdLst)): - print(cmdLst[i].GetName() + ' ' + str(cmdLst[i].GetLength())) - i = i + 1 - song.SetCurrentMove(0) - song.SetCurrentMove(1) - newcmdlist = song.GetCurrentMove().LookUpName('A').GetCommandList() - i = 0 - while (i < len(newcmdlist)): - print(newcmdlist[i].GetName() + ' ' + str(newcmdlist[i].GetLength())) - i = i + 1 - song.SetCurrentMove(0) - newr3 = song.GetCurrentMove().LookUpID(r3ID) - listOfPoints = newr3.GetEndLocation().GetListOfPoints() - i = 0 - while (i < len(listOfPoints)): - print(repr(listOfPoints[i])) - i = i + 1 - - - - def TestCoreWrapper(self): - core = CoreWrapper.CoreWrapper("Test", 50, [(1,4)], []) - core.MoveAdded(1, 4, None) - core.MoveAdded(5, 8, None) - core.RankDrawn([Point.Point(100, 100), Point.Point(116, 100)]) - rID = core.GetRanks()[0][0] - core.RankAddSpline(rID, 0) - rLoc = core.GetRanks()[0][2] - rLoc2 = copy.deepcopy(rLoc) - print(repr(rLoc.GetListOfPoints())) - print(repr(rLoc.GetListOfDrawingPoints())) - print(repr(rLoc2.GetListOfPoints())) - print(repr(rLoc2.GetListOfDrawingPoints())) - - - def TestCHS(self): - splines = CubicHermiteSpline.SplineGenerator.GetSplines([Point.Point(0, 0), Point.Point(4, 4), Point.Point(8, 0)], [None, None, None]) - print(repr(splines)) - lengths = CubicHermiteSpline.SplineGenerator.GetLengths(splines) - print(repr(lengths)) - points = CubicHermiteSpline.SplineGenerator.GetPoints(splines) - i = 0 - while (i < len(points)): - j = 0 - while (j < len(points[i])): - print('x = ' + str(points[i][j].x) + ', y = ' + str(points[i][j].y)) - j = j + 1 - i = i + 1 - - - def TestSaving(self): - cw = CoreWrapper.CoreWrapper('Seven', 50, [(1,4)], [(1,1)]) - cw.MoveAdded(1, 9, None) - cw.Save('Heyla') - print('Done!') - - def TestLoading(self): - cw = CoreWrapper.CoreWrapper('Things', 50, [(1,4)], [(1,1)]) - cw.Load('Heyla') - print(cw.GetSong()) - print(cw.GetMoves()[0][1]) - print('Done!') - - - def TestGetLocationAtCount(self): - song = Song.Song('title', 100, 4) - song.AddMove(1, 8) #1 - 32 - song.AddMove(9, 16) #33 - 64 - move0 = song.GetMoveList()[0] - move1 = song.GetMoveList()[1] - loc0 = RankLocation.RankLocation([Point.Point(100, 100), Point.Point(116, 100)]) - loc1 = RankLocation.RankLocation([Point.Point(100, 116), Point.Point(116, 116)]) - r0 = move0.CreateRank(loc0, name='A') - r1 = move1.CreateRank(loc1, name='A') - - rls = song.GetRankLocationsAtCount(8) -# print(str(len(rls))) -# pprint.pprint(r1.GetCommandList()) -# pprint.pprint(rls) -# pointList = rls[0][2].GetListOfPoints() -# print('x0 = ' + str(pointList[0].x) + ', y0 = ' + str(pointList[0].y) + ', x1 = ' + str(pointList[1].x) + ', y1 = ' + str(pointList[1].y)) - - - - def TestGetTimeDifferenceAtCount(self): - song = Song.Song('title', 100, 4) - song.AddMove(1, 8) #Counts 1 - 32 - song.AddMove(9, 16) #Counts 33 - 64 - move0 = song.GetMoveList()[0] - move1 = song.GetMoveList()[1] - loc0 = RankLocation.RankLocation([Point.Point(100, 100), Point.Point(116, 100)]) - loc1 = RankLocation.RankLocation([Point.Point(100, 116), Point.Point(116, 116)]) - r0 = move0.CreateRank(loc0, name='A') - r1 = move1.CreateRank(loc1, name='A') - song.AddWayPoint(1, 0) #Count 1 - song.AddWayPoint(15, 10000) #Count 57 - print('song.GetTimeDifferenceAtCount(30) = ' + str(song.GetTimeDifferenceAtCount(30))) - - def TestMusic(self): - try: - print('line one:') - pygame.mixer.init() - print('line two:') - pygame.mixer.music.load('21 - Guys and Dolls (Reprise).mp3') - print('line three:') - pygame.mixer.music.play(1, 10.0) - print('Loop:') - i = 0 - stop = True - while (stop and (i < 100000000)): - i = i + 1 -# if (pygame.key.get_pressed()[K_s]): -# stop = False -# if ((i % 100000) == 0): -# print(str(pygame.key.get_focused())) - - print('Done!') - except: - j = 0 - while (j < 10000): - print('j = ' + str(j)) - j = j + 1 - finally: - print('finally!') - return 0 - - def TestStuff(self, a=3, b=4): - print('a = ' + str(a)) - print('b = ' + str(b)) - - -#Tester().TestStuff(b = 8, a = 15) -Tester().TestMusic() - - - +''' + +Created on Oct 13, 2009 + + + +@author: Adam + +''' + + + +import Song +import Move +import Rank +import Commands +import Point +import RankLocation +import RankIDGen +import CubicHermiteSpline +import CoreWrapper +import copy +import pprint +import pygame +from pygame.locals import * + + +class Tester(object): + def TestPoint1(self): + p1 = Point.Point(0,0) + p2 = Point.Point(10,10) + print(p1.x) + print(p2.y) + + def TestCubicHermiteSpline1(self): + p1 = Point.Point(0,0) + p2 = Point.Point(8, 4) + p3 = Point.Point(16, 0) + splineList = CubicHermiteSpline.SplineGenerator.GetSplines([p1, p2, p3], [None, None, None]) + pointList = CubicHermiteSpline.SplineGenerator.GetPoints(splineList) + print(pointList) + + + def TestSong1(self): + song = Song.Song("DefaultSong", 50, 4) + song.AddMove(1,8) + song.AddMove(9,16) + song.AddMove(4, 12) + Move1 = song.GetCurrentMove() + song.SetCurrentMove(0) + Move0 = song.GetCurrentMove() + print(str(Move0.GetNumber())) + print(str(Move1.GetNumber())) + print(str(Move0.GetFollowing().GetNumber())) + print(str(Move1.GetPrior().GetNumber())) + + location1 = RankLocation.RankLocation([Point.Point(100,100), Point.Point(116,100)]) + location3 = RankLocation.RankLocation([Point.Point(200,200), Point.Point(216, 200)]) + r1 = song.currentMove.CreateRank(location1) + r3 = song.currentMove.CreateRank(location3) + r3ID = r3.GetID() + song.GetCurrentMove().NameRank(r1.GetID(), 'A') + print('ID = ' + str(r1.GetID())) + + song.SetCurrentMove(1) + location2 = RankLocation.RankLocation([Point.Point(100,100), Point.Point(100,116)]) + print("location2's midpoint = " + str(location2.GetMidPoint().x) + ', ' + str(location2.GetMidPoint().y)) + r2 = song.currentMove.CreateRank(location2) + song.GetCurrentMove().NameRank(r1.GetID(), 'A') + + print('ID = ' + str(r2.GetID())) + + cmdLst = r2.GetCommandList() + + i = 0 + while (i < len(cmdLst)): + print(cmdLst[i].GetName() + ' ' + str(cmdLst[i].GetLength())) + i = i + 1 + song.SetCurrentMove(0) + song.SetCurrentMove(1) + newcmdlist = song.GetCurrentMove().LookUpName('A').GetCommandList() + i = 0 + while (i < len(newcmdlist)): + print(newcmdlist[i].GetName() + ' ' + str(newcmdlist[i].GetLength())) + i = i + 1 + song.SetCurrentMove(0) + newr3 = song.GetCurrentMove().LookUpID(r3ID) + listOfPoints = newr3.GetEndLocation().GetListOfPoints() + i = 0 + while (i < len(listOfPoints)): + print(repr(listOfPoints[i])) + i = i + 1 + + + + def TestCoreWrapper(self): + core = CoreWrapper.CoreWrapper("Test", 50, [(1,4)], []) + core.MoveAdded(1, 4, None) + core.MoveAdded(5, 8, None) + core.RankDrawn([Point.Point(100, 100), Point.Point(116, 100)]) + rID = core.GetRanks()[0][0] + core.RankAddSpline(rID, 0) + rLoc = core.GetRanks()[0][2] + rLoc2 = copy.deepcopy(rLoc) + print(repr(rLoc.GetListOfPoints())) + print(repr(rLoc.GetListOfDrawingPoints())) + print(repr(rLoc2.GetListOfPoints())) + print(repr(rLoc2.GetListOfDrawingPoints())) + + + def TestCHS(self): + splines = CubicHermiteSpline.SplineGenerator.GetSplines([Point.Point(0, 0), Point.Point(4, 4), Point.Point(8, 0)], [None, None, None]) + print(repr(splines)) + lengths = CubicHermiteSpline.SplineGenerator.GetLengths(splines) + print(repr(lengths)) + points = CubicHermiteSpline.SplineGenerator.GetPoints(splines) + i = 0 + while (i < len(points)): + j = 0 + while (j < len(points[i])): + print('x = ' + str(points[i][j].x) + ', y = ' + str(points[i][j].y)) + j = j + 1 + i = i + 1 + + + def TestSaving(self): + cw = CoreWrapper.CoreWrapper('Seven', 50, [(1,4)], [(1,1)]) + cw.MoveAdded(1, 9, None) + cw.Save('Heyla') + print('Done!') + + def TestLoading(self): + cw = CoreWrapper.CoreWrapper('Things', 50, [(1,4)], [(1,1)]) + cw.Load('Heyla') + print(cw.GetSong()) + print(cw.GetMoves()[0][1]) + print('Done!') + + + def TestGetLocationAtCount(self): + song = Song.Song('title', 100, 4) + song.AddMove(1, 8) #1 - 32 + song.AddMove(9, 16) #33 - 64 + move0 = song.GetMoveList()[0] + move1 = song.GetMoveList()[1] + loc0 = RankLocation.RankLocation([Point.Point(100, 100), Point.Point(116, 100)]) + loc1 = RankLocation.RankLocation([Point.Point(100, 116), Point.Point(116, 116)]) + r0 = move0.CreateRank(loc0, name='A') + r1 = move1.CreateRank(loc1, name='A') + + rls = song.GetRankLocationsAtCount(8) +# print(str(len(rls))) +# pprint.pprint(r1.GetCommandList()) +# pprint.pprint(rls) +# pointList = rls[0][2].GetListOfPoints() +# print('x0 = ' + str(pointList[0].x) + ', y0 = ' + str(pointList[0].y) + ', x1 = ' + str(pointList[1].x) + ', y1 = ' + str(pointList[1].y)) + + + + def TestGetTimeDifferenceAtCount(self): + song = Song.Song('title', 100, 4) + song.AddMove(1, 8) #Counts 1 - 32 + song.AddMove(9, 16) #Counts 33 - 64 + move0 = song.GetMoveList()[0] + move1 = song.GetMoveList()[1] + loc0 = RankLocation.RankLocation([Point.Point(100, 100), Point.Point(116, 100)]) + loc1 = RankLocation.RankLocation([Point.Point(100, 116), Point.Point(116, 116)]) + r0 = move0.CreateRank(loc0, name='A') + r1 = move1.CreateRank(loc1, name='A') + song.AddWayPoint(1, 0) #Count 1 + song.AddWayPoint(15, 10000) #Count 57 + print('song.GetTimeDifferenceAtCount(30) = ' + str(song.GetTimeDifferenceAtCount(30))) + + def TestMusic(self): + try: + print('line one:') + pygame.mixer.init() + print('line two:') + pygame.mixer.music.load('21 - Guys and Dolls (Reprise).mp3') + print('line three:') + pygame.mixer.music.play(1, 10.0) + print('Loop:') + i = 0 + stop = True + while (stop and (i < 100000000)): + i = i + 1 +# if (pygame.key.get_pressed()[K_s]): +# stop = False +# if ((i % 100000) == 0): +# print(str(pygame.key.get_focused())) + + print('Done!') + except: + j = 0 + while (j < 10000): + print('j = ' + str(j)) + j = j + 1 + finally: + print('finally!') + return 0 + + def TestStuff(self, a=3, b=4): + print('a = ' + str(a)) + print('b = ' + str(b)) + + +#Tester().TestStuff(b = 8, a = 15) +Tester().TestMusic() + + + diff --git a/ReadMe.txt b/ReadMe.txt index afd8a29..c28afeb 100644 --- a/ReadMe.txt +++ b/ReadMe.txt @@ -25,4 +25,5 @@ UPDATED INSTRUCTIONS: (as of 6/19/13 - Brady) http://www.wxpython.org/download.php Install or build them and put them in the site-packages folder of PythonXX\Lib folder (where XX is the version of python you installed) Python 2.7 may be downloaded from http://www.python.org/getit/ - NOTE: RankPanda will not work with Python 3. \ No newline at end of file + NOTE: RankPanda will not work with Python 3. + From 30228b85c238e76e442dd88797f077cf8afafa88 Mon Sep 17 00:00:00 2001 From: brajac709 Date: Fri, 21 Jun 2013 17:21:17 -0400 Subject: [PATCH 6/8] Style fixes + more iterative Made iterative versions of _GetLengthHelper and _GetTValueAtLengthHelper. Baseline tests show that they do the same thing as recursive versions but more tests may be needed. --- RankPanda/Commands.py | 21 +- RankPanda/CoreWrapper.py | 52 +- RankPanda/CubicHermiteSpline.py | 586 ++++--- RankPanda/GUIDialogs.py | 6 +- RankPanda/GUIField.py | 2544 +++++++++++++-------------- RankPanda/GUIMain.py | 2913 +++++++++++++++---------------- RankPanda/GUIRankList.py | 148 +- RankPanda/GUITimeBar.py | 488 +++--- RankPanda/Move.py | 618 +++---- RankPanda/Point.py | 126 +- RankPanda/Printer.py | 340 ++-- RankPanda/Rank.py | 1772 +++++++++---------- RankPanda/RankIDGen.py | 22 +- RankPanda/RankLocation.py | 532 +++--- RankPanda/Song.py | 6 +- RankPanda/Spline_test.py | 18 + RankPanda/Tester.py | 422 ++--- ReadMe.txt | 3 +- 18 files changed, 5343 insertions(+), 5274 deletions(-) create mode 100644 RankPanda/Spline_test.py diff --git a/RankPanda/Commands.py b/RankPanda/Commands.py index 7882f56..871c816 100755 --- a/RankPanda/Commands.py +++ b/RankPanda/Commands.py @@ -19,7 +19,8 @@ def CalcLocation(self, count, beginLocation): """ Given the beginning location, calculates the location that the rank should be in after count counts. Uses information such as self._delta and the command type, but does NOT use - self.endLocation. """ + self.endLocation. + """ return beginLocation @@ -28,7 +29,8 @@ def CalcLocation(self, count, beginLocation): def Split(self, count, beginLocation): """ Splits the command in two, at the specified count. Changes this command to be the appropriate length, and returns this command as well as the - new one.""" + new one. + """ pass @@ -44,7 +46,8 @@ def SetName(self, name): def GetLength(self): """Returns the number of counts this command spans. If you want to change - this value, make a new command instead.""" + this value, make a new command instead. + """ return round(self._length) @@ -59,17 +62,19 @@ def SnapEndLocation(self, newBeginLocation): # Be sure to overwrite this in each Command! def CalcBeginLocation(self, count, endLocation): - """From the ending location, calculate the beginning location. Will require - basically the same implementation as CalcLocation(), but in reverse. - count should be the location in the whole command you want it to be - returned. So, pass in 0 to run the whole command.""" + """From the ending location, calculate the beginning location. + Will require basically the same implementation as CalcLocation(), + but in reverse. count should be the location in the whole command + you want it to be returned. So, pass in 0 to run the whole command. + """ return None def MergeWithFollowing(self, following): """ Checks following to see if it's a command of the same name. If so, - return the merged commands.""" + return the merged commands. + """ if ((following._name == self._name) and (isinstance(following, self))): self._length = self._length + following._length self._endLocation = following._endLocation diff --git a/RankPanda/CoreWrapper.py b/RankPanda/CoreWrapper.py index fce563a..f63c1be 100755 --- a/RankPanda/CoreWrapper.py +++ b/RankPanda/CoreWrapper.py @@ -52,16 +52,8 @@ def GetSong(self): # [(0, 'Move 0'), (1, 'Move 1'), ...] def GetMoves(self): listOfMoves = self._song.GetMoveList() - #(Brady) Trying list comprehension return [(curmove.GetNumber(), curmove.GetName()) for curmove in listOfMoves] - #Original Code - #i = 0 - #moveInfo = [] - #while (i < len(listOfMoves)): - # curmove = listOfMoves[i] - # moveInfo.append((curmove.GetNumber(), curmove.GetName())) - # i = i + 1 - #return moveInfo + @@ -79,7 +71,6 @@ def GetCurrentMove(self): # ? # List of all ranks in current move # [(id0, name0, location0), (id1, name1, location1)] - #(Brady) Merged next method with this def GetRanks(self, moveNumber = None): if (self._song.currentMove is None): return None @@ -87,31 +78,9 @@ def GetRanks(self, moveNumber = None): allRanks = self._song.currentMove.GetAllRanks() else: allRanks = self._song.GetMoveList()[moveNumber].GetAllRanks() - #(Brady) List comprehensions - return [(r.GetID(), r.GetName(), r.GetEndLocation(), r.GetLabelLocation()) for r in allRanks] - #Orig. code - #i = 0 - #allRankInfo = [] - #while (i < len(allRanks)): - # allRankInfo.append((allRanks[i].GetID(), allRanks[i].GetName(), allRanks[i].GetEndLocation(), allRanks[i].GetLabelLocation())) - # i = i + 1 - #return allRankInfo - - #(Brady) merged with prev with optional args. Keeping this so I dont have to change all the method calls. - # Same as above but now you input the move number. - def GetRanksGivenMove(self, moveNumber): - return self.GetRanks(moveNumber) - # if (self._song.currentMove is None): - # return None - # allRanks = self._song.GetMoveList()[moveNumber].GetAllRanks() - # i = 0 - # allRankInfo = [] - # while (i < len(allRanks)): - # allRankInfo.append((allRanks[i].GetID(), allRanks[i].GetName(), allRanks[i].GetEndLocation(), allRanks[i].GetLabelLocation())) - # i = i + 1 - # return allRankInfo - - + return [(r.GetID(), r.GetName(), r.GetEndLocation(),\ + r.GetLabelLocation()) for r in allRanks] + def IsRankHeld(self, ID): if (self._song.currentMove is None): return None @@ -124,15 +93,8 @@ def GetSelectedRanks(self): if (self._song.currentMove is None): return None allSelectedRanks = self._song.currentMove.GetSelectedRanks() - #(Brady) List comp. - return [(r.GetID(), r.GetName(), r.GetEndLocation(), r.GetLabelLocation()) for r in allSelectedRanks] - #Orig. code - #i = 0 - #allSelectedRankInfo = [] - #while (i < len(allSelectedRanks)): - # allSelectedRankInfo.append((allSelectedRanks[i].GetID(), allSelectedRanks[i].GetName(), allSelectedRanks[i].GetEndLocation(), allSelectedRanks[i].GetLabelLocation())) - # i = i + 1 - #return allSelectedRankInfo + return [(r.GetID(), r.GetName(), r.GetEndLocation(),\ + r.GetLabelLocation()) for r in allSelectedRanks] def GetAdditionalRanks(self): curList = [] @@ -789,7 +751,7 @@ def RankDeleteSpline(self, rankID, number): return None self._song.currentMove.LookUpID(rankID).DeleteSplinePoint(number) - #(Brady) TODO: prevent changing name if not on the very first move. + #(Brady) TODO: prevent changing name if not on the very first move. # Pass in the ID of the rank to name, and its new name. def NameRank(self, name): if (self._song.currentMove is None): diff --git a/RankPanda/CubicHermiteSpline.py b/RankPanda/CubicHermiteSpline.py index 3e718e1..9beb8bf 100755 --- a/RankPanda/CubicHermiteSpline.py +++ b/RankPanda/CubicHermiteSpline.py @@ -1,255 +1,331 @@ -import math -import Point - - -# This class handles the creation of splines. -# A Cubic Hermite Spline can be defined by a list of points and their slopes. -# Once all these points and slopes are had, a smooth spline is defined between -# each consecutive two points. -# Let x(t) be a spline between two points, xi and xj (j = i + 1). -# Let the slope at xi be mi and the slope at xj be mj. -# Then, x(t) is defined as: -# x(t) = (2t^3 - 3t^2 + 1)xi + (t^3 - 2t^2 + t)mi + (-2t^3 + 3t^2)xj + (t^3 - t^2)mj -# Repeat to get y(t). -# When you shift t from 0 to 1, x(t) and y(t) will generate a smooth curve -# between each pair of points. Further, the curves will all be smooth with -# the one next to them, as the slopes are equal. -# The splines are saved in a simple format; see below. - -# TODO(astory): convert to module level functions. None of these actually take -# advantage of, or have any reason for, being in a class. - - -# This calculated what the slopes at the points should be for the splines. -# The method we use simply draws a straight line between the point before -# and the point after, and this becomes the slope of the point. -# Note that the slopes are dx/dt and dy/dt, not dx/dy. -def _GetSlope(PointTriple): - p0 = PointTriple[0] - p1 = PointTriple[1] - p2 = PointTriple[2] - pFirst = None - pSecond = None - n = 1 - if (p0 is None): - pFirst = p1 - pSecond = p2 - n = 2 - elif (p2 is None): - pFirst = p0 - pSecond = p1 - n = 2 - else: - pFirst = p0 - pSecond = p2 - slopex = (n/float(2))*(pSecond.x - pFirst.x) - slopey = (n/float(2))*(pSecond.y - pFirst.y) - return Point.Point(slopex, slopey) - -# The main splining method. Takes in a list of points and their slopes, -# and calculates the splines connecting them. Farily straightforward, once -# you know how it works, as described above. -# Note that is any slope is None, it'll automatically be calculated. -# This should often be the case, except when doing something like -# intermediate DTP locations or something. -# A given spline is stored as a list of four elements: -# [constantTerm, linearTerm, quadraticTerm, cubicTerm] -# The spline lists are then saved as [xsplines, ysplines] -# Each one is saved in order. -def GetSplines(pointList, oldSlopeList): - slopeList = [] - l = len(pointList) - i = 0 - while (i < l): - if (oldSlopeList[i] is None): - if (i == 0): - slopeList.append(_GetSlope([None, pointList[0], pointList[1]])) - elif (i == (l - 1)): - slopeList.append(_GetSlope([pointList[i - 1], pointList[i], None])) - else: - slopeList.append(_GetSlope([pointList[i - 1], pointList[i], pointList[i + 1]])) - else: - slopeList.append(oldSlopeList[i]) - i = i + 1 - splineList = [] - i = 0 - while (i < (l - 1)): - xfn = [0, 0, 0, 0] - yfn = [0, 0, 0, 0] - xfn[0] = pointList[i].x - yfn[0] = pointList[i].y - xfn[1] = slopeList[i].x - yfn[1] = slopeList[i].y - xfn[2] = (-3*pointList[i].x) + (-2*slopeList[i].x) + (3*pointList[i+1].x) + (-1*slopeList[i+1].x) - yfn[2] = (-3*pointList[i].y) + (-2*slopeList[i].y) + (3*pointList[i+1].y) + (-1*slopeList[i+1].y) - xfn[3] = (2*pointList[i].x) + (slopeList[i].x) + (-2*pointList[i+1].x) + (slopeList[i+1].x) - yfn[3] = (2*pointList[i].y) + (slopeList[i].y) + (-2*pointList[i+1].y) + (slopeList[i+1].y) - splineList.append([xfn,yfn]) - i = i + 1 - return splineList - - -# Algorithm here: Find the length between the two end points. Then, -# find the length from point 0 to the midpoint of the spline, and then -# from the midpoint to point 1. If the sum of these two lengths is close -# to the length of the first one, return the length. If not, recurse -# and add the lengths together. -def GetLength(fnList, tol): - return _GetLengthHelper(fnList, 0, 1, tol) - -def dist(p1, p2): - """Calculates the euclidean distance between points p1 and p2 - p1, p2 are a tuple of form (x,y)""" - return math.sqrt((p2[0]-p1[0])**2 + (p2[1] - p1[1])**2) - -#(Brady) Making it iterative (note it doesnt have underscore...) -def GetLengthHelper(fnList, ti, tf, tol): -#maybe use a stack?? - xi = EvalCubic(ti, fnList[0]) - yi = EvalCubic(ti, fnList[1]) - xf = EvalCubic(tf, fnList[0]) - yf = EvalCubic(tf, fnList[1]) - tm = (ti + tf)/float(2) - xm = EvalCubic(tm, fnList[0]) - ym = EvalCubic(tm, fnList[1]) - - side3 = dist((xi,yi), (xf,yf)) #from beginning to end - side1 = dist((xi,yi), (xm,ym)) #from middle to begining - side2 = dist((xm,ym), (xf,yf)) #from middle to end - if ((math.fabs(((side1 + side2)/float(side3)) - 1)) <= tol): - return math.fabs(side1 + side2) - #essentially else - cumSideLeft = side1 - cumSideRight = side2 - while((math.fabs(((side1 + side2)/float(side3)) -1)) > tol): - try: - if ((math.fabs(((side1 + side2)/float(side3)) - 1)) <= tol): - return math.fabs(side1 + side2) - else: - return (_GetLengthHelper(fnList, ti, tm, tol)) + (_GetLengthHelper(fnList, tm, tf, tol)) - except: - if ((math.fabs(((side1 + side2)/float(side3)) - 1)) <= tol): - return math.fabs(side1 + side2) - else: - return (_GetLengthHelper(fnList, ti, tm, tol)) + (_GetLengthHelper(fnList, tm, tf, tol)) - -# TODO: Make iterative! It'll help. -def _GetLengthHelper(fnList, ti, tf, tol): - xi = EvalCubic(ti, fnList[0]) - yi = EvalCubic(ti, fnList[1]) - xf = EvalCubic(tf, fnList[0]) - yf = EvalCubic(tf, fnList[1]) - tm = (ti + tf)/float(2) - xm = EvalCubic(tm, fnList[0]) - ym = EvalCubic(tm, fnList[1]) - side3 = math.sqrt((xf - xi)**2 + (yf - yi)**2) #from beginning to end - side1 = math.sqrt((xm - xi)**2 + (ym - yi)**2) #from middle to begining - side2 = math.sqrt((xf - xm)**2 + (yf - ym)**2) #from middle to end - try: - if ((math.fabs(((side1 + side2)/float(side3)) - 1)) <= tol): - return math.fabs(side1 + side2) - else: - return (_GetLengthHelper(fnList, ti, tm, tol)) + (_GetLengthHelper(fnList, tm, tf, tol)) - except: - if ((math.fabs(((side1 + side2)/float(side3)) - 1)) <= tol): - return math.fabs(side1 + side2) - else: - return (_GetLengthHelper(fnList, ti, tm, tol)) + (_GetLengthHelper(fnList, tm, tf, tol)) - -# Simply find the value of the spline function at the point. -def EvalCubic(t, fn): - return (fn[0] + fn[1]*t + fn[2]*t*t + fn[3]*t*t*t) - -def EvalSlopeOfCubic(t, fn): - return (fn[1] + 2*fn[2]*t + 3*fn[3]*t*t) - -# This method takes in a splineList and figures out all the points to draw -# along the spline. -# Increase the value of NUMBERPERSTEP to draw more points. Decrease to draw fewer. -# I return a list of lists - each inner list contains all the points to be -# drawn. - -def GetPoints(splineList): - NUMBERPERSTEP = 8 - lengths = GetLengths(splineList) - i = 0 -# Ignore and feel free to remove the next lines, I think they're repetitiions of the GetLengths() function. -# while (i < len(splineList)): -# lengths.append(SplineGenerator.GetLength(splineList[i], 0.001)) -# i = i + 1 -# i = 0 - listOfPointLists = [] - while (i < len(splineList)): - total = NUMBERPERSTEP*lengths[i] - t = 0 - listOfPointLists.append([]) - xfn = splineList[i][0] - yfn = splineList[i][1] - while (t <= 1): - listOfPointLists[i].append(Point.Point(EvalCubic(t, xfn), EvalCubic(t, yfn))) - t = t + (1/float(total)) - i = i + 1 - return listOfPointLists - - -# Pass in a fraction along the spline (as a whole) that you want -# information about. For example, pass in 0.5 to get exactly halfway along. -# Returns a point, a slope, and an index at the given fractional length -# along a spline. -# The index is which spline part the point in question lies along. - -# First, I get the lengths of each spline part. I then go through and find -# the total lenght along the splines needed, and find which spline part the -# fraction will lie on. -# I then find the t value at which we need, and then just find the point -# and slope at tha t value. -# Note: Not designed for repeated use in real time, because the finding of -# the t value recurses a recursive method until it's found. -def GetInformationAtLengthFraction(splineList, lengthFraction): - lengths = GetLengths(splineList) - totalLength = sum(lengths) - lengthNeeded = lengthFraction * totalLength - i = 0 - if (lengthNeeded > 0.1): - while (lengthNeeded > 0.1): - lengthNeeded = lengthNeeded - lengths[i] - i = i + 1 - i = i - 1 - lengthNeeded = lengthNeeded + lengths[i] - if lengthNeeded <= 0.1: - t = 0 - else: - t = _GetTValueAtLengthHelper(splineList[i], lengthNeeded, 0, 1, 0.001, 0.001) - x = EvalCubic(t, splineList[i][0]) - y = EvalCubic(t, splineList[i][1]) - dx = EvalSlopeOfCubic(t, splineList[i][0]) - dy = EvalSlopeOfCubic(t, splineList[i][1]) - return [(Point.Point(x, y)),(Point.Point(dx, dy)), i] - -# Finds the length of each spline part in the list -def GetLengths(splineList): - lengths = [] - i = 0 - while (i < len(splineList)): - lengths.append(GetLength(splineList[i], 0.001)) - i = i + 1 - return lengths - -# A simple recursive method. Finds the midpoint of the current section -# of the spline and zeros in until the length desired is found. -# Note that while it's a recursive method, every call calls -# GetLengthHelper(), another recursive method, which is why this shouldn't -# be used in real time, if possible. -# TODO: make it iterative! Should sav both space (for obvious -# recursion-related reasons) and time (Python's not great with method calls, -# I believe.) -def _GetTValueAtLengthHelper(fnList, length, tinit, tfinal, lengthCalcTol, lengthCompareTol): - tmid = (tinit + tfinal) / float(2) - curLength = _GetLengthHelper(fnList, 0, tmid, lengthCalcTol) - if (math.fabs((curLength / float(length)) - 1) < lengthCompareTol): - return tmid - else: - if (curLength > length): - return _GetTValueAtLengthHelper(fnList, length, tinit, tmid, lengthCalcTol, lengthCompareTol) - else: - return _GetTValueAtLengthHelper(fnList, length, tmid, tfinal, lengthCalcTol, lengthCompareTol) +import math +import Point +import decimal + +D = decimal.Decimal + + +# This class handles the creation of splines. +# A Cubic Hermite Spline can be defined by a list of points and their slopes. +# Once all these points and slopes are had, a smooth spline is defined between +# each consecutive two points. +# Let x(t) be a spline between two points, xi and xj (j = i + 1). +# Let the slope at xi be mi and the slope at xj be mj. +# Then, x(t) is defined as: +# x(t) = (2t^3 - 3t^2 + 1)xi + (t^3 - 2t^2 + t)mi + (-2t^3 + 3t^2)xj + (t^3 - t^2)mj +# Repeat to get y(t). +# When you shift t from 0 to 1, x(t) and y(t) will generate a smooth curve +# between each pair of points. Further, the curves will all be smooth with +# the one next to them, as the slopes are equal. +# The splines are saved in a simple format; see below. + + +# This calculated what the slopes at the points should be for the splines. +# The method we use simply draws a straight line between the point before +# and the point after, and this becomes the slope of the point. +# Note that the slopes are dx/dt and dy/dt, not dx/dy. +def _GetSlope(PointTriple): + p0 = PointTriple[0] + p1 = PointTriple[1] + p2 = PointTriple[2] + pFirst = None + pSecond = None + n = 1 + if (p0 is None): + pFirst = p1 + pSecond = p2 + n = 2 + elif (p2 is None): + pFirst = p0 + pSecond = p1 + n = 2 + else: + pFirst = p0 + pSecond = p2 + slopex = (n/2)*(pSecond.x - pFirst.x) + slopey = (n/2)*(pSecond.y - pFirst.y) + return Point.Point(slopex, slopey) + +# The main splining method. Takes in a list of points and their slopes, +# and calculates the splines connecting them. Farily straightforward, once +# you know how it works, as described above. +# Note that is any slope is None, it'll automatically be calculated. +# This should often be the case, except when doing something like +# intermediate DTP locations or something. +# A given spline is stored as a list of four elements: +# [constantTerm, linearTerm, quadraticTerm, cubicTerm] +# The spline lists are then saved as [xsplines, ysplines] +# Each one is saved in order. +def GetSplines(pointList, oldSlopeList): + slopeList = [] + l = len(pointList) + i = 0 + while (i < l): + if (oldSlopeList[i] is None): + if (i == 0): + slopeList.append(_GetSlope([None, pointList[0], pointList[1]])) + elif (i == (l - 1)): + slopeList.append(_GetSlope([pointList[i - 1], pointList[i], None])) + else: + slopeList.append(_GetSlope([pointList[i - 1], pointList[i], pointList[i + 1]])) + else: + slopeList.append(oldSlopeList[i]) + i = i + 1 + splineList = [] + i = 0 + while (i < (l - 1)): + xfn = [0, 0, 0, 0] + yfn = [0, 0, 0, 0] + xfn[0] = pointList[i].x + yfn[0] = pointList[i].y + xfn[1] = slopeList[i].x + yfn[1] = slopeList[i].y + xfn[2] = (-3*pointList[i].x) + (-2*slopeList[i].x) + (3*pointList[i+1].x) + (-1*slopeList[i+1].x) + yfn[2] = (-3*pointList[i].y) + (-2*slopeList[i].y) + (3*pointList[i+1].y) + (-1*slopeList[i+1].y) + xfn[3] = (2*pointList[i].x) + (slopeList[i].x) + (-2*pointList[i+1].x) + (slopeList[i+1].x) + yfn[3] = (2*pointList[i].y) + (slopeList[i].y) + (-2*pointList[i+1].y) + (slopeList[i+1].y) + splineList.append([xfn,yfn]) + i = i + 1 + return splineList + + +# Algorithm here: Find the length between the two end points. Then, +# find the length from point 0 to the midpoint of the spline, and then +# from the midpoint to point 1. If the sum of these two lengths is close +# to the length of the first one, return the length. If not, recurse +# and add the lengths together. +def GetLength(fnList, tol): + #return _GetLengthHelper(fnList, 0, 1, tol) + return GetLengthHelper(fnList, 0, 1, tol) + +def dist(p1, p2): + """Calculates the euclidean distance between points p1 and p2 + p1, p2 are a tuple of form (x,y) + """ + return math.sqrt((p2[0]-p1[0])**2 + (p2[1] - p1[1])**2) + +def findSideLengths(tif, fnList): + """Just a helper function to consolidate these actions. + tif is a tuple (ti,tf). returns a tuple (tm, side i-f, side i-m, side m-f) + """ + xi = EvalCubic(tif[0], fnList[0]) + yi = EvalCubic(tif[0], fnList[1]) + xf = EvalCubic(tif[1], fnList[0]) + yf = EvalCubic(tif[1], fnList[1]) + tm = (tif[0] + tif[1])/float(2) + xm = EvalCubic(tm, fnList[0]) + ym = EvalCubic(tm, fnList[1]) + side3 = dist((xi,yi), (xf,yf)) #from beginning to end (TODO: make a check for this being zero) + side1 = dist((xi,yi), (xm,ym)) #from middle to begining + side2 = dist((xm,ym), (xf,yf)) #from middle to end + return (tm, side3, side1, side2) + +#(Brady) Making it iterative (note it doesnt have underscore...) +def GetLengthHelper(fnList, ti, tf, tol): +#TODO: FIX IT!!! the "r" case is a little broken, sometimes need to pop twice, others not... + states = ["d", (ti,tf)] #a state is represented by a tuple of (ti, tf) for each phase + #try using some sort of call address (probably a string in this case) to identify what type of state this is... + #"d" => done + address = "s" #i think i use "s" for start, or like base case, or something... basically still recursing. + #import time #for debugging.... + while(len(states) > 0): + #time.sleep(.1) # Temp... + if address == "d": + returnValue = states.pop() + elif address == "l": #this is a lowercase L not a 1 + val = states.pop() + p = states.pop() + tm, side3, side1, side2 = findSideLengths(p, fnList) + if ((math.fabs(((side1 + side2)/float(side3)) - 1 )) <= tol): + address = states.pop() + states.append(val + math.fabs(side1 + side2)) + else: + states.append(val) + states.append("r") + states.append((tm,p[1])) + states.append("l") + states.append((p[0],tm)) + address = "s" + elif address == "r": + val1 = states.pop() + val2 = states.pop() + if val2 == "d": + returnValue = val1 #should be the length.... otherwise, keep going + break + elif val2 == "l" or val2 == "r": #not really sure if necessary, or right... + address = val2 + states.append(val1) + continue + address = states.pop() + address = states.pop() #need to pop twice... + states.append(val1 + val2) + elif address == "s": + #still recursing + p = states.pop() + tm, side3, side1, side2 = findSideLengths(p, fnList) + if ((math.fabs(((side1 + side2)/float(side3)) - 1)) <= tol): + #base case + address = states.pop() + states.append(math.fabs(side1 + side2)) #push the length of this segment. + #i think.... + else: + #push the 2 sides + #push "r", (tm,tf),"l", (ti,tm) tentatively + #address = "s" + states.append("r") + states.append((tm, p[1])) + states.append("l") + states.append((p[0],tm)) + address = "s" + else: #basically if the address is a number not a string. + val = states.pop() + newval = address + val + address = states.pop() + states.append(newval) + #print(states) + #print(address) + return returnValue + +# TODO: Make iterative! It'll help. +def _GetLengthHelper(fnList, ti, tf, tol): + xi = EvalCubic(ti, fnList[0]) + yi = EvalCubic(ti, fnList[1]) + xf = EvalCubic(tf, fnList[0]) + yf = EvalCubic(tf, fnList[1]) + tm = (ti + tf)/float(2) + xm = EvalCubic(tm, fnList[0]) + ym = EvalCubic(tm, fnList[1]) + side3 = math.sqrt((xf - xi)**2 + (yf - yi)**2) #from beginning to end + side1 = math.sqrt((xm - xi)**2 + (ym - yi)**2) #from middle to begining + side2 = math.sqrt((xf - xm)**2 + (yf - ym)**2) #from middle to end + try: + if ((math.fabs(((side1 + side2)/float(side3)) - 1)) <= tol): + return math.fabs(side1 + side2) + else: + return (_GetLengthHelper(fnList, ti, tm, tol)) + (_GetLengthHelper(fnList, tm, tf, tol)) + except: + if ((math.fabs(((side1 + side2)/float(side3)) - 1)) <= tol): + return math.fabs(side1 + side2) + else: + return (_GetLengthHelper(fnList, ti, tm, tol)) + (_GetLengthHelper(fnList, tm, tf, tol)) + +# Simply find the value of the spline function at the point. +def EvalCubic(t, fn): + return (fn[0] + fn[1]*t + fn[2]*t*t + fn[3]*t*t*t) + +def EvalSlopeOfCubic(t, fn): + return (fn[1] + 2*fn[2]*t + 3*fn[3]*t*t) + +# This method takes in a splineList and figures out all the points to draw +# along the spline. +# Increase the value of NUMBERPERSTEP to draw more points. Decrease to draw fewer. +# I return a list of lists - each inner list contains all the points to be +# drawn. + +def GetPoints(splineList): + NUMBERPERSTEP = 8 + lengths = GetLengths(splineList) + i = 0 +# Ignore and feel free to remove the next lines, I think they're repetitiions of the GetLengths() function. +# while (i < len(splineList)): +# lengths.append(SplineGenerator.GetLength(splineList[i], 0.001)) +# i = i + 1 +# i = 0 + listOfPointLists = [] + while (i < len(splineList)): + total = NUMBERPERSTEP*lengths[i] + t = 0 + listOfPointLists.append([]) + xfn = splineList[i][0] + yfn = splineList[i][1] + while (t <= 1): + listOfPointLists[i].append(Point.Point(EvalCubic(t, xfn), EvalCubic(t, yfn))) + t = t + (1/float(total)) + i = i + 1 + return listOfPointLists + + +# Pass in a fraction along the spline (as a whole) that you want +# information about. For example, pass in 0.5 to get exactly halfway along. +# Returns a point, a slope, and an index at the given fractional length +# along a spline. +# The index is which spline part the point in question lies along. + +# First, I get the lengths of each spline part. I then go through and find +# the total length along the splines needed, and find which spline part the +# fraction will lie on. +# I then find the t value at which we need, and then just find the point +# and slope at tha t value. +# Note: Not designed for repeated use in real time, because the finding of +# the t value recurses a recursive method until it's found. +def GetInformationAtLengthFraction(splineList, lengthFraction): + lengths = GetLengths(splineList) + totalLength = sum(lengths) + lengthNeeded = lengthFraction * totalLength + i = 0 + if (lengthNeeded > 0.1): + while (lengthNeeded > 0.1): + lengthNeeded = lengthNeeded - lengths[i] + i = i + 1 + i = i - 1 + lengthNeeded = lengthNeeded + lengths[i] + if lengthNeeded <= 0.1: + t = 0 + else: + #t = _GetTValueAtLengthHelper(splineList[i], lengthNeeded, 0, 1, 0.001, 0.001) + #TODO MAKE SURE THIS WORKS!!!! + t = GetTValueAtLengthHelper(splineList[i], lengthNeeded, 0, 1, 0.001, 0.001) + x = EvalCubic(t, splineList[i][0]) + y = EvalCubic(t, splineList[i][1]) + dx = EvalSlopeOfCubic(t, splineList[i][0]) + dy = EvalSlopeOfCubic(t, splineList[i][1]) + return [(Point.Point(x, y)),(Point.Point(dx, dy)), i] + +# Finds the length of each spline part in the list +def GetLengths(splineList): + lengths = [] + i = 0 + while (i < len(splineList)): + lengths.append(GetLength(splineList[i], 0.001)) + i = i + 1 + return lengths + +# A simple recursive method. Finds the midpoint of the current section +# of the spline and zeros in until the length desired is found. +# Note that while it's a recursive method, every call calls +# GetLengthHelper(), another recursive method, which is why this shouldn't +# be used in real time, if possible. +# TODO: make it iterative! Should sav both space (for obvious +# recursion-related reasons) and time (Python's not great with method calls, +# I believe.) +def _GetTValueAtLengthHelper(fnList, length, tinit, tfinal, lengthCalcTol, lengthCompareTol): + tmid = (tinit + tfinal) / float(2) + #curLength = _GetLengthHelper(fnList, 0, tmid, lengthCalcTol) + curLength = GetLengthHelper(fnList, 0, tmid, lengthCalcTol) + if (math.fabs((curLength / float(length)) - 1) < lengthCompareTol): + return tmid + else: + if (curLength > length): + return _GetTValueAtLengthHelper(fnList, length, tinit, tmid, lengthCalcTol, lengthCompareTol) + else: + return _GetTValueAtLengthHelper(fnList, length, tmid, tfinal, lengthCalcTol, lengthCompareTol) + +def GetTValueAtLengthHelper(fnList, length, tinit, tfinal, lengthCalcTol, lengthCompareTol): + """Iterative version of above. Finds the midpoint of the current section of spline + and zeros in until the length desired is found. + """ + stack = [(tinit, tfinal)] + while(len(stack) > 0): + tif = stack.pop() + tmid = (tif[0] + tif[1])/float(2) + curLength = GetLengthHelper(fnList, 0, tmid, lengthCalcTol) + if(math.fabs((curLength/float(length)) -1) < lengthCompareTol): + return tmid + else: + #NOTE: try using math.decimal package to avoid floating point errors at really small lengths. + if (curLength > length): + stack.append((tif[0], tmid)) + #print("Length: " + str(curLength) + ", Point: " + str(stack[-1])) #debugging... + else: + stack.append((tmid, tif[1])) + #print("Length: " + str(curLength) + ", Point: " +str(stack[-1])) diff --git a/RankPanda/GUIDialogs.py b/RankPanda/GUIDialogs.py index 157daa7..9dc5b70 100755 --- a/RankPanda/GUIDialogs.py +++ b/RankPanda/GUIDialogs.py @@ -846,14 +846,12 @@ def __init__(self, parent): # begin layout code self.directionPanel = wx.BoxSizer(wx.HORIZONTAL) self.directionChoices = ['Clockwise', 'Counter-clockwise'] - #self.directionChoicesValues = [0, 1] #These aren't really needed I dont think self.directionRadioBox = wx.RadioBox(self, wx.ID_ANY, "Direction", choices = self.directionChoices, majorDimension = 2, style = wx.RA_SPECIFY_COLS) self.directionRadioBox.SetSelection(0) # clockwise selected by default self.directionPanel.Add(self.directionRadioBox, 1) self.pivotPanel = wx.BoxSizer(wx.HORIZONTAL) self.pivotChoices = ['At arrow ', 'At point'] - #self.pivotChoicesValues = [0, 1] self.pivotRadioBox = wx.RadioBox(self, wx.ID_ANY, "Non-Pivot Point", choices = self.pivotChoices, majorDimension = 2, style = wx.RA_SPECIFY_COLS) self.pivotRadioBox.SetSelection(0) # arrow selected by default self.pivotPanel.Add(self.pivotRadioBox, 1) @@ -909,8 +907,8 @@ def OnOK(self, event): d.ShowModal() d.Destroy() return - #self.output = (self.directionChoicesValues[self.directionRadioBox.GetSelection()], self.pivotChoicesValues[self.pivotRadioBox.GetSelection()] , length) - self.output = (self.directionRadioBox.GetSelection(), self.pivotRadioBox.GetSelection(), length) + self.output = (self.directionRadioBox.GetSelection(),\ + self.pivotRadioBox.GetSelection(), length) self.EndModal(0) def OnCancel(self, event): diff --git a/RankPanda/GUIField.py b/RankPanda/GUIField.py index 6fef87f..b68dbab 100644 --- a/RankPanda/GUIField.py +++ b/RankPanda/GUIField.py @@ -1,1272 +1,1272 @@ -#!/usr/bin/env python -#GUI Field: contains controls for the field, as well as rendering field thumbnails - -import wx -#import CoreWrapper -import RankLocation -import Point - -# Debug constants -DEBUG = False -DEBUG_RANK_COLOUR = wx.Colour(127, 63, 0) - -# print constants -PRINT_WIDTH = 880.0 -PRINT_HEIGHT = 425.0 - -# field constants -FIELD_LENGTH_STEPS = 176.0 -FIELD_WIDTH_STEPS = 85.0# + 1.0/3.0 -FIELD_RATIO = FIELD_LENGTH_STEPS/FIELD_WIDTH_STEPS - -RANK_COLOUR = wx.Colour(0, 0, 0) -RANK_DRAW_COLOUR = wx.Colour(0, 0, 255) -SELECTED_RANK_COLOUR = wx.Colour(255, 0, 0) -RANK_START_COLOUR = wx.Colour(0, 255, 0) -RANK_END_COLOUR = wx.Colour(255, 0, 255) -RANK_CALCULATED_COLOUR = wx.Colour(127, 127, 0) - -MINI_FIELD_SIZE = (176, 85) - -# field line constants -FIELD_HORIZONTAL_SPACING_STEPS = 4.0 -FIELD_VERTICAL_SPACING_STEPS = 4.0 -FIELD_FRONT_HASH = 32.0 -FIELD_BACK_HASH = FIELD_WIDTH_STEPS - 32.0 - -FIELD_LINE_YARDLINE_COLOUR = wx.Colour(63, 63, 63) -FIELD_LINE_MAJOR_COLOUR = wx.Colour(95, 95, 95) -FIELD_LINE_MINOR_COLOUR = wx.Colour(127, 127, 127) -FIELD_LINE_HASH_COLOUR = wx.Colour(63, 63, 63) - -FIELD_LINE_YARDLINE_WIDTH = 3.0 -FIELD_LINE_MAJOR_WIDTH = 2.0 -FIELD_LINE_MINOR_WIDTH = 1.0 -FIELD_LINE_HASH_WIDTH = 3.0 - -MINI_FIELD_LINE_YARDLINE_WIDTH = 1.0 -MINI_FIELD_LINE_MAJOR_WIDTH = 1.0 -MINI_FIELD_LINE_MINOR_WIDTH = 0.0 -MINI_FIELD_LINE_HASH_WIDTH = 1.0 - -# field number constants -FIELD_NUMBER_FRONT_FRONT = 11.0 -FIELD_NUMBER_FRONT_BACK = 14.0 -FIELD_NUMBER_BACK_FRONT = FIELD_WIDTH_STEPS - FIELD_NUMBER_FRONT_FRONT -FIELD_NUMBER_BACK_BACK = FIELD_WIDTH_STEPS - FIELD_NUMBER_FRONT_BACK - -FIELD_NUMBER_HEIGHT = FIELD_NUMBER_FRONT_BACK - FIELD_NUMBER_FRONT_FRONT -FIELD_NUMBER_SPACE = 0.05 - -FIELD_NUMBER_COLOUR = wx.Colour(63, 63, 63) - -# drawing constants -ENDPOINT_RADIUS = 6.0 -SPLINEPOINT_RADIUS = 4.0 -LINE_WIDTH = 4.0 -LINEPOINT_RADIUS = 2.0 -ARROW_SEPARATION = 6.0 -ARROW_LENGTH = 12.0 - -MINI_ENDPOINT_RADIUS = 3.0 -MINI_SPLINEPOINT_RADIUS = 2.0 -MINI_LINE_WIDTH = 2.0 -MINI_LINEPOINT_RADIUS = 1.0 -MINI_ARROW_SEPARATION = 3.0 -MINI_ARROW_LENGTH = 6.0 - -NAME_COLOUR = wx.Colour(0, 127, 127) -SELECTED_NAME_COLOUR = wx.Colour(255, 127, 0) -NAME_CALCULATED_COLOUR = wx.Colour(127, 127, 0) - -NAME_RECT_WIDTH = 10.0 -NAME_RECT_HEIGHT = 20.0 -NAME_DIST = 8.0 -MINI_NAME_DIST = 2.5 # this one is not used as names are not drawn in the minifields - -# mode constants -RANK_OFF = 0.0 -RANK_ADD = 1.0 -RANK_DRAG_POINT = 2.0 -RANK_DRAG_LINE = 3.0 -FTA_DRAG_WAYPOINT = 4.0 - -# DTP constants -DTP_START_COLOUR = wx.Colour(0, 255, 0) -DTP_END_COLOUR = wx.Colour(255, 0, 255) - -# FTA constants -FTA_START_COLOUR = wx.Colour(0, 255, 0) -FTA_END_COLOUR = wx.Colour(255, 0, 255) - -FTA_WAYPOINT_COLOUR = wx.Colour(0, 127, 127) - -FTA_WAYPOINT_RADIUS = 6.0 - -FTA_WAYPOINT_NUMBER_COLOUR = wx.Colour(0, 127, 127) - -FTA_NUMBER_RECT_WIDTH = 10.0 -FTA_NUMBER_RECT_HEIGHT = 20.0 -FTA_NUMBER_DIST = 15.0 - - -class Field(wx.Panel): - - def __init__(self, parent, id, style, main): - wx.Panel.__init__(self, parent, id, style = style) - self.main = main - self.fieldNumbersFont = wx.Font(72,wx.FONTFAMILY_MODERN,wx.FONTSTYLE_NORMAL,wx.FONTWEIGHT_BOLD, False, u'DejaVuSans') - self.rankNameFont = wx.Font(12, wx.FONTFAMILY_TELETYPE, wx.FONTSTYLE_NORMAL, wx.BOLD, False, u'DejaVuSansMono') - - # init field numbers - self.InitNumbers() - - # mode variables - self.rankMode = RANK_OFF - self.rankOldPos = (0, 0) - - self.firstPoint = (0, 0) - self.secondPoint = (0, 0) - - self.fieldRect = None - - # init stuff - self.OnResize(None) - -# DEBUG - if DEBUG: - self.DisplayRanksOn = False -# DEBUG - - def OnPaint(self, event): - self.Draw() - fielddc = wx.PaintDC(self) - fielddc.DrawBitmap(self.paintBitmap, 0, 0, False) # manual double-buffering since nothing else seems to work... - - def Draw(self): - self.paintBitmap = wx.EmptyBitmap(*self.GetSizeTuple()) - dc = wx.MemoryDC() - dc.SetFont(wx.Font(10,wx.FONTFAMILY_MODERN,wx.FONTSTYLE_NORMAL,wx.FONTWEIGHT_NORMAL, faceName="DejaVu Sans")) - dc.SelectObject(self.paintBitmap) - - dc.SetBrush(wx.Brush('black')) - dc.DrawRectangle(0, 0, *self.GetSizeTuple()) - - dc.SetClippingRect(self.fieldRect) - - dc.SetBrush(wx.Brush('white')) -# dc.DrawRectangleRect(self.fieldRect) - dc.Clear() - - self.DrawFieldNumbers(self.fieldRect, dc) - self.DrawFieldLines(self.fieldRect, dc) - - dc.DestroyClippingRegion() - - additional = self.main.core.GetAdditionalRanks() - self.additionalStart = [] - self.additionalEnd = [] - - for r in additional: - if r[3] == 'Begin': - self.additionalStart.append(r) - elif r[3] == 'End': - self.additionalEnd.append(r) - - self.DrawRanks(self.fieldRect, self.additionalStart, RANK_START_COLOUR, dc) - self.DrawRanks(self.fieldRect, self.additionalEnd, RANK_END_COLOUR, dc) - if self.main.animranks == None: -# not animating - self.DrawRanks(self.fieldRect, self.main.core.GetCalculatedRanks(), RANK_CALCULATED_COLOUR, dc) - self.DrawRanks(self.fieldRect, self.main.core.GetRanks(), RANK_COLOUR, dc) - self.DrawRanks(self.fieldRect, self.main.core.GetSelectedRanks(), SELECTED_RANK_COLOUR, dc) - - self.DrawRankNames(self.fieldRect, self.main.core.GetCalculatedRanks(), NAME_CALCULATED_COLOUR, dc) - self.DrawRankNames(self.fieldRect, self.main.core.GetRanks(), NAME_COLOUR, dc) - self.DrawRankNames(self.fieldRect, self.main.core.GetSelectedRanks(), SELECTED_NAME_COLOUR, dc) - - else: -# is animating - self.DrawRanks(self.fieldRect, self.main.animranks, RANK_COLOUR, dc) - self.DrawRankNames(self.fieldRect, self.main.animranks, NAME_COLOUR, dc) - -# draw initial point if user is adding a rank - - if self.rankMode == RANK_ADD: - dc.SetPen(wx.Pen(RANK_DRAW_COLOUR, 0)) - dc.SetBrush(wx.Brush(RANK_DRAW_COLOUR)) - x1, y1 = self.firstPoint - x2, y2 = self.secondPoint - dc.DrawCircle(x1, y1, ENDPOINT_RADIUS) - dc.DrawCircle(x2, y2, ENDPOINT_RADIUS) - dc.SetPen(wx.Pen(RANK_DRAW_COLOUR, LINE_WIDTH)) - dc.DrawLine(x1, y1, x2, y2) - -# DEBUG - if DEBUG: - if self.DisplayRanksOn: - for l in self.main.core.DisplayRanks(): - self.DrawLocation(self.fieldRect, l, dc, DEBUG_RANK_COLOUR) -# DEBUG - - def OnResize(self, event): - self.fieldRect = self.AspectRatioRect(self.GetRect()) - self.ScaleNumbers(self.fieldRect) - - self.Refresh(False) - - def OnLeftClick(self, event): - self.SetFocus() - if not self.InFieldRect(event.m_x, event.m_y): - return - - if event.ControlDown(): - self.rankMode = RANK_ADD -# self.firstPoint = (event.m_x, event.m_y) -# snap to grid - self.firstPoint = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) - self.secondPoint = self.firstPoint - else: - id = self.PickSpline(self.main.core.GetSelectedRanks(), event.m_x, event.m_y) - if id is not None: - self.rankMode = RANK_DRAG_POINT - self.draggedPoint = id -# self.rankPos = (event.m_x, event.m_y) -# snap to grid - self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) - self.main.core.PointGrabbed(*id) - else: - id = self.PickRank(self.main.core.GetSelectedRanks(), event.m_x, event.m_y) - if id is not None: - self.rankMode = RANK_DRAG_LINE -# self.rankPos = (event.m_x, event.m_y) -# snap to grid - self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) - self.main.core.RanksGrabbed() - else: - self.rankMode = RANK_OFF - id = self.PickSpline(self.main.core.GetRanks(), event.m_x, event.m_y) - if id is not None: - self.main.core.RankClicked(id[0], event.ShiftDown()) # deselect all ranks if shift isn't down - else: - id = self.PickRank(self.main.core.GetRanks(), event.m_x, event.m_y) - if id is not None: - self.main.core.RankClicked(id, event.ShiftDown()) # deselect all ranks if shift isn't down - elif not event.ShiftDown(): - self.main.core.FieldClicked() # deselect all ranks if shift isn't down - - self.main.RefreshRankList() - self.main.RefreshCommandList() - - # need to repaint field after a click - self.Refresh(False) - - def OnRightClick(self, event): - self.SetFocus() - self.OnLeftUnclick(event) # simulate left release to avoid weirdness such as deleting a spline point while it is being dragged - - if self.rankMode != RANK_OFF: - return - - spline = self.PickSpline(self.main.core.GetSelectedRanks(), event.m_x, event.m_y) - if spline is not None: - if event.ShiftDown(): - self.main.core.RankDeleteSpline(*spline) - else: - self.main.core.RankAddSpline(*spline) - - self.Refresh(False) - self.main.RefreshCurrentMove() - - def OnLeftUnclick(self, event): - if self.rankMode == RANK_ADD: - x, y = self.firstPoint - -# firstPt = Point.Point(self.RX(self.fieldRect, x), self.RY(self.fieldRect, y)) -# secondPt = Point.Point(self.RX(self.fieldRect, event.m_x), self.RY(self.fieldRect, event.m_y)) -# snap to grid - firstPt = Point.Point(round(self.RX(self.fieldRect, x)), round(self.RY(self.fieldRect, y))) - secondPt = Point.Point(round(self.RX(self.fieldRect, event.m_x)), round(self.RY(self.fieldRect, event.m_y))) - - self.main.core.RankDrawn([firstPt, secondPt]) - - elif self.rankMode == RANK_DRAG_POINT: - x, y = self.rankPos -# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) -# snap to grid - delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) - - self.main.core.PointDragged(self.draggedPoint[0], self.draggedPoint[1], delta[0], delta[1]) - self.main.core.PointDropped(*self.draggedPoint) - self.main.RefreshCommandList() - elif self.rankMode == RANK_DRAG_LINE: - x, y = self.rankPos -# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) -# snap to grid - delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) - - self.main.core.RanksDragged(*delta) - self.main.core.RanksDropped() - self.main.RefreshCommandList() - - self.rankMode = RANK_OFF - self.Refresh(False) - self.main.RefreshCurrentMove() - - def OnRightUnclick(self, event): - pass - - def OnMouseMove(self, event): - if not self.InFieldRect(event.m_x, event.m_y): - self.OnMouseExit(event) - else: - if self.rankMode == RANK_ADD: -# self.secondPoint = (event.m_x, event.m_y) -# snap to grid - self.secondPoint = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) - - elif self.rankMode == RANK_DRAG_POINT: - x, y = self.rankPos -# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) -# snap to grid - delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) -# self.rankPos = (event.m_x, event.m_y) -# snap to grid - self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) - - self.main.core.PointDragged(self.draggedPoint[0], self.draggedPoint[1], delta[0], delta[1]) - elif self.rankMode == RANK_DRAG_LINE: - x, y = self.rankPos -# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) -# snap to grid - delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) -# self.rankPos = (event.m_x, event.m_y) -# snap to grid - self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) - - self.main.core.RanksDragged(*delta) - - self.Refresh(False) -# don't refresh every frame... -# self.main.RefreshCurrentMove() - - def OnMouseExit(self, event): - if self.rankMode == RANK_DRAG_POINT: - self.main.core.PointDropped(*self.draggedPoint) - self.main.RefreshCommandList() - elif self.rankMode == RANK_DRAG_LINE: - self.main.core.RanksDropped() - self.main.RefreshCommandList() - - self.rankMode = RANK_OFF - self.Refresh(False) - self.main.RefreshCurrentMove() - - - def RenderSet(self, ranks): - bitmap = wx.EmptyBitmap(*MINI_FIELD_SIZE) - dc = wx.MemoryDC() - dc.SelectObject(bitmap) - - rect = self.AspectRatioRect(wx.Rect(0, 0, *MINI_FIELD_SIZE)) - - dc.SetBrush(wx.Brush('black')) - dc.DrawRectangle(0, 0, *MINI_FIELD_SIZE) - -# now, we make the background white - dc.SetClippingRect(rect) - - dc.SetBrush(wx.Brush('white')) - dc.DrawRectangleRect(rect) -# dc.Clear() # we use the old line so that we get a thin black border - - self.DrawFieldLines(rect, dc, True) - - dc.DestroyClippingRegion() - - self.DrawRanks(self.fieldRect, self.main.core.GetCalculatedRanks(), RANK_CALCULATED_COLOUR, dc, True) - self.DrawRanks(rect, ranks, RANK_COLOUR, dc, True) -# self.DrawRankNames(self.fieldRect, self.main.core.GetCalculatedRanks(), NAME_CALCULATED_COLOUR, dc, True) -# self.DrawRankNames(rect, ranks, NAME_COLOUR, dc, True) - - return bitmap - - def PrintBitmap(self): - bitmap = wx.EmptyBitmap(PRINT_WIDTH, PRINT_HEIGHT) - dc = wx.MemoryDC() - dc.SelectObject(bitmap) - - dc.SetBrush(wx.Brush('white')) - dc.Clear() - - tempRect = wx.Rect(0, 0, PRINT_WIDTH, PRINT_HEIGHT) - - self.ScaleNumbers(tempRect) - self.DrawFieldNumbers(tempRect, dc) - self.DrawFieldLines(tempRect, dc) - - dc.DestroyClippingRegion() - - self.DrawRanks(tempRect, self.main.core.GetRanks(), wx.Colour(0,0,0), dc) - self.DrawRankNames(tempRect, self.main.core.GetRanks(), wx.Colour(0,0,0), dc) - - self.OnResize(None) # re-scale the numbers back to normal - - return bitmap - - def DrawFieldLines(self, rect, dc, mini = False): - if mini: - field_line_yardline_width = MINI_FIELD_LINE_YARDLINE_WIDTH - field_line_hash_width = MINI_FIELD_LINE_HASH_WIDTH - field_line_major_width = MINI_FIELD_LINE_MAJOR_WIDTH - field_line_minor_width = MINI_FIELD_LINE_MINOR_WIDTH - else: - field_line_yardline_width = FIELD_LINE_YARDLINE_WIDTH - field_line_hash_width = FIELD_LINE_HASH_WIDTH - field_line_major_width = FIELD_LINE_MAJOR_WIDTH - field_line_minor_width = FIELD_LINE_MINOR_WIDTH - - horizontalStep = self.T(rect, FIELD_HORIZONTAL_SPACING_STEPS) - verticalStep = self.T(rect, FIELD_VERTICAL_SPACING_STEPS) - - horizontalStart = rect.GetX() + rect.GetWidth() / 2.0 - fieldBottom = rect.GetY() - fieldTop = rect.GetY() + rect.GetHeight() - - verticalStart = rect.GetY() + rect.GetHeight() # we start from the front, and should DECREMENT y-pos per line - fieldLeft = rect.GetX() - fieldRight = rect.GetX() + rect.GetWidth() - - isMajor = False - isAugmented = False - - for i in range(1, int((FIELD_LENGTH_STEPS / (2.0 * FIELD_HORIZONTAL_SPACING_STEPS)) + 1)): - if isMajor: - if isAugmented: - dc.SetPen(wx.Pen(FIELD_LINE_YARDLINE_COLOUR, field_line_yardline_width)) - isAugmented = False - else: - dc.SetPen(wx.Pen(FIELD_LINE_MAJOR_COLOUR, field_line_major_width)) - isAugmented = True - isMajor = False - else: - dc.SetPen(wx.Pen(FIELD_LINE_MINOR_COLOUR, field_line_minor_width)) - isMajor = True - - if dc.GetPen().GetWidth() > 0 : - dc.DrawLine(horizontalStart + i * horizontalStep, fieldBottom, horizontalStart + i * horizontalStep, fieldTop) - dc.DrawLine(horizontalStart - i * horizontalStep, fieldBottom, horizontalStart - i * horizontalStep, fieldTop) - - isMajor = True - - for i in range(0, int((FIELD_WIDTH_STEPS / FIELD_VERTICAL_SPACING_STEPS) + 1)): - if isMajor: - dc.SetPen(wx.Pen(FIELD_LINE_MAJOR_COLOUR, field_line_major_width)) - isMajor = False - else: - dc.SetPen(wx.Pen(FIELD_LINE_MINOR_COLOUR, field_line_minor_width)) - isMajor = True - - # Note: Need to SUBTRACT from vertical position to move toward the back of the field! - if dc.GetPen().GetWidth() > 0 : - dc.DrawLine(fieldLeft, verticalStart - i * verticalStep, fieldRight, verticalStart - i * verticalStep) - - dc.SetPen(wx.Pen(FIELD_LINE_YARDLINE_COLOUR, field_line_yardline_width)) - dc.DrawLine(horizontalStart, fieldBottom, horizontalStart, fieldTop) - - dc.SetPen(wx.Pen(FIELD_LINE_HASH_COLOUR, field_line_hash_width, wx.LONG_DASH)) - dc.DrawLine(fieldLeft, self.TY(rect, FIELD_FRONT_HASH), fieldRight, self.TY(rect, FIELD_FRONT_HASH)) - dc.DrawLine(fieldLeft, self.TY(rect, FIELD_BACK_HASH), fieldRight, self.TY(rect, FIELD_BACK_HASH)) - - def InitNumbers(self): - self.fixednumbers = [] - for i in range(10): - dc = wx.MemoryDC() - dc.SetFont(self.fieldNumbersFont) - textdim = dc.GetTextExtent(str(i)) - textdim = (textdim[0], textdim[1]) - bitmap = wx.EmptyBitmap(*textdim) - dc.SelectObject(bitmap) - dc.SetBrush(wx.Brush('white')) - dc.Clear() - dc.SetTextForeground(FIELD_NUMBER_COLOUR) - textrect = wx.Rect(0, 0, *textdim) - dc.DrawImageLabel(str(i), wx.NullBitmap, textrect, wx.ALIGN_CENTRE) - self.fixednumbers.append((bitmap.ConvertToImage(), textdim[0], textdim[1])) - - def ScaleNumbers(self, rect): - self.numbers = [] - self.numbersheight = self.T(rect, FIELD_NUMBER_HEIGHT) + 2 # + self.T(rect, 10.0) - for i in range(20): # we store 180-rotated images at index i + 10 - if i < 10: - w = self.fixednumbers[i][1] * float(self.numbersheight) / self.fixednumbers[i][2] - self.numbers.append((self.fixednumbers[i][0].Scale(w, self.numbersheight, wx.IMAGE_QUALITY_HIGH).ConvertToBitmap(), w)) - else: # store the 180-rotated one - w = self.fixednumbers[i - 10][1] * float(self.numbersheight) / self.fixednumbers[i - 10][2] - self.numbers.append((self.fixednumbers[i - 10][0].Scale(w, self.numbersheight, wx.IMAGE_QUALITY_HIGH).Rotate90().Rotate90().ConvertToBitmap(), w)) - - def DrawFieldNumbers(self, rect, dc, mini = False): - horizontalStep = 2 * self.T(rect, FIELD_HORIZONTAL_SPACING_STEPS) - - horizontalStart = rect.GetX() + rect.GetWidth() / 2.0 - fieldBottom = rect.GetY() - fieldTop = rect.GetY() + rect.GetHeight() - - fronty = self.TY(rect, FIELD_NUMBER_FRONT_FRONT) - self.numbersheight - backy = self.TY(rect, FIELD_NUMBER_BACK_BACK) - self.numbersheight - space = self.T(rect, FIELD_NUMBER_SPACE) + (self.numbers[5][1] / 2.0) - - dc.DrawBitmap(self.numbers[5][0], horizontalStart - self.numbers[5][1] - space, fronty, False) - dc.DrawBitmap(self.numbers[0][0], horizontalStart + space, fronty, False) - dc.DrawBitmap(self.numbers[10][0], horizontalStart - self.numbers[5][1] - space, backy, False) - dc.DrawBitmap(self.numbers[15][0], horizontalStart + space, backy, False) - - number1 = 4 - number0 = 5 - -# self.ScaleNumbers(rect) # since we only call this when drawing the field proper, we scale the images in OnResize to save computations - - for i in range(1, int((FIELD_LENGTH_STEPS / (4.0 * FIELD_HORIZONTAL_SPACING_STEPS)) + 1)): - dc.DrawBitmap(self.numbers[number1][0], horizontalStart + i * horizontalStep - self.numbers[number1][1] - space, fronty, False) - dc.DrawBitmap(self.numbers[number1][0], horizontalStart - i * horizontalStep - self.numbers[number1][1] - space, fronty, False) - dc.DrawBitmap(self.numbers[number0][0], horizontalStart + i * horizontalStep + space, fronty, False) - dc.DrawBitmap(self.numbers[number0][0], horizontalStart - i * horizontalStep + space, fronty, False) - dc.DrawBitmap(self.numbers[number0 + 10][0], horizontalStart + i * horizontalStep - self.numbers[number1][1] - space, backy, False) - dc.DrawBitmap(self.numbers[number0 + 10][0], horizontalStart - i * horizontalStep - self.numbers[number1][1] - space, backy, False) - dc.DrawBitmap(self.numbers[number1 + 10][0], horizontalStart + i * horizontalStep + space, backy, False) - dc.DrawBitmap(self.numbers[number1 + 10][0], horizontalStart - i * horizontalStep + space, backy, False) - - number0 -= 5 - if number0 < 0: - number0 += 10 - number1 -= 1 - if number1 < 0: - break - - def DrawRanks(self, rect, ranks, colour, dc, mini = False): - if ranks is not None: - for r in ranks: - self.DrawLocation(rect, r[2], dc, colour, mini) - - def DrawLocation(self, rect, loc, dc, colour, mini = False): - if mini: - endpoint_radius = MINI_ENDPOINT_RADIUS - splinepoint_radius = MINI_SPLINEPOINT_RADIUS - line_width = MINI_LINE_WIDTH - linepoint_radius = MINI_LINEPOINT_RADIUS - arrow_separation = MINI_ARROW_SEPARATION - arrow_length = MINI_ARROW_LENGTH - else: - endpoint_radius = ENDPOINT_RADIUS - splinepoint_radius = SPLINEPOINT_RADIUS - line_width = LINE_WIDTH - linepoint_radius = LINEPOINT_RADIUS - arrow_separation = ARROW_SEPARATION - arrow_length = ARROW_LENGTH - - if loc.IsStraight(): - pts = self.TList(rect, loc.GetListOfPoints()) - - dc.SetPen(wx.Pen(colour, 0)) - dc.SetBrush(wx.Brush(colour)) - dc.DrawCircle(pts[0].x, pts[0].y, endpoint_radius) - dc.DrawCircle(pts[1].x, pts[1].y, endpoint_radius) - - dc.SetPen(wx.Pen(colour, line_width)) - dc.DrawLine(pts[0].x, pts[0].y, pts[1].x, pts[1].y) - - dx = pts[1].x - pts[0].x - dy = pts[1].y - pts[0].y - dist = (dx ** 2 + dy ** 2) ** .5 - - newpt = (pts[0].x + dx * arrow_length / dist, pts[0].y + dy * arrow_length / dist) - - arr = (-dy * arrow_separation / dist, dx * arrow_separation / dist) - - dc.SetPen(wx.Pen(colour, line_width)) - dc.DrawLine(pts[0].x, pts[0].y, newpt[0] + arr[0], newpt[1] + arr[1]) - dc.DrawLine(pts[0].x, pts[0].y, newpt[0] - arr[0], newpt[1] - arr[1]) - elif loc.curved: - pts = self.TList(rect, loc.GetListOfPoints()) - plen = len(pts) - - dc.SetPen(wx.Pen(colour, 0)) - dc.SetBrush(wx.Brush(colour)) - dc.DrawCircle(pts[0].x, pts[0].y, endpoint_radius) - dc.DrawCircle(pts[plen - 1].x, pts[plen - 1].y, endpoint_radius) - - for i in range(1, plen - 1): - dc.DrawCircle(pts[i].x, pts[i].y, splinepoint_radius) - - pts = self.TList2(rect, loc.GetListOfDrawingPoints()) - for p in pts: - dc.DrawCircle(p.x, p.y, linepoint_radius) - - dx = pts[1].x - pts[0].x - dy = pts[1].y - pts[0].y - dist = (dx ** 2 + dy ** 2) ** .5 - - newpt = (pts[0].x + dx * arrow_length / dist, pts[0].y + dy * arrow_length / dist) - - arr = (-dy * arrow_separation / dist, dx * arrow_separation / dist) - - dc.SetPen(wx.Pen(colour, line_width)) - dc.DrawLine(pts[0].x, pts[0].y, newpt[0] + arr[0], newpt[1] + arr[1]) - dc.DrawLine(pts[0].x, pts[0].y, newpt[0] - arr[0], newpt[1] - arr[1]) - else: # connect-the-dots - pts = self.TList(rect, loc.GetListOfPoints()) - plen = len(pts) - - dc.SetPen(wx.Pen(colour, 0)) - dc.SetBrush(wx.Brush(colour)) - dc.DrawCircle(pts[0].x, pts[0].y, endpoint_radius) - dc.DrawCircle(pts[plen - 1].x, pts[plen - 1].y, endpoint_radius) - - for i in range(1, plen): - dc.SetPen(wx.Pen(colour, 0)) - dc.DrawCircle(pts[i].x, pts[i].y, splinepoint_radius) - dc.SetPen(wx.Pen(colour, line_width)) - dc.DrawLine(pts[i - 1].x, pts[i - 1].y, pts[i].x, pts[i].y) - - dx = pts[1].x - pts[0].x - dy = pts[1].y - pts[0].y - dist = (dx ** 2 + dy ** 2) ** .5 - - newpt = (pts[0].x + dx * arrow_length / dist, pts[0].y + dy * arrow_length / dist) - - arr = (-dy * arrow_separation / dist, dx * arrow_separation / dist) - - dc.SetPen(wx.Pen(colour, line_width)) - dc.DrawLine(pts[0].x, pts[0].y, newpt[0] + arr[0], newpt[1] + arr[1]) - dc.DrawLine(pts[0].x, pts[0].y, newpt[0] - arr[0], newpt[1] - arr[1]) - - def DrawRankNames(self, rect, ranks, colour, dc, mini = False): - dc.SetTextForeground(colour) - - if mini: - name_separation = MINI_NAME_DIST - else: - name_separation = NAME_DIST - - dc.SetFont(self.rankNameFont) - for r in ranks: - if r[-1]: - inner_name_separation = name_separation - else: - inner_name_separation = -name_separation - - if r[1] is not None: - pts = self.TList(rect, r[2].GetListOfPoints()) - if len(pts) % 2 == 1: - middlei = int((len(pts) - 1) / 2) - middle = pts[middlei] - middle1 = pts[middlei - 1] - middle2 = pts[middlei - 2] - - (dx, dy) = (middle1.x - middle2.x, middle1.y - middle2.y) - dist = (dx ** 2 + dy ** 2) ** .5 - - newpt = (middle.x - dy * inner_name_separation / dist, middle.y + dx * inner_name_separation / dist) - - textrect = wx.Rect(newpt[0] - NAME_RECT_WIDTH / 2, newpt[1] - NAME_RECT_HEIGHT / 2, NAME_RECT_WIDTH, NAME_RECT_HEIGHT) - else: - middle1 = pts[int((len(pts) - 1) / 2)] - middle2 = pts[int((len(pts) + 1) / 2)] - middle = (round((middle1.x + middle2.x) / 2.0), round((middle1.y + middle2.y) / 2.0)) - - (dx, dy) = (middle1.x - middle2.x, middle1.y - middle2.y) - dist = (dx ** 2 + dy ** 2) ** .5 - - newpt = (middle[0] - dy * inner_name_separation / dist, middle[1] + dx * inner_name_separation / dist) - - textrect = wx.Rect(newpt[0] - NAME_RECT_WIDTH / 2, newpt[1] - NAME_RECT_HEIGHT / 2, NAME_RECT_WIDTH, NAME_RECT_HEIGHT) - - dc.DrawImageLabel(r[1], wx.NullBitmap, textrect, wx.ALIGN_CENTRE) - - -# converts distance d in steps to pixels - def T(self, rect, d): - return d * rect.GetWidth() / FIELD_LENGTH_STEPS - -#def TranslateXStepsToPixel(x): # converts x in steps to pixels - def TX(self, rect, x): - return x * rect.GetWidth() / FIELD_LENGTH_STEPS + rect.GetX() -#def TranslateYStepsToPixel(y): # converts y in steps to pixels - def TY(self, rect, y): - return (FIELD_WIDTH_STEPS - y) * rect.GetHeight() / FIELD_WIDTH_STEPS + rect.GetY() - - def TList(self, rect, l): # converts from steps to counts for an entire list of points - l2 = [] - for i in l: - l2.append(Point.Point(self.TX(rect, i.x), self.TY(rect, i.y))) - return l2 - - def TList2(self, rect, l): # converts from steps to counts for an entire list of lists of points - l2 = [] - for i in l: - for j in i: - l2.append(Point.Point(self.TX(rect, j.x), self.TY(rect, j.y))) - return l2 - -# converts distance d in pixels to steps - def R(self, rect, d): - return d * FIELD_LENGTH_STEPS / rect.GetWidth() - -# converts x in pixels to steps - def RX(self, rect, x): - return (x - rect.GetX()) * FIELD_LENGTH_STEPS / rect.GetWidth() -# converts y in pixels to steps - def RY(self, rect, y): - return (rect.GetHeight() - y + rect.GetY()) * FIELD_WIDTH_STEPS / rect.GetHeight() - - -# returns rank id or None - def PickRank(self, ranks, mx, my): - x = self.RX(self.fieldRect, mx) - y = self.RY(self.fieldRect, my) - - for r in ranks: - loc = r[2] - - if loc.IsStraight(): - pts = loc.GetListOfPoints() - if self.PickLine(((pts[0].x, pts[0].y), (pts[1].x, pts[1].y)), (x, y), self.R(self.fieldRect, LINE_WIDTH)): - return r[0] - elif loc.curved: - pts = loc.GetListOfDrawingPoints() - for i in pts: - for j in i: - if self.PickCircle((j.x, j.y), (x, y), self.R(self.fieldRect, LINEPOINT_RADIUS)): - return r[0] - else: - pts = loc.GetListOfPoints() - for i in range(0, len(pts) - 1): - if self.PickLine(((pts[i].x, pts[i].y), (pts[i + 1].x, pts[i + 1].y)), (x, y), self.R(self.fieldRect, LINE_WIDTH)): - return r[0] - # failed to find a rank - return None - -# returns (rank id, spline pt index) or None - def PickSpline(self, ranks, mx, my): - x = self.RX(self.fieldRect, mx) - y = self.RY(self.fieldRect, my) - - for r in ranks: - loc = r[2] - pts = loc.GetListOfPoints() - - if loc.IsStraight(): - if self.PickCircle((pts[0].x, pts[0].y), (x, y), self.R(self.fieldRect, ENDPOINT_RADIUS)): - return (r[0], 0) - if self.PickCircle((pts[1].x, pts[1].y), (x, y), self.R(self.fieldRect, ENDPOINT_RADIUS)): - return (r[0], 1) - else: - plen = len(pts) - 1 - if self.PickCircle((pts[0].x, pts[0].y), (x, y), self.R(self.fieldRect, ENDPOINT_RADIUS)): - return (r[0], 0) - if self.PickCircle((pts[plen].x, pts[plen].y), (x, y), self.R(self.fieldRect, ENDPOINT_RADIUS)): - return (r[0], plen) - - for i in range(1, plen): - if self.PickCircle((pts[i].x, pts[i].y), (x, y), self.R(self.fieldRect, SPLINEPOINT_RADIUS)): - return (r[0], i) - # failed to find a pt - return None - - def PickLine(self, endpts, pt, d): - """ True if pt lies within distance d of the line defined by endpts """ - p1, p2 = endpts - x1, y1 = p1 - x2, y2 = p2 - x, y = pt - - if x1 == x2 and y1 == y2: - return False - - dist = ((x2 - x1)**2 + (y2 - y1)**2)**.5 - dx, dy = x2 - x1, y2 - y1 - dxt, dyt = x - x1, y - y1 - - proj = (dxt * dx + dyt * dy) / float(dist) - - # if projection extends beyond endpoint: - if proj > dist or proj < 0: - return False - else: - x1p, y1p = x1 + (dx * proj / float(dist)), y1 + (dy * proj / float(dist)) - return (x - x1p)**2 + (y - y1p)**2 <= d**2 - - def PickCircle(self, centre, pt, r): - """ True if pt lies within distance r of centre """ - xc, yc = centre - x, y = pt - return (x - xc)**2 + (y - yc)**2 <= r**2 - - def InFieldRect(self, x, y): - """ True if pt lies within the field rect """ - leftBound = self.fieldRect.GetX() - rightBound = leftBound + self.fieldRect.GetWidth() - lowerBound = self.fieldRect.GetY() - upperBound = lowerBound + self.fieldRect.GetHeight() - return x >= leftBound and x <= rightBound and y >= lowerBound and y <= upperBound - - def AspectRatioRect(self, rect): - newRect = wx.Rect(0, 0, rect.GetWidth(), rect.GetHeight()) - if float(newRect.GetWidth()) / newRect.GetHeight() < FIELD_RATIO: - newHeight = newRect.GetWidth() / FIELD_RATIO - diff = newRect.GetHeight() - newHeight - newRect.SetY(diff / 2.0) - newRect.SetHeight(newHeight) - else: - newWidth = newRect.GetHeight() * FIELD_RATIO - diff = newRect.GetWidth() - newWidth - newRect.SetX(diff / 2.0) - newRect.SetWidth(newWidth) - - return newRect - - -class DTPField(Field): - ''' Used for DTP Add dialog ''' - - def __init__(self, parent, id, style, main, DTPranks): - Field.__init__(self, parent, id, style, main) - - # init field numbers - self.InitNumbers() - - self.DTPranks = DTPranks - self.DTPstaticranks = [] - self.DTPeditranks = [] - - for r in self.DTPranks: - if r[3] == 'End': - self.DTPeditranks.append(r) - elif r[3] == 'Begin': - self.DTPstaticranks.append(r) - - self.point = 0 - - def Draw(self): - self.paintBitmap = wx.EmptyBitmap(*self.GetSizeTuple()) - dc = wx.MemoryDC() - dc.SetFont(wx.Font(10,wx.FONTFAMILY_MODERN,wx.FONTSTYLE_NORMAL,wx.FONTWEIGHT_NORMAL, faceName="DejaVu Sans")) - dc.SelectObject(self.paintBitmap) - - dc.SetBrush(wx.Brush('black')) - dc.DrawRectangle(0, 0, *self.GetSizeTuple()) - - dc.SetClippingRect(self.fieldRect) - - dc.SetBrush(wx.Brush('white')) -# dc.DrawRectangleRect(self.fieldRect) - dc.Clear() - - self.DrawFieldNumbers(self.fieldRect, dc) - self.DrawFieldLines(self.fieldRect, dc) - - dc.DestroyClippingRegion() - - self.DrawRanks(self.fieldRect, self.main.core.GetCalculatedRanks(), RANK_CALCULATED_COLOUR, dc) - self.DrawRanks(self.fieldRect, self.main.core.GetRanks(), RANK_COLOUR, dc) - self.DrawRanks(self.fieldRect, self.main.core.GetSelectedRanks(), SELECTED_RANK_COLOUR, dc) - - self.DrawRankNames(self.fieldRect, self.main.core.GetCalculatedRanks(), NAME_CALCULATED_COLOUR, dc) - self.DrawRankNames(self.fieldRect, self.main.core.GetRanks(), NAME_COLOUR, dc) - self.DrawRankNames(self.fieldRect, self.main.core.GetSelectedRanks(), SELECTED_NAME_COLOUR, dc) - - self.DrawRanks(self.fieldRect, self.DTPstaticranks, DTP_START_COLOUR, dc) - self.DrawRanks(self.fieldRect, self.DTPeditranks, DTP_END_COLOUR, dc) - - def OnLeftClick(self, event): - self.SetFocus() - if not self.InFieldRect(event.m_x, event.m_y): - return - - id = self.PickSpline(self.DTPeditranks, event.m_x, event.m_y) - if id is not None: - self.rankMode = RANK_DRAG_POINT - self.draggedPoint = id -# self.rankPos = (event.m_x, event.m_y) -# snap to grid - self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) - else: - id = self.PickRank(self.DTPeditranks, event.m_x, event.m_y) - if id is not None: - self.rankMode = RANK_DRAG_LINE -# self.rankPos = (event.m_x, event.m_y) -# snap to grid - self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) - else: - self.rankMode = RANK_OFF - - # need to repaint field after a click - self.Refresh(False) - - def OnRightClick(self, event): - self.SetFocus() - self.OnLeftUnclick(event) # simulate left release to avoid weirdness such as deleting a spline point while it is being dragged - - spline = self.PickSpline(self.DTPeditranks, event.m_x, event.m_y) - if spline is not None: - if event.ShiftDown(): - self.main.core.DTPDeletingSplinePoint(spline[1]) - else: - self.main.core.DTPAddingSplinePoint(spline[1]) - - self.Refresh(False) - - def OnLeftUnclick(self, event): - if self.rankMode == RANK_DRAG_POINT: - x, y = self.rankPos -# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) -# snap to grid - delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) - - self.main.core.AdjustDTPPoint(self.draggedPoint[1], delta[0], delta[1]) - elif self.rankMode == RANK_DRAG_LINE: - x, y = self.rankPos -# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) -# snap to grid - delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) - - self.main.core.AdjustDTPWhole(*delta) - - self.rankMode = RANK_OFF - self.Refresh(False) - - def OnRightUnclick(self, event): - pass - - def OnMouseMove(self, event): - if not self.InFieldRect(event.m_x, event.m_y): - self.OnMouseExit(event) - else: - if self.rankMode == RANK_DRAG_POINT: - x, y = self.rankPos -# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) -# snap to grid - delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) -# self.rankPos = (event.m_x, event.m_y) -# snap to grid - self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) - - self.main.core.AdjustDTPPoint(self.draggedPoint[1], delta[0], delta[1]) - elif self.rankMode == RANK_DRAG_LINE: - x, y = self.rankPos -# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) -# snap to grid - delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) -# self.rankPos = (event.m_x, event.m_y) -# snap to grid - self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) - - self.main.core.AdjustDTPWhole(*delta) - - self.Refresh(False) -# don't refresh every frame... -# self.main.RefreshCurrentMove() - - def OnMouseExit(self, event): - self.rankMode = RANK_OFF - self.Refresh(False) - - -class FTAField(Field): - ''' Used for FTA Add dialog ''' - - def __init__(self, parent, id, style, main, endpoint, FTAranks): # endpoint is True (1) or False (0) - Field.__init__(self, parent, id, style, main) - self.waypointFont = wx.Font(12, wx.FONTFAMILY_TELETYPE, wx.FONTSTYLE_NORMAL, wx.BOLD, False, u'DejaVuSansMono') - - # init field numbers - self.InitNumbers() - - self.endpoint = endpoint - self.FTAranks = FTAranks - self.FTAstaticranks = [] - self.FTAwaypoints = [] - self.FTAeditranks = [] - -# for r in self.FTAranks: -# if r[3] == 'End': -# self.FTAeditranks.append(r) -# elif r[3] == 'Begin': -# self.FTAstaticranks.append(r) - -# we assume that FTAs will only ever be added for one rank at a time - self.FTAeditranks.append(self.FTAranks[2]) - self.FTAwaypoints = self.FTAranks[1] - self.FTAstaticranks.append(self.FTAranks[0]) - - self.point = 0 - - def Draw(self): - self.paintBitmap = wx.EmptyBitmap(*self.GetSizeTuple()) - dc = wx.MemoryDC() - dc.SetFont(wx.Font(10,wx.FONTFAMILY_MODERN,wx.FONTSTYLE_NORMAL,wx.FONTWEIGHT_NORMAL, faceName="DejaVu Sans")) - dc.SelectObject(self.paintBitmap) - - dc.SetBrush(wx.Brush('black')) - dc.DrawRectangle(0, 0, *self.GetSizeTuple()) - - dc.SetClippingRect(self.fieldRect) - - dc.SetBrush(wx.Brush('white')) -# dc.DrawRectangleRect(self.fieldRect) - dc.Clear() - - self.DrawFieldNumbers(self.fieldRect, dc) - self.DrawFieldLines(self.fieldRect, dc) - - dc.DestroyClippingRegion() - - self.DrawRanks(self.fieldRect, self.main.core.GetCalculatedRanks(), RANK_CALCULATED_COLOUR, dc) - self.DrawRanks(self.fieldRect, self.main.core.GetRanks(), RANK_COLOUR, dc) - self.DrawRanks(self.fieldRect, self.main.core.GetSelectedRanks(), SELECTED_RANK_COLOUR, dc) - - self.DrawRankNames(self.fieldRect, self.main.core.GetCalculatedRanks(), NAME_CALCULATED_COLOUR, dc) - self.DrawRankNames(self.fieldRect, self.main.core.GetRanks(), NAME_COLOUR, dc) - self.DrawRankNames(self.fieldRect, self.main.core.GetSelectedRanks(), SELECTED_NAME_COLOUR, dc) - - self.DrawRanks(self.fieldRect, self.FTAstaticranks, FTA_START_COLOUR, dc) - self.DrawRanks(self.fieldRect, self.FTAeditranks, FTA_END_COLOUR, dc) - self.DrawWaypoints(self.fieldRect, self.FTAwaypoints, FTA_WAYPOINT_COLOUR, FTA_WAYPOINT_NUMBER_COLOUR, dc) - - def OnLeftClick(self, event): - self.SetFocus() - if not self.InFieldRect(event.m_x, event.m_y): - return - - if event.ControlDown(): -# self.firstPoint = (event.m_x, event.m_y) -# snap to grid - point = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) - if self.endpoint: - self.main.core.FTA1AddingWayPoint(self.RX(self.fieldRect, point[0]), self.RY(self.fieldRect, point[1])) - else: - self.main.core.FTA0AddingWayPoint(self.RX(self.fieldRect, point[0]), self.RY(self.fieldRect, point[1])) - else: - id = self.PickSpline(self.FTAeditranks, event.m_x, event.m_y) - if id is not None: - self.rankMode = RANK_DRAG_POINT - self.draggedPoint = id -# self.rankPos = (event.m_x, event.m_y) -# snap to grid - self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) - else: - id = self.PickRank(self.FTAeditranks, event.m_x, event.m_y) - if id is not None: - self.rankMode = RANK_DRAG_LINE -# self.rankPos = (event.m_x, event.m_y) -# snap to grid - self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) - else: - self.rankMode = RANK_OFF - - i = 0 - x = self.RX(self.fieldRect, event.m_x) - y = self.RY(self.fieldRect, event.m_y) - for w in self.FTAwaypoints: - if self.PickCircle((w.x, w.y), (x, y), self.R(self.fieldRect, FTA_WAYPOINT_RADIUS)): - self.rankMode = FTA_DRAG_WAYPOINT - self.draggedPoint = i -# self.rankPos = (event.m_x, event.m_y) -# snap to grid - self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) - break - i += 1 - - # need to repaint field after a click - self.Refresh(False) - - def OnRightClick(self, event): - self.SetFocus() - self.OnLeftUnclick(event) # simulate left release to avoid weirdness such as deleting a spline point while it is being dragged - - if event.ShiftDown(): - i = 0 - x = self.RX(self.fieldRect, event.m_x) - y = self.RY(self.fieldRect, event.m_y) - for w in self.FTAwaypoints: - if self.PickCircle((w.x, w.y), (x, y), self.R(self.fieldRect, FTA_WAYPOINT_RADIUS)): - if self.endpoint: - self.main.core.FTA1DeleteWayPoint(i) - return - else: - self.main.core.FTA0DeleteWayPoint(i) - return - i += 1 - - spline = self.PickSpline(self.FTAeditranks, event.m_x, event.m_y) - if spline is not None: - if event.ShiftDown(): - if self.endpoint: - self.main.core.FTA1DeletingSplinePoint(spline[1]) - else: - self.main.core.FTA0DeletingSplinePoint(spline[1]) - else: - if self.endpoint: - self.main.core.FTA1AddingSplinePoint(spline[1]) - else: - self.main.core.FTA0AddingSplinePoint(spline[1]) - - self.Refresh(False) - - def OnLeftUnclick(self, event): - if self.rankMode == RANK_DRAG_POINT: - x, y = self.rankPos -# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) -# snap to grid - delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) - - if self.endpoint: - self.main.core.AdjustFTA1EndLocationPoint(self.draggedPoint[1], delta[0], delta[1]) - else: - self.main.core.AdjustFTA0EndLocationPoint(self.draggedPoint[1], delta[0], delta[1]) - elif self.rankMode == RANK_DRAG_LINE: - x, y = self.rankPos -# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) -# snap to grid - delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) - - if self.endpoint: - self.main.core.AdjustFTA1Whole(*delta) - else: - self.main.core.AdjustFTA0Whole(*delta) - elif self.rankMode == FTA_DRAG_WAYPOINT: - x, y = self.rankPos -# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) -# snap to grid - delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) - - if self.endpoint: - self.main.core.FTA1AdjustWayPoint(self.draggedPoint, *delta) - else: - self.main.core.FTA0AdjustWayPoint(self.draggedPoint, *delta) - - self.rankMode = RANK_OFF - self.Refresh(False) - - def OnRightUnclick(self, event): - pass - - def OnMouseMove(self, event): - if not self.InFieldRect(event.m_x, event.m_y): - self.OnMouseExit(event) - else: - if self.rankMode == RANK_DRAG_POINT: - x, y = self.rankPos -# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) -# snap to grid - delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) -# self.rankPos = (event.m_x, event.m_y) -# snap to grid - self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) - - if self.endpoint: - self.main.core.AdjustFTA1EndLocationPoint(self.draggedPoint[1], delta[0], delta[1]) - else: - self.main.core.AdjustFTA0EndLocationPoint(self.draggedPoint[1], delta[0], delta[1]) - elif self.rankMode == RANK_DRAG_LINE: - x, y = self.rankPos -# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) -# snap to grid - delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) -# self.rankPos = (event.m_x, event.m_y) -# snap to grid - self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) - - if self.endpoint: - self.main.core.AdjustFTA1Whole(*delta) - else: - self.main.core.AdjustFTA0Whole(*delta) - elif self.rankMode == FTA_DRAG_WAYPOINT: - x, y = self.rankPos -# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) -# snap to grid - delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) -# self.rankPos = (event.m_x, event.m_y) -# snap to grid - self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) - - if self.endpoint: - self.main.core.FTA1AdjustWayPoint(self.draggedPoint, *delta) - else: - self.main.core.FTA0AdjustWayPoint(self.draggedPoint, *delta) - - self.Refresh(False) -# don't refresh every frame... -# self.main.RefreshCurrentMove() - - def OnMouseExit(self, event): - self.rankMode = RANK_OFF - self.Refresh(False) - - def DrawWaypoints(self, rect, waypoints, colour, numbercolour, dc): - dc.SetPen(wx.Pen(colour, 0)) - dc.SetBrush(wx.Brush(colour)) - dc.SetTextForeground(colour) - i = 0 - - dc.SetFont(self.waypointFont) - for w in waypoints: - (x, y) = (self.TX(self.fieldRect, w.x), self.TY(self.fieldRect, w.y)) - dc.DrawCircle(x, y, FTA_WAYPOINT_RADIUS) - textrect = wx.Rect(x - FTA_NUMBER_RECT_WIDTH / 2, y - FTA_NUMBER_DIST - FTA_NUMBER_RECT_HEIGHT / 2, FTA_NUMBER_RECT_WIDTH, FTA_NUMBER_RECT_HEIGHT) - dc.DrawImageLabel(str(i), wx.NullBitmap, textrect, wx.ALIGN_CENTRE) - i += 1 - - -class StatusField(Field): - ''' Used for Ranks at Count dialog ''' - - def __init__(self, parent, id, style, main, count): - Field.__init__(self, parent, id, style, main) - - # init field numbers - self.InitNumbers() - - self.count = count - - def Draw(self): - self.paintBitmap = wx.EmptyBitmap(*self.GetSizeTuple()) - dc = wx.MemoryDC() - dc.SetFont(wx.Font(10,wx.FONTFAMILY_MODERN,wx.FONTSTYLE_NORMAL,wx.FONTWEIGHT_NORMAL, faceName="DejaVu Sans")) - dc.SelectObject(self.paintBitmap) - - dc.SetBrush(wx.Brush('black')) - dc.DrawRectangle(0, 0, *self.GetSizeTuple()) - - dc.SetClippingRect(self.fieldRect) - - dc.SetBrush(wx.Brush('white')) -# dc.DrawRectangleRect(self.fieldRect) - dc.Clear() - - self.DrawFieldNumbers(self.fieldRect, dc) - self.DrawFieldLines(self.fieldRect, dc) - - dc.DestroyClippingRegion() - - self.DrawRanks(self.fieldRect, self.main.core.DisplayStatusAtCount(self.count), RANK_COLOUR, dc) - - self.DrawRankNames(self.fieldRect, self.main.core.DisplayStatusAtCount(self.count), NAME_COLOUR, dc) - - def OnLeftClick(self, event): - pass - - def OnRightClick(self, event): - pass - - def OnLeftUnclick(self, event): - pass - - def OnRightUnclick(self, event): - pass - - def OnMouseMove(self, event): - pass - - def OnMouseExit(self, event): - pass +#!/usr/bin/env python +#GUI Field: contains controls for the field, as well as rendering field thumbnails + +import wx +#import CoreWrapper +import RankLocation +import Point + +# Debug constants +DEBUG = False +DEBUG_RANK_COLOUR = wx.Colour(127, 63, 0) + +# print constants +PRINT_WIDTH = 880.0 +PRINT_HEIGHT = 425.0 + +# field constants +FIELD_LENGTH_STEPS = 176.0 +FIELD_WIDTH_STEPS = 85.0# + 1.0/3.0 +FIELD_RATIO = FIELD_LENGTH_STEPS/FIELD_WIDTH_STEPS + +RANK_COLOUR = wx.Colour(0, 0, 0) +RANK_DRAW_COLOUR = wx.Colour(0, 0, 255) +SELECTED_RANK_COLOUR = wx.Colour(255, 0, 0) +RANK_START_COLOUR = wx.Colour(0, 255, 0) +RANK_END_COLOUR = wx.Colour(255, 0, 255) +RANK_CALCULATED_COLOUR = wx.Colour(127, 127, 0) + +MINI_FIELD_SIZE = (176, 85) + +# field line constants +FIELD_HORIZONTAL_SPACING_STEPS = 4.0 +FIELD_VERTICAL_SPACING_STEPS = 4.0 +FIELD_FRONT_HASH = 32.0 +FIELD_BACK_HASH = FIELD_WIDTH_STEPS - 32.0 + +FIELD_LINE_YARDLINE_COLOUR = wx.Colour(63, 63, 63) +FIELD_LINE_MAJOR_COLOUR = wx.Colour(95, 95, 95) +FIELD_LINE_MINOR_COLOUR = wx.Colour(127, 127, 127) +FIELD_LINE_HASH_COLOUR = wx.Colour(63, 63, 63) + +FIELD_LINE_YARDLINE_WIDTH = 3.0 +FIELD_LINE_MAJOR_WIDTH = 2.0 +FIELD_LINE_MINOR_WIDTH = 1.0 +FIELD_LINE_HASH_WIDTH = 3.0 + +MINI_FIELD_LINE_YARDLINE_WIDTH = 1.0 +MINI_FIELD_LINE_MAJOR_WIDTH = 1.0 +MINI_FIELD_LINE_MINOR_WIDTH = 0.0 +MINI_FIELD_LINE_HASH_WIDTH = 1.0 + +# field number constants +FIELD_NUMBER_FRONT_FRONT = 11.0 +FIELD_NUMBER_FRONT_BACK = 14.0 +FIELD_NUMBER_BACK_FRONT = FIELD_WIDTH_STEPS - FIELD_NUMBER_FRONT_FRONT +FIELD_NUMBER_BACK_BACK = FIELD_WIDTH_STEPS - FIELD_NUMBER_FRONT_BACK + +FIELD_NUMBER_HEIGHT = FIELD_NUMBER_FRONT_BACK - FIELD_NUMBER_FRONT_FRONT +FIELD_NUMBER_SPACE = 0.05 + +FIELD_NUMBER_COLOUR = wx.Colour(63, 63, 63) + +# drawing constants +ENDPOINT_RADIUS = 6.0 +SPLINEPOINT_RADIUS = 4.0 +LINE_WIDTH = 4.0 +LINEPOINT_RADIUS = 2.0 +ARROW_SEPARATION = 6.0 +ARROW_LENGTH = 12.0 + +MINI_ENDPOINT_RADIUS = 3.0 +MINI_SPLINEPOINT_RADIUS = 2.0 +MINI_LINE_WIDTH = 2.0 +MINI_LINEPOINT_RADIUS = 1.0 +MINI_ARROW_SEPARATION = 3.0 +MINI_ARROW_LENGTH = 6.0 + +NAME_COLOUR = wx.Colour(0, 127, 127) +SELECTED_NAME_COLOUR = wx.Colour(255, 127, 0) +NAME_CALCULATED_COLOUR = wx.Colour(127, 127, 0) + +NAME_RECT_WIDTH = 10.0 +NAME_RECT_HEIGHT = 20.0 +NAME_DIST = 8.0 +MINI_NAME_DIST = 2.5 # this one is not used as names are not drawn in the minifields + +# mode constants +RANK_OFF = 0.0 +RANK_ADD = 1.0 +RANK_DRAG_POINT = 2.0 +RANK_DRAG_LINE = 3.0 +FTA_DRAG_WAYPOINT = 4.0 + +# DTP constants +DTP_START_COLOUR = wx.Colour(0, 255, 0) +DTP_END_COLOUR = wx.Colour(255, 0, 255) + +# FTA constants +FTA_START_COLOUR = wx.Colour(0, 255, 0) +FTA_END_COLOUR = wx.Colour(255, 0, 255) + +FTA_WAYPOINT_COLOUR = wx.Colour(0, 127, 127) + +FTA_WAYPOINT_RADIUS = 6.0 + +FTA_WAYPOINT_NUMBER_COLOUR = wx.Colour(0, 127, 127) + +FTA_NUMBER_RECT_WIDTH = 10.0 +FTA_NUMBER_RECT_HEIGHT = 20.0 +FTA_NUMBER_DIST = 15.0 + + +class Field(wx.Panel): + + def __init__(self, parent, id, style, main): + wx.Panel.__init__(self, parent, id, style = style) + self.main = main + self.fieldNumbersFont = wx.Font(72,wx.FONTFAMILY_MODERN,wx.FONTSTYLE_NORMAL,wx.FONTWEIGHT_BOLD, False, u'DejaVuSans') + self.rankNameFont = wx.Font(12, wx.FONTFAMILY_TELETYPE, wx.FONTSTYLE_NORMAL, wx.BOLD, False, u'DejaVuSansMono') + + # init field numbers + self.InitNumbers() + + # mode variables + self.rankMode = RANK_OFF + self.rankOldPos = (0, 0) + + self.firstPoint = (0, 0) + self.secondPoint = (0, 0) + + self.fieldRect = None + + # init stuff + self.OnResize(None) + +# DEBUG + if DEBUG: + self.DisplayRanksOn = False +# DEBUG + + def OnPaint(self, event): + self.Draw() + fielddc = wx.PaintDC(self) + fielddc.DrawBitmap(self.paintBitmap, 0, 0, False) # manual double-buffering since nothing else seems to work... + + def Draw(self): + self.paintBitmap = wx.EmptyBitmap(*self.GetSizeTuple()) + dc = wx.MemoryDC() + dc.SetFont(wx.Font(10,wx.FONTFAMILY_MODERN,wx.FONTSTYLE_NORMAL,wx.FONTWEIGHT_NORMAL, faceName="DejaVu Sans")) + dc.SelectObject(self.paintBitmap) + + dc.SetBrush(wx.Brush('black')) + dc.DrawRectangle(0, 0, *self.GetSizeTuple()) + + dc.SetClippingRect(self.fieldRect) + + dc.SetBrush(wx.Brush('white')) +# dc.DrawRectangleRect(self.fieldRect) + dc.Clear() + + self.DrawFieldNumbers(self.fieldRect, dc) + self.DrawFieldLines(self.fieldRect, dc) + + dc.DestroyClippingRegion() + + additional = self.main.core.GetAdditionalRanks() + self.additionalStart = [] + self.additionalEnd = [] + + for r in additional: + if r[3] == 'Begin': + self.additionalStart.append(r) + elif r[3] == 'End': + self.additionalEnd.append(r) + + self.DrawRanks(self.fieldRect, self.additionalStart, RANK_START_COLOUR, dc) + self.DrawRanks(self.fieldRect, self.additionalEnd, RANK_END_COLOUR, dc) + if self.main.animranks == None: +# not animating + self.DrawRanks(self.fieldRect, self.main.core.GetCalculatedRanks(), RANK_CALCULATED_COLOUR, dc) + self.DrawRanks(self.fieldRect, self.main.core.GetRanks(), RANK_COLOUR, dc) + self.DrawRanks(self.fieldRect, self.main.core.GetSelectedRanks(), SELECTED_RANK_COLOUR, dc) + + self.DrawRankNames(self.fieldRect, self.main.core.GetCalculatedRanks(), NAME_CALCULATED_COLOUR, dc) + self.DrawRankNames(self.fieldRect, self.main.core.GetRanks(), NAME_COLOUR, dc) + self.DrawRankNames(self.fieldRect, self.main.core.GetSelectedRanks(), SELECTED_NAME_COLOUR, dc) + + else: +# is animating + self.DrawRanks(self.fieldRect, self.main.animranks, RANK_COLOUR, dc) + self.DrawRankNames(self.fieldRect, self.main.animranks, NAME_COLOUR, dc) + +# draw initial point if user is adding a rank + + if self.rankMode == RANK_ADD: + dc.SetPen(wx.Pen(RANK_DRAW_COLOUR, 0)) + dc.SetBrush(wx.Brush(RANK_DRAW_COLOUR)) + x1, y1 = self.firstPoint + x2, y2 = self.secondPoint + dc.DrawCircle(x1, y1, ENDPOINT_RADIUS) + dc.DrawCircle(x2, y2, ENDPOINT_RADIUS) + dc.SetPen(wx.Pen(RANK_DRAW_COLOUR, LINE_WIDTH)) + dc.DrawLine(x1, y1, x2, y2) + +# DEBUG + if DEBUG: + if self.DisplayRanksOn: + for l in self.main.core.DisplayRanks(): + self.DrawLocation(self.fieldRect, l, dc, DEBUG_RANK_COLOUR) +# DEBUG + + def OnResize(self, event): + self.fieldRect = self.AspectRatioRect(self.GetRect()) + self.ScaleNumbers(self.fieldRect) + + self.Refresh(False) + + def OnLeftClick(self, event): + self.SetFocus() + if not self.InFieldRect(event.m_x, event.m_y): + return + + if event.ControlDown(): + self.rankMode = RANK_ADD +# self.firstPoint = (event.m_x, event.m_y) +# snap to grid + self.firstPoint = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) + self.secondPoint = self.firstPoint + else: + id = self.PickSpline(self.main.core.GetSelectedRanks(), event.m_x, event.m_y) + if id is not None: + self.rankMode = RANK_DRAG_POINT + self.draggedPoint = id +# self.rankPos = (event.m_x, event.m_y) +# snap to grid + self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) + self.main.core.PointGrabbed(*id) + else: + id = self.PickRank(self.main.core.GetSelectedRanks(), event.m_x, event.m_y) + if id is not None: + self.rankMode = RANK_DRAG_LINE +# self.rankPos = (event.m_x, event.m_y) +# snap to grid + self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) + self.main.core.RanksGrabbed() + else: + self.rankMode = RANK_OFF + id = self.PickSpline(self.main.core.GetRanks(), event.m_x, event.m_y) + if id is not None: + self.main.core.RankClicked(id[0], event.ShiftDown()) # deselect all ranks if shift isn't down + else: + id = self.PickRank(self.main.core.GetRanks(), event.m_x, event.m_y) + if id is not None: + self.main.core.RankClicked(id, event.ShiftDown()) # deselect all ranks if shift isn't down + elif not event.ShiftDown(): + self.main.core.FieldClicked() # deselect all ranks if shift isn't down + + self.main.RefreshRankList() + self.main.RefreshCommandList() + + # need to repaint field after a click + self.Refresh(False) + + def OnRightClick(self, event): + self.SetFocus() + self.OnLeftUnclick(event) # simulate left release to avoid weirdness such as deleting a spline point while it is being dragged + + if self.rankMode != RANK_OFF: + return + + spline = self.PickSpline(self.main.core.GetSelectedRanks(), event.m_x, event.m_y) + if spline is not None: + if event.ShiftDown(): + self.main.core.RankDeleteSpline(*spline) + else: + self.main.core.RankAddSpline(*spline) + + self.Refresh(False) + self.main.RefreshCurrentMove() + + def OnLeftUnclick(self, event): + if self.rankMode == RANK_ADD: + x, y = self.firstPoint + +# firstPt = Point.Point(self.RX(self.fieldRect, x), self.RY(self.fieldRect, y)) +# secondPt = Point.Point(self.RX(self.fieldRect, event.m_x), self.RY(self.fieldRect, event.m_y)) +# snap to grid + firstPt = Point.Point(round(self.RX(self.fieldRect, x)), round(self.RY(self.fieldRect, y))) + secondPt = Point.Point(round(self.RX(self.fieldRect, event.m_x)), round(self.RY(self.fieldRect, event.m_y))) + + self.main.core.RankDrawn([firstPt, secondPt]) + + elif self.rankMode == RANK_DRAG_POINT: + x, y = self.rankPos +# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) +# snap to grid + delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) + + self.main.core.PointDragged(self.draggedPoint[0], self.draggedPoint[1], delta[0], delta[1]) + self.main.core.PointDropped(*self.draggedPoint) + self.main.RefreshCommandList() + elif self.rankMode == RANK_DRAG_LINE: + x, y = self.rankPos +# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) +# snap to grid + delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) + + self.main.core.RanksDragged(*delta) + self.main.core.RanksDropped() + self.main.RefreshCommandList() + + self.rankMode = RANK_OFF + self.Refresh(False) + self.main.RefreshCurrentMove() + + def OnRightUnclick(self, event): + pass + + def OnMouseMove(self, event): + if not self.InFieldRect(event.m_x, event.m_y): + self.OnMouseExit(event) + else: + if self.rankMode == RANK_ADD: +# self.secondPoint = (event.m_x, event.m_y) +# snap to grid + self.secondPoint = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) + + elif self.rankMode == RANK_DRAG_POINT: + x, y = self.rankPos +# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) +# snap to grid + delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) +# self.rankPos = (event.m_x, event.m_y) +# snap to grid + self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) + + self.main.core.PointDragged(self.draggedPoint[0], self.draggedPoint[1], delta[0], delta[1]) + elif self.rankMode == RANK_DRAG_LINE: + x, y = self.rankPos +# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) +# snap to grid + delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) +# self.rankPos = (event.m_x, event.m_y) +# snap to grid + self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) + + self.main.core.RanksDragged(*delta) + + self.Refresh(False) +# don't refresh every frame... +# self.main.RefreshCurrentMove() + + def OnMouseExit(self, event): + if self.rankMode == RANK_DRAG_POINT: + self.main.core.PointDropped(*self.draggedPoint) + self.main.RefreshCommandList() + elif self.rankMode == RANK_DRAG_LINE: + self.main.core.RanksDropped() + self.main.RefreshCommandList() + + self.rankMode = RANK_OFF + self.Refresh(False) + self.main.RefreshCurrentMove() + + + def RenderSet(self, ranks): + bitmap = wx.EmptyBitmap(*MINI_FIELD_SIZE) + dc = wx.MemoryDC() + dc.SelectObject(bitmap) + + rect = self.AspectRatioRect(wx.Rect(0, 0, *MINI_FIELD_SIZE)) + + dc.SetBrush(wx.Brush('black')) + dc.DrawRectangle(0, 0, *MINI_FIELD_SIZE) + +# now, we make the background white + dc.SetClippingRect(rect) + + dc.SetBrush(wx.Brush('white')) + dc.DrawRectangleRect(rect) +# dc.Clear() # we use the old line so that we get a thin black border + + self.DrawFieldLines(rect, dc, True) + + dc.DestroyClippingRegion() + + self.DrawRanks(self.fieldRect, self.main.core.GetCalculatedRanks(), RANK_CALCULATED_COLOUR, dc, True) + self.DrawRanks(rect, ranks, RANK_COLOUR, dc, True) +# self.DrawRankNames(self.fieldRect, self.main.core.GetCalculatedRanks(), NAME_CALCULATED_COLOUR, dc, True) +# self.DrawRankNames(rect, ranks, NAME_COLOUR, dc, True) + + return bitmap + + def PrintBitmap(self): + bitmap = wx.EmptyBitmap(PRINT_WIDTH, PRINT_HEIGHT) + dc = wx.MemoryDC() + dc.SelectObject(bitmap) + + dc.SetBrush(wx.Brush('white')) + dc.Clear() + + tempRect = wx.Rect(0, 0, PRINT_WIDTH, PRINT_HEIGHT) + + self.ScaleNumbers(tempRect) + self.DrawFieldNumbers(tempRect, dc) + self.DrawFieldLines(tempRect, dc) + + dc.DestroyClippingRegion() + + self.DrawRanks(tempRect, self.main.core.GetRanks(), wx.Colour(0,0,0), dc) + self.DrawRankNames(tempRect, self.main.core.GetRanks(), wx.Colour(0,0,0), dc) + + self.OnResize(None) # re-scale the numbers back to normal + + return bitmap + + def DrawFieldLines(self, rect, dc, mini = False): + if mini: + field_line_yardline_width = MINI_FIELD_LINE_YARDLINE_WIDTH + field_line_hash_width = MINI_FIELD_LINE_HASH_WIDTH + field_line_major_width = MINI_FIELD_LINE_MAJOR_WIDTH + field_line_minor_width = MINI_FIELD_LINE_MINOR_WIDTH + else: + field_line_yardline_width = FIELD_LINE_YARDLINE_WIDTH + field_line_hash_width = FIELD_LINE_HASH_WIDTH + field_line_major_width = FIELD_LINE_MAJOR_WIDTH + field_line_minor_width = FIELD_LINE_MINOR_WIDTH + + horizontalStep = self.T(rect, FIELD_HORIZONTAL_SPACING_STEPS) + verticalStep = self.T(rect, FIELD_VERTICAL_SPACING_STEPS) + + horizontalStart = rect.GetX() + rect.GetWidth() / 2.0 + fieldBottom = rect.GetY() + fieldTop = rect.GetY() + rect.GetHeight() + + verticalStart = rect.GetY() + rect.GetHeight() # we start from the front, and should DECREMENT y-pos per line + fieldLeft = rect.GetX() + fieldRight = rect.GetX() + rect.GetWidth() + + isMajor = False + isAugmented = False + + for i in range(1, int((FIELD_LENGTH_STEPS / (2.0 * FIELD_HORIZONTAL_SPACING_STEPS)) + 1)): + if isMajor: + if isAugmented: + dc.SetPen(wx.Pen(FIELD_LINE_YARDLINE_COLOUR, field_line_yardline_width)) + isAugmented = False + else: + dc.SetPen(wx.Pen(FIELD_LINE_MAJOR_COLOUR, field_line_major_width)) + isAugmented = True + isMajor = False + else: + dc.SetPen(wx.Pen(FIELD_LINE_MINOR_COLOUR, field_line_minor_width)) + isMajor = True + + if dc.GetPen().GetWidth() > 0 : + dc.DrawLine(horizontalStart + i * horizontalStep, fieldBottom, horizontalStart + i * horizontalStep, fieldTop) + dc.DrawLine(horizontalStart - i * horizontalStep, fieldBottom, horizontalStart - i * horizontalStep, fieldTop) + + isMajor = True + + for i in range(0, int((FIELD_WIDTH_STEPS / FIELD_VERTICAL_SPACING_STEPS) + 1)): + if isMajor: + dc.SetPen(wx.Pen(FIELD_LINE_MAJOR_COLOUR, field_line_major_width)) + isMajor = False + else: + dc.SetPen(wx.Pen(FIELD_LINE_MINOR_COLOUR, field_line_minor_width)) + isMajor = True + + # Note: Need to SUBTRACT from vertical position to move toward the back of the field! + if dc.GetPen().GetWidth() > 0 : + dc.DrawLine(fieldLeft, verticalStart - i * verticalStep, fieldRight, verticalStart - i * verticalStep) + + dc.SetPen(wx.Pen(FIELD_LINE_YARDLINE_COLOUR, field_line_yardline_width)) + dc.DrawLine(horizontalStart, fieldBottom, horizontalStart, fieldTop) + + dc.SetPen(wx.Pen(FIELD_LINE_HASH_COLOUR, field_line_hash_width, wx.LONG_DASH)) + dc.DrawLine(fieldLeft, self.TY(rect, FIELD_FRONT_HASH), fieldRight, self.TY(rect, FIELD_FRONT_HASH)) + dc.DrawLine(fieldLeft, self.TY(rect, FIELD_BACK_HASH), fieldRight, self.TY(rect, FIELD_BACK_HASH)) + + def InitNumbers(self): + self.fixednumbers = [] + for i in range(10): + dc = wx.MemoryDC() + dc.SetFont(self.fieldNumbersFont) + textdim = dc.GetTextExtent(str(i)) + textdim = (textdim[0], textdim[1]) + bitmap = wx.EmptyBitmap(*textdim) + dc.SelectObject(bitmap) + dc.SetBrush(wx.Brush('white')) + dc.Clear() + dc.SetTextForeground(FIELD_NUMBER_COLOUR) + textrect = wx.Rect(0, 0, *textdim) + dc.DrawImageLabel(str(i), wx.NullBitmap, textrect, wx.ALIGN_CENTRE) + self.fixednumbers.append((bitmap.ConvertToImage(), textdim[0], textdim[1])) + + def ScaleNumbers(self, rect): + self.numbers = [] + self.numbersheight = self.T(rect, FIELD_NUMBER_HEIGHT) + 2 # + self.T(rect, 10.0) + for i in range(20): # we store 180-rotated images at index i + 10 + if i < 10: + w = self.fixednumbers[i][1] * float(self.numbersheight) / self.fixednumbers[i][2] + self.numbers.append((self.fixednumbers[i][0].Scale(w, self.numbersheight, wx.IMAGE_QUALITY_HIGH).ConvertToBitmap(), w)) + else: # store the 180-rotated one + w = self.fixednumbers[i - 10][1] * float(self.numbersheight) / self.fixednumbers[i - 10][2] + self.numbers.append((self.fixednumbers[i - 10][0].Scale(w, self.numbersheight, wx.IMAGE_QUALITY_HIGH).Rotate90().Rotate90().ConvertToBitmap(), w)) + + def DrawFieldNumbers(self, rect, dc, mini = False): + horizontalStep = 2 * self.T(rect, FIELD_HORIZONTAL_SPACING_STEPS) + + horizontalStart = rect.GetX() + rect.GetWidth() / 2.0 + fieldBottom = rect.GetY() + fieldTop = rect.GetY() + rect.GetHeight() + + fronty = self.TY(rect, FIELD_NUMBER_FRONT_FRONT) - self.numbersheight + backy = self.TY(rect, FIELD_NUMBER_BACK_BACK) - self.numbersheight + space = self.T(rect, FIELD_NUMBER_SPACE) + (self.numbers[5][1] / 2.0) + + dc.DrawBitmap(self.numbers[5][0], horizontalStart - self.numbers[5][1] - space, fronty, False) + dc.DrawBitmap(self.numbers[0][0], horizontalStart + space, fronty, False) + dc.DrawBitmap(self.numbers[10][0], horizontalStart - self.numbers[5][1] - space, backy, False) + dc.DrawBitmap(self.numbers[15][0], horizontalStart + space, backy, False) + + number1 = 4 + number0 = 5 + +# self.ScaleNumbers(rect) # since we only call this when drawing the field proper, we scale the images in OnResize to save computations + + for i in range(1, int((FIELD_LENGTH_STEPS / (4.0 * FIELD_HORIZONTAL_SPACING_STEPS)) + 1)): + dc.DrawBitmap(self.numbers[number1][0], horizontalStart + i * horizontalStep - self.numbers[number1][1] - space, fronty, False) + dc.DrawBitmap(self.numbers[number1][0], horizontalStart - i * horizontalStep - self.numbers[number1][1] - space, fronty, False) + dc.DrawBitmap(self.numbers[number0][0], horizontalStart + i * horizontalStep + space, fronty, False) + dc.DrawBitmap(self.numbers[number0][0], horizontalStart - i * horizontalStep + space, fronty, False) + dc.DrawBitmap(self.numbers[number0 + 10][0], horizontalStart + i * horizontalStep - self.numbers[number1][1] - space, backy, False) + dc.DrawBitmap(self.numbers[number0 + 10][0], horizontalStart - i * horizontalStep - self.numbers[number1][1] - space, backy, False) + dc.DrawBitmap(self.numbers[number1 + 10][0], horizontalStart + i * horizontalStep + space, backy, False) + dc.DrawBitmap(self.numbers[number1 + 10][0], horizontalStart - i * horizontalStep + space, backy, False) + + number0 -= 5 + if number0 < 0: + number0 += 10 + number1 -= 1 + if number1 < 0: + break + + def DrawRanks(self, rect, ranks, colour, dc, mini = False): + if ranks is not None: + for r in ranks: + self.DrawLocation(rect, r[2], dc, colour, mini) + + def DrawLocation(self, rect, loc, dc, colour, mini = False): + if mini: + endpoint_radius = MINI_ENDPOINT_RADIUS + splinepoint_radius = MINI_SPLINEPOINT_RADIUS + line_width = MINI_LINE_WIDTH + linepoint_radius = MINI_LINEPOINT_RADIUS + arrow_separation = MINI_ARROW_SEPARATION + arrow_length = MINI_ARROW_LENGTH + else: + endpoint_radius = ENDPOINT_RADIUS + splinepoint_radius = SPLINEPOINT_RADIUS + line_width = LINE_WIDTH + linepoint_radius = LINEPOINT_RADIUS + arrow_separation = ARROW_SEPARATION + arrow_length = ARROW_LENGTH + + if loc.IsStraight(): + pts = self.TList(rect, loc.GetListOfPoints()) + + dc.SetPen(wx.Pen(colour, 0)) + dc.SetBrush(wx.Brush(colour)) + dc.DrawCircle(pts[0].x, pts[0].y, endpoint_radius) + dc.DrawCircle(pts[1].x, pts[1].y, endpoint_radius) + + dc.SetPen(wx.Pen(colour, line_width)) + dc.DrawLine(pts[0].x, pts[0].y, pts[1].x, pts[1].y) + + dx = pts[1].x - pts[0].x + dy = pts[1].y - pts[0].y + dist = (dx ** 2 + dy ** 2) ** .5 + + newpt = (pts[0].x + dx * arrow_length / dist, pts[0].y + dy * arrow_length / dist) + + arr = (-dy * arrow_separation / dist, dx * arrow_separation / dist) + + dc.SetPen(wx.Pen(colour, line_width)) + dc.DrawLine(pts[0].x, pts[0].y, newpt[0] + arr[0], newpt[1] + arr[1]) + dc.DrawLine(pts[0].x, pts[0].y, newpt[0] - arr[0], newpt[1] - arr[1]) + elif loc.curved: + pts = self.TList(rect, loc.GetListOfPoints()) + plen = len(pts) + + dc.SetPen(wx.Pen(colour, 0)) + dc.SetBrush(wx.Brush(colour)) + dc.DrawCircle(pts[0].x, pts[0].y, endpoint_radius) + dc.DrawCircle(pts[plen - 1].x, pts[plen - 1].y, endpoint_radius) + + for i in range(1, plen - 1): + dc.DrawCircle(pts[i].x, pts[i].y, splinepoint_radius) + + pts = self.TList2(rect, loc.GetListOfDrawingPoints()) + for p in pts: + dc.DrawCircle(p.x, p.y, linepoint_radius) + + dx = pts[1].x - pts[0].x + dy = pts[1].y - pts[0].y + dist = (dx ** 2 + dy ** 2) ** .5 + + newpt = (pts[0].x + dx * arrow_length / dist, pts[0].y + dy * arrow_length / dist) + + arr = (-dy * arrow_separation / dist, dx * arrow_separation / dist) + + dc.SetPen(wx.Pen(colour, line_width)) + dc.DrawLine(pts[0].x, pts[0].y, newpt[0] + arr[0], newpt[1] + arr[1]) + dc.DrawLine(pts[0].x, pts[0].y, newpt[0] - arr[0], newpt[1] - arr[1]) + else: # connect-the-dots + pts = self.TList(rect, loc.GetListOfPoints()) + plen = len(pts) + + dc.SetPen(wx.Pen(colour, 0)) + dc.SetBrush(wx.Brush(colour)) + dc.DrawCircle(pts[0].x, pts[0].y, endpoint_radius) + dc.DrawCircle(pts[plen - 1].x, pts[plen - 1].y, endpoint_radius) + + for i in range(1, plen): + dc.SetPen(wx.Pen(colour, 0)) + dc.DrawCircle(pts[i].x, pts[i].y, splinepoint_radius) + dc.SetPen(wx.Pen(colour, line_width)) + dc.DrawLine(pts[i - 1].x, pts[i - 1].y, pts[i].x, pts[i].y) + + dx = pts[1].x - pts[0].x + dy = pts[1].y - pts[0].y + dist = (dx ** 2 + dy ** 2) ** .5 + + newpt = (pts[0].x + dx * arrow_length / dist, pts[0].y + dy * arrow_length / dist) + + arr = (-dy * arrow_separation / dist, dx * arrow_separation / dist) + + dc.SetPen(wx.Pen(colour, line_width)) + dc.DrawLine(pts[0].x, pts[0].y, newpt[0] + arr[0], newpt[1] + arr[1]) + dc.DrawLine(pts[0].x, pts[0].y, newpt[0] - arr[0], newpt[1] - arr[1]) + + def DrawRankNames(self, rect, ranks, colour, dc, mini = False): + dc.SetTextForeground(colour) + + if mini: + name_separation = MINI_NAME_DIST + else: + name_separation = NAME_DIST + + dc.SetFont(self.rankNameFont) + for r in ranks: + if r[-1]: + inner_name_separation = name_separation + else: + inner_name_separation = -name_separation + + if r[1] is not None: + pts = self.TList(rect, r[2].GetListOfPoints()) + if len(pts) % 2 == 1: + middlei = int((len(pts) - 1) / 2) + middle = pts[middlei] + middle1 = pts[middlei - 1] + middle2 = pts[middlei - 2] + + (dx, dy) = (middle1.x - middle2.x, middle1.y - middle2.y) + dist = (dx ** 2 + dy ** 2) ** .5 + + newpt = (middle.x - dy * inner_name_separation / dist, middle.y + dx * inner_name_separation / dist) + + textrect = wx.Rect(newpt[0] - NAME_RECT_WIDTH / 2, newpt[1] - NAME_RECT_HEIGHT / 2, NAME_RECT_WIDTH, NAME_RECT_HEIGHT) + else: + middle1 = pts[int((len(pts) - 1) / 2)] + middle2 = pts[int((len(pts) + 1) / 2)] + middle = (round((middle1.x + middle2.x) / 2.0), round((middle1.y + middle2.y) / 2.0)) + + (dx, dy) = (middle1.x - middle2.x, middle1.y - middle2.y) + dist = (dx ** 2 + dy ** 2) ** .5 + + newpt = (middle[0] - dy * inner_name_separation / dist, middle[1] + dx * inner_name_separation / dist) + + textrect = wx.Rect(newpt[0] - NAME_RECT_WIDTH / 2, newpt[1] - NAME_RECT_HEIGHT / 2, NAME_RECT_WIDTH, NAME_RECT_HEIGHT) + + dc.DrawImageLabel(r[1], wx.NullBitmap, textrect, wx.ALIGN_CENTRE) + + +# converts distance d in steps to pixels + def T(self, rect, d): + return d * rect.GetWidth() / FIELD_LENGTH_STEPS + +#def TranslateXStepsToPixel(x): # converts x in steps to pixels + def TX(self, rect, x): + return x * rect.GetWidth() / FIELD_LENGTH_STEPS + rect.GetX() +#def TranslateYStepsToPixel(y): # converts y in steps to pixels + def TY(self, rect, y): + return (FIELD_WIDTH_STEPS - y) * rect.GetHeight() / FIELD_WIDTH_STEPS + rect.GetY() + + def TList(self, rect, l): # converts from steps to counts for an entire list of points + l2 = [] + for i in l: + l2.append(Point.Point(self.TX(rect, i.x), self.TY(rect, i.y))) + return l2 + + def TList2(self, rect, l): # converts from steps to counts for an entire list of lists of points + l2 = [] + for i in l: + for j in i: + l2.append(Point.Point(self.TX(rect, j.x), self.TY(rect, j.y))) + return l2 + +# converts distance d in pixels to steps + def R(self, rect, d): + return d * FIELD_LENGTH_STEPS / rect.GetWidth() + +# converts x in pixels to steps + def RX(self, rect, x): + return (x - rect.GetX()) * FIELD_LENGTH_STEPS / rect.GetWidth() +# converts y in pixels to steps + def RY(self, rect, y): + return (rect.GetHeight() - y + rect.GetY()) * FIELD_WIDTH_STEPS / rect.GetHeight() + + +# returns rank id or None + def PickRank(self, ranks, mx, my): + x = self.RX(self.fieldRect, mx) + y = self.RY(self.fieldRect, my) + + for r in ranks: + loc = r[2] + + if loc.IsStraight(): + pts = loc.GetListOfPoints() + if self.PickLine(((pts[0].x, pts[0].y), (pts[1].x, pts[1].y)), (x, y), self.R(self.fieldRect, LINE_WIDTH)): + return r[0] + elif loc.curved: + pts = loc.GetListOfDrawingPoints() + for i in pts: + for j in i: + if self.PickCircle((j.x, j.y), (x, y), self.R(self.fieldRect, LINEPOINT_RADIUS)): + return r[0] + else: + pts = loc.GetListOfPoints() + for i in range(0, len(pts) - 1): + if self.PickLine(((pts[i].x, pts[i].y), (pts[i + 1].x, pts[i + 1].y)), (x, y), self.R(self.fieldRect, LINE_WIDTH)): + return r[0] + # failed to find a rank + return None + +# returns (rank id, spline pt index) or None + def PickSpline(self, ranks, mx, my): + x = self.RX(self.fieldRect, mx) + y = self.RY(self.fieldRect, my) + + for r in ranks: + loc = r[2] + pts = loc.GetListOfPoints() + + if loc.IsStraight(): + if self.PickCircle((pts[0].x, pts[0].y), (x, y), self.R(self.fieldRect, ENDPOINT_RADIUS)): + return (r[0], 0) + if self.PickCircle((pts[1].x, pts[1].y), (x, y), self.R(self.fieldRect, ENDPOINT_RADIUS)): + return (r[0], 1) + else: + plen = len(pts) - 1 + if self.PickCircle((pts[0].x, pts[0].y), (x, y), self.R(self.fieldRect, ENDPOINT_RADIUS)): + return (r[0], 0) + if self.PickCircle((pts[plen].x, pts[plen].y), (x, y), self.R(self.fieldRect, ENDPOINT_RADIUS)): + return (r[0], plen) + + for i in range(1, plen): + if self.PickCircle((pts[i].x, pts[i].y), (x, y), self.R(self.fieldRect, SPLINEPOINT_RADIUS)): + return (r[0], i) + # failed to find a pt + return None + + def PickLine(self, endpts, pt, d): + """ True if pt lies within distance d of the line defined by endpts """ + p1, p2 = endpts + x1, y1 = p1 + x2, y2 = p2 + x, y = pt + + if x1 == x2 and y1 == y2: + return False + + dist = ((x2 - x1)**2 + (y2 - y1)**2)**.5 + dx, dy = x2 - x1, y2 - y1 + dxt, dyt = x - x1, y - y1 + + proj = (dxt * dx + dyt * dy) / float(dist) + + # if projection extends beyond endpoint: + if proj > dist or proj < 0: + return False + else: + x1p, y1p = x1 + (dx * proj / float(dist)), y1 + (dy * proj / float(dist)) + return (x - x1p)**2 + (y - y1p)**2 <= d**2 + + def PickCircle(self, centre, pt, r): + """ True if pt lies within distance r of centre """ + xc, yc = centre + x, y = pt + return (x - xc)**2 + (y - yc)**2 <= r**2 + + def InFieldRect(self, x, y): + """ True if pt lies within the field rect """ + leftBound = self.fieldRect.GetX() + rightBound = leftBound + self.fieldRect.GetWidth() + lowerBound = self.fieldRect.GetY() + upperBound = lowerBound + self.fieldRect.GetHeight() + return x >= leftBound and x <= rightBound and y >= lowerBound and y <= upperBound + + def AspectRatioRect(self, rect): + newRect = wx.Rect(0, 0, rect.GetWidth(), rect.GetHeight()) + if float(newRect.GetWidth()) / newRect.GetHeight() < FIELD_RATIO: + newHeight = newRect.GetWidth() / FIELD_RATIO + diff = newRect.GetHeight() - newHeight + newRect.SetY(diff / 2.0) + newRect.SetHeight(newHeight) + else: + newWidth = newRect.GetHeight() * FIELD_RATIO + diff = newRect.GetWidth() - newWidth + newRect.SetX(diff / 2.0) + newRect.SetWidth(newWidth) + + return newRect + + +class DTPField(Field): + ''' Used for DTP Add dialog ''' + + def __init__(self, parent, id, style, main, DTPranks): + Field.__init__(self, parent, id, style, main) + + # init field numbers + self.InitNumbers() + + self.DTPranks = DTPranks + self.DTPstaticranks = [] + self.DTPeditranks = [] + + for r in self.DTPranks: + if r[3] == 'End': + self.DTPeditranks.append(r) + elif r[3] == 'Begin': + self.DTPstaticranks.append(r) + + self.point = 0 + + def Draw(self): + self.paintBitmap = wx.EmptyBitmap(*self.GetSizeTuple()) + dc = wx.MemoryDC() + dc.SetFont(wx.Font(10,wx.FONTFAMILY_MODERN,wx.FONTSTYLE_NORMAL,wx.FONTWEIGHT_NORMAL, faceName="DejaVu Sans")) + dc.SelectObject(self.paintBitmap) + + dc.SetBrush(wx.Brush('black')) + dc.DrawRectangle(0, 0, *self.GetSizeTuple()) + + dc.SetClippingRect(self.fieldRect) + + dc.SetBrush(wx.Brush('white')) +# dc.DrawRectangleRect(self.fieldRect) + dc.Clear() + + self.DrawFieldNumbers(self.fieldRect, dc) + self.DrawFieldLines(self.fieldRect, dc) + + dc.DestroyClippingRegion() + + self.DrawRanks(self.fieldRect, self.main.core.GetCalculatedRanks(), RANK_CALCULATED_COLOUR, dc) + self.DrawRanks(self.fieldRect, self.main.core.GetRanks(), RANK_COLOUR, dc) + self.DrawRanks(self.fieldRect, self.main.core.GetSelectedRanks(), SELECTED_RANK_COLOUR, dc) + + self.DrawRankNames(self.fieldRect, self.main.core.GetCalculatedRanks(), NAME_CALCULATED_COLOUR, dc) + self.DrawRankNames(self.fieldRect, self.main.core.GetRanks(), NAME_COLOUR, dc) + self.DrawRankNames(self.fieldRect, self.main.core.GetSelectedRanks(), SELECTED_NAME_COLOUR, dc) + + self.DrawRanks(self.fieldRect, self.DTPstaticranks, DTP_START_COLOUR, dc) + self.DrawRanks(self.fieldRect, self.DTPeditranks, DTP_END_COLOUR, dc) + + def OnLeftClick(self, event): + self.SetFocus() + if not self.InFieldRect(event.m_x, event.m_y): + return + + id = self.PickSpline(self.DTPeditranks, event.m_x, event.m_y) + if id is not None: + self.rankMode = RANK_DRAG_POINT + self.draggedPoint = id +# self.rankPos = (event.m_x, event.m_y) +# snap to grid + self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) + else: + id = self.PickRank(self.DTPeditranks, event.m_x, event.m_y) + if id is not None: + self.rankMode = RANK_DRAG_LINE +# self.rankPos = (event.m_x, event.m_y) +# snap to grid + self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) + else: + self.rankMode = RANK_OFF + + # need to repaint field after a click + self.Refresh(False) + + def OnRightClick(self, event): + self.SetFocus() + self.OnLeftUnclick(event) # simulate left release to avoid weirdness such as deleting a spline point while it is being dragged + + spline = self.PickSpline(self.DTPeditranks, event.m_x, event.m_y) + if spline is not None: + if event.ShiftDown(): + self.main.core.DTPDeletingSplinePoint(spline[1]) + else: + self.main.core.DTPAddingSplinePoint(spline[1]) + + self.Refresh(False) + + def OnLeftUnclick(self, event): + if self.rankMode == RANK_DRAG_POINT: + x, y = self.rankPos +# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) +# snap to grid + delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) + + self.main.core.AdjustDTPPoint(self.draggedPoint[1], delta[0], delta[1]) + elif self.rankMode == RANK_DRAG_LINE: + x, y = self.rankPos +# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) +# snap to grid + delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) + + self.main.core.AdjustDTPWhole(*delta) + + self.rankMode = RANK_OFF + self.Refresh(False) + + def OnRightUnclick(self, event): + pass + + def OnMouseMove(self, event): + if not self.InFieldRect(event.m_x, event.m_y): + self.OnMouseExit(event) + else: + if self.rankMode == RANK_DRAG_POINT: + x, y = self.rankPos +# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) +# snap to grid + delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) +# self.rankPos = (event.m_x, event.m_y) +# snap to grid + self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) + + self.main.core.AdjustDTPPoint(self.draggedPoint[1], delta[0], delta[1]) + elif self.rankMode == RANK_DRAG_LINE: + x, y = self.rankPos +# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) +# snap to grid + delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) +# self.rankPos = (event.m_x, event.m_y) +# snap to grid + self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) + + self.main.core.AdjustDTPWhole(*delta) + + self.Refresh(False) +# don't refresh every frame... +# self.main.RefreshCurrentMove() + + def OnMouseExit(self, event): + self.rankMode = RANK_OFF + self.Refresh(False) + + +class FTAField(Field): + ''' Used for FTA Add dialog ''' + + def __init__(self, parent, id, style, main, endpoint, FTAranks): # endpoint is True (1) or False (0) + Field.__init__(self, parent, id, style, main) + self.waypointFont = wx.Font(12, wx.FONTFAMILY_TELETYPE, wx.FONTSTYLE_NORMAL, wx.BOLD, False, u'DejaVuSansMono') + + # init field numbers + self.InitNumbers() + + self.endpoint = endpoint + self.FTAranks = FTAranks + self.FTAstaticranks = [] + self.FTAwaypoints = [] + self.FTAeditranks = [] + +# for r in self.FTAranks: +# if r[3] == 'End': +# self.FTAeditranks.append(r) +# elif r[3] == 'Begin': +# self.FTAstaticranks.append(r) + +# we assume that FTAs will only ever be added for one rank at a time + self.FTAeditranks.append(self.FTAranks[2]) + self.FTAwaypoints = self.FTAranks[1] + self.FTAstaticranks.append(self.FTAranks[0]) + + self.point = 0 + + def Draw(self): + self.paintBitmap = wx.EmptyBitmap(*self.GetSizeTuple()) + dc = wx.MemoryDC() + dc.SetFont(wx.Font(10,wx.FONTFAMILY_MODERN,wx.FONTSTYLE_NORMAL,wx.FONTWEIGHT_NORMAL, faceName="DejaVu Sans")) + dc.SelectObject(self.paintBitmap) + + dc.SetBrush(wx.Brush('black')) + dc.DrawRectangle(0, 0, *self.GetSizeTuple()) + + dc.SetClippingRect(self.fieldRect) + + dc.SetBrush(wx.Brush('white')) +# dc.DrawRectangleRect(self.fieldRect) + dc.Clear() + + self.DrawFieldNumbers(self.fieldRect, dc) + self.DrawFieldLines(self.fieldRect, dc) + + dc.DestroyClippingRegion() + + self.DrawRanks(self.fieldRect, self.main.core.GetCalculatedRanks(), RANK_CALCULATED_COLOUR, dc) + self.DrawRanks(self.fieldRect, self.main.core.GetRanks(), RANK_COLOUR, dc) + self.DrawRanks(self.fieldRect, self.main.core.GetSelectedRanks(), SELECTED_RANK_COLOUR, dc) + + self.DrawRankNames(self.fieldRect, self.main.core.GetCalculatedRanks(), NAME_CALCULATED_COLOUR, dc) + self.DrawRankNames(self.fieldRect, self.main.core.GetRanks(), NAME_COLOUR, dc) + self.DrawRankNames(self.fieldRect, self.main.core.GetSelectedRanks(), SELECTED_NAME_COLOUR, dc) + + self.DrawRanks(self.fieldRect, self.FTAstaticranks, FTA_START_COLOUR, dc) + self.DrawRanks(self.fieldRect, self.FTAeditranks, FTA_END_COLOUR, dc) + self.DrawWaypoints(self.fieldRect, self.FTAwaypoints, FTA_WAYPOINT_COLOUR, FTA_WAYPOINT_NUMBER_COLOUR, dc) + + def OnLeftClick(self, event): + self.SetFocus() + if not self.InFieldRect(event.m_x, event.m_y): + return + + if event.ControlDown(): +# self.firstPoint = (event.m_x, event.m_y) +# snap to grid + point = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) + if self.endpoint: + self.main.core.FTA1AddingWayPoint(self.RX(self.fieldRect, point[0]), self.RY(self.fieldRect, point[1])) + else: + self.main.core.FTA0AddingWayPoint(self.RX(self.fieldRect, point[0]), self.RY(self.fieldRect, point[1])) + else: + id = self.PickSpline(self.FTAeditranks, event.m_x, event.m_y) + if id is not None: + self.rankMode = RANK_DRAG_POINT + self.draggedPoint = id +# self.rankPos = (event.m_x, event.m_y) +# snap to grid + self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) + else: + id = self.PickRank(self.FTAeditranks, event.m_x, event.m_y) + if id is not None: + self.rankMode = RANK_DRAG_LINE +# self.rankPos = (event.m_x, event.m_y) +# snap to grid + self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) + else: + self.rankMode = RANK_OFF + + i = 0 + x = self.RX(self.fieldRect, event.m_x) + y = self.RY(self.fieldRect, event.m_y) + for w in self.FTAwaypoints: + if self.PickCircle((w.x, w.y), (x, y), self.R(self.fieldRect, FTA_WAYPOINT_RADIUS)): + self.rankMode = FTA_DRAG_WAYPOINT + self.draggedPoint = i +# self.rankPos = (event.m_x, event.m_y) +# snap to grid + self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) + break + i += 1 + + # need to repaint field after a click + self.Refresh(False) + + def OnRightClick(self, event): + self.SetFocus() + self.OnLeftUnclick(event) # simulate left release to avoid weirdness such as deleting a spline point while it is being dragged + + if event.ShiftDown(): + i = 0 + x = self.RX(self.fieldRect, event.m_x) + y = self.RY(self.fieldRect, event.m_y) + for w in self.FTAwaypoints: + if self.PickCircle((w.x, w.y), (x, y), self.R(self.fieldRect, FTA_WAYPOINT_RADIUS)): + if self.endpoint: + self.main.core.FTA1DeleteWayPoint(i) + return + else: + self.main.core.FTA0DeleteWayPoint(i) + return + i += 1 + + spline = self.PickSpline(self.FTAeditranks, event.m_x, event.m_y) + if spline is not None: + if event.ShiftDown(): + if self.endpoint: + self.main.core.FTA1DeletingSplinePoint(spline[1]) + else: + self.main.core.FTA0DeletingSplinePoint(spline[1]) + else: + if self.endpoint: + self.main.core.FTA1AddingSplinePoint(spline[1]) + else: + self.main.core.FTA0AddingSplinePoint(spline[1]) + + self.Refresh(False) + + def OnLeftUnclick(self, event): + if self.rankMode == RANK_DRAG_POINT: + x, y = self.rankPos +# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) +# snap to grid + delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) + + if self.endpoint: + self.main.core.AdjustFTA1EndLocationPoint(self.draggedPoint[1], delta[0], delta[1]) + else: + self.main.core.AdjustFTA0EndLocationPoint(self.draggedPoint[1], delta[0], delta[1]) + elif self.rankMode == RANK_DRAG_LINE: + x, y = self.rankPos +# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) +# snap to grid + delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) + + if self.endpoint: + self.main.core.AdjustFTA1Whole(*delta) + else: + self.main.core.AdjustFTA0Whole(*delta) + elif self.rankMode == FTA_DRAG_WAYPOINT: + x, y = self.rankPos +# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) +# snap to grid + delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) + + if self.endpoint: + self.main.core.FTA1AdjustWayPoint(self.draggedPoint, *delta) + else: + self.main.core.FTA0AdjustWayPoint(self.draggedPoint, *delta) + + self.rankMode = RANK_OFF + self.Refresh(False) + + def OnRightUnclick(self, event): + pass + + def OnMouseMove(self, event): + if not self.InFieldRect(event.m_x, event.m_y): + self.OnMouseExit(event) + else: + if self.rankMode == RANK_DRAG_POINT: + x, y = self.rankPos +# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) +# snap to grid + delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) +# self.rankPos = (event.m_x, event.m_y) +# snap to grid + self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) + + if self.endpoint: + self.main.core.AdjustFTA1EndLocationPoint(self.draggedPoint[1], delta[0], delta[1]) + else: + self.main.core.AdjustFTA0EndLocationPoint(self.draggedPoint[1], delta[0], delta[1]) + elif self.rankMode == RANK_DRAG_LINE: + x, y = self.rankPos +# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) +# snap to grid + delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) +# self.rankPos = (event.m_x, event.m_y) +# snap to grid + self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) + + if self.endpoint: + self.main.core.AdjustFTA1Whole(*delta) + else: + self.main.core.AdjustFTA0Whole(*delta) + elif self.rankMode == FTA_DRAG_WAYPOINT: + x, y = self.rankPos +# delta = (self.R(self.fieldRect, event.m_x - x), -self.R(self.fieldRect, event.m_y - y)) +# snap to grid + delta = (round(self.R(self.fieldRect, event.m_x - x)), round(-self.R(self.fieldRect, event.m_y - y))) +# self.rankPos = (event.m_x, event.m_y) +# snap to grid + self.rankPos = (self.TX(self.fieldRect, round(self.RX(self.fieldRect, event.m_x))), self.TY(self.fieldRect, round(self.RY(self.fieldRect, event.m_y)))) + + if self.endpoint: + self.main.core.FTA1AdjustWayPoint(self.draggedPoint, *delta) + else: + self.main.core.FTA0AdjustWayPoint(self.draggedPoint, *delta) + + self.Refresh(False) +# don't refresh every frame... +# self.main.RefreshCurrentMove() + + def OnMouseExit(self, event): + self.rankMode = RANK_OFF + self.Refresh(False) + + def DrawWaypoints(self, rect, waypoints, colour, numbercolour, dc): + dc.SetPen(wx.Pen(colour, 0)) + dc.SetBrush(wx.Brush(colour)) + dc.SetTextForeground(colour) + i = 0 + + dc.SetFont(self.waypointFont) + for w in waypoints: + (x, y) = (self.TX(self.fieldRect, w.x), self.TY(self.fieldRect, w.y)) + dc.DrawCircle(x, y, FTA_WAYPOINT_RADIUS) + textrect = wx.Rect(x - FTA_NUMBER_RECT_WIDTH / 2, y - FTA_NUMBER_DIST - FTA_NUMBER_RECT_HEIGHT / 2, FTA_NUMBER_RECT_WIDTH, FTA_NUMBER_RECT_HEIGHT) + dc.DrawImageLabel(str(i), wx.NullBitmap, textrect, wx.ALIGN_CENTRE) + i += 1 + + +class StatusField(Field): + ''' Used for Ranks at Count dialog ''' + + def __init__(self, parent, id, style, main, count): + Field.__init__(self, parent, id, style, main) + + # init field numbers + self.InitNumbers() + + self.count = count + + def Draw(self): + self.paintBitmap = wx.EmptyBitmap(*self.GetSizeTuple()) + dc = wx.MemoryDC() + dc.SetFont(wx.Font(10,wx.FONTFAMILY_MODERN,wx.FONTSTYLE_NORMAL,wx.FONTWEIGHT_NORMAL, faceName="DejaVu Sans")) + dc.SelectObject(self.paintBitmap) + + dc.SetBrush(wx.Brush('black')) + dc.DrawRectangle(0, 0, *self.GetSizeTuple()) + + dc.SetClippingRect(self.fieldRect) + + dc.SetBrush(wx.Brush('white')) +# dc.DrawRectangleRect(self.fieldRect) + dc.Clear() + + self.DrawFieldNumbers(self.fieldRect, dc) + self.DrawFieldLines(self.fieldRect, dc) + + dc.DestroyClippingRegion() + + self.DrawRanks(self.fieldRect, self.main.core.DisplayStatusAtCount(self.count), RANK_COLOUR, dc) + + self.DrawRankNames(self.fieldRect, self.main.core.DisplayStatusAtCount(self.count), NAME_COLOUR, dc) + + def OnLeftClick(self, event): + pass + + def OnRightClick(self, event): + pass + + def OnLeftUnclick(self, event): + pass + + def OnRightUnclick(self, event): + pass + + def OnMouseMove(self, event): + pass + + def OnMouseExit(self, event): + pass diff --git a/RankPanda/GUIMain.py b/RankPanda/GUIMain.py index 05fea2a..d89c08a 100755 --- a/RankPanda/GUIMain.py +++ b/RankPanda/GUIMain.py @@ -1,1461 +1,1452 @@ -#!/usr/bin/env python -# GUI Main: RankPanda main window - -import os -import wx -import GUIDialogs -import GUIField -import GUIRankList -import GUITimeBar -import CoreWrapper -#import RankLocation -#import Point -import Printer - -DEBUG = False -# Idea: instead of displaying * for locked rank, how about use different colour? - -default_size = (800,600) - -ID_EXIT = 0 -ID_NEW = 1 -ID_OPEN = 2 -ID_CLOSE = 3 -ID_SAVE = 4 -ID_SAVE_AS = 5 -ID_HELP = 6 -ID_ABOUT = 7 -ID_UNDO = 8 -ID_REDO = 9 -ID_CUT = 10 -ID_COPY = 11 -ID_PASTE = 12 -ID_FIND = 13 -ID_FIND_REPLACE = 14 -ID_EXPORT_PDF = 15 -ID_SETTINGS = 16 -ID_FIELD = 17 -ID_COMMAND_LIST = 18 -ID_COMMAND_BUTTON_MOVE_UP = 19 -ID_COMMAND_BUTTON_MOVE_DOWN = 20 -ID_COMMAND_BUTTON_SPLIT = 21 -ID_COMMAND_BUTTON_MERGE = 22 -ID_COMMAND_BUTTON_DELETE = 23 -ID_MOVE_LIST = 24 -ID_MOVE_NEW = 25 -ID_MOVE_IMPORT = 26 -ID_MOVE_SPLIT = 27 -ID_MOVE_MERGE = 28 -ID_MOVE_SHIFT = 29 -ID_ANIM_TIMER = 30 -ID_EDIT = 31 -ID_EXPORT_IND = 32 -ID_MOVE_TEXT = 33 -ID_MOVE_TEXT_OVERWRITE = 34 - -class MainWindow(wx.Frame): - """ Main Window; derived from wx.Frame """ - - def __init__(self, parent): - wx.Frame.__init__(self, parent, wx.ID_ANY, "Rank Panda", size = default_size) - - self.SetFont(wx.Font(10,wx.FONTFAMILY_MODERN,wx.FONTSTYLE_NORMAL,wx.FONTWEIGHT_NORMAL, faceName="DejaVu Sans")) - - -# DEBUG - if DEBUG: - self.core = CoreWrapper.CoreWrapper("Debug", 128, [(1, 4)], [(1, 1)]) - self.filename = None - else: - d = GUIDialogs.ProgramOptionsDialog(self) - while True: - start = d.ShowModal() - - if start == 0: # user requested new - self.filename = None - f = GUIDialogs.SongCreationDialog(self) - if f.ShowModal() == 0: - try: - self.core = CoreWrapper.CoreWrapper(*f.output) - break - except NameError as exception: - e = wx.MessageDialog(self, "Exception thrown while creating song!\nRank Panda will now close", "Song Creation Exception", wx.OK) - e.ShowModal() - e.Destroy() - self.Close(True) # exit since we couldn't create a song - return - else: - pass - f.Destroy() - elif start == 1: # user requested open - loop = True - loopOuter = True - self.core = CoreWrapper.CoreWrapper("Dummy", 10, [(1, 4)], [(1, 1)]) - - while loop: - dirname = '' - e = wx.FileDialog(self, "Open File", dirname, "", "*.panda", wx.OPEN) - if e.ShowModal() == wx.ID_OK: - self.filename = os.path.join(e.GetDirectory(), e.GetFilename()) - if self.core.Load(self.filename) == -1: # load failed - f = wx.MessageDialog(self, "Unable to open file!", "File Access Exception", wx.OK) - f.ShowModal() - f.Destroy() - else: - loop = False - loopOuter = False - else: - loop = False - e.Destroy() - if not loopOuter: - break -# if start == -1: # user requested exit - else: - self.Close(True) - return - - d.Destroy() -# DEBUG - -# hopefully this will fix our flickering problems... -#self.SetDoubleBuffered(True) - -#self.CreateStatusBar(2) -#self.SetStatusText("Try these: Exit, Open, Help, About", 1) - - - # begin menubar code - filemenu = wx.Menu() - filemenu.Append(ID_OPEN, "&Open...", "Open something") - filemenu.Append(ID_NEW, "&New...", "Create a new whatsit") -# TODO(astory): find out if the following line is intentionally this way. - filemenu.Append(ID_EDIT, "E&dit...", "Edit current whatsit") - filemenu.Append(ID_CLOSE, "&Close", "Close current whatsit") - filemenu.AppendSeparator() - filemenu.Append(ID_SAVE, "&Save", "Save current whatsit") - filemenu.Append(ID_SAVE_AS, "Save &As...", "Save current whatsit as something else") - filemenu.AppendSeparator() - filemenu.Append(ID_EXPORT_PDF, "&Export to PDF...", "Export current whatsit to PDF") - filemenu.Append(ID_EXPORT_IND, "Export &Individual Ranks", "Export ranks to PDF") - filemenu.AppendSeparator() - filemenu.Append(ID_EXIT, "E&xit", "Click to exit") - -# editmenu = wx.Menu() -# editmenu.Append(ID_UNDO, "&Undo", "Undo something") -# editmenu.Append(ID_REDO, "&Redo", "Undo an undo") -# editmenu.AppendSeparator() -# editmenu.Append(ID_CUT, "Cut", "Cut something") -# editmenu.Append(ID_COPY, "&Copy", "Copy something") -# editmenu.Append(ID_PASTE, "&Paste", "Paste something") -# editmenu.AppendSeparator() -# editmenu.Append(ID_FIND, "&Find...", "Find something") -# editmenu.Append(ID_FIND_REPLACE, "Find and Rep&lace...", "Find and replace something") -# editmenu.AppendSeparator() -# settingsmenu = wx.Menu() -# settingsmenu.Append(wx.ID_ANY, "Settings go here!", "Indeed they do!") -# editmenu.AppendSubMenu(settingsmenu, "&Settings", "Settings menu") - - helpmenu = wx.Menu() - helpmenu.Append(ID_HELP, "&Help", "No help for you!") - helpmenu.AppendSeparator() - helpmenu.Append(ID_ABOUT, "&About", "About this program") - - movemenu = wx.Menu() - movemenu.Append(ID_MOVE_NEW, "&New...", "Create a new whatsit") - movemenu.Append(ID_MOVE_IMPORT, "&Import ranks...", "Import ranks") - movemenu.AppendSeparator() - movemenu.Append(ID_MOVE_SPLIT, "&Split...", "...") - movemenu.Append(ID_MOVE_MERGE, "&Merge with next...", "...") - movemenu.Append(ID_MOVE_SHIFT, "S&hift...", "...") - movemenu.AppendSeparator() - movemenu.Append(ID_MOVE_TEXT, "Set &Text...", "...") - movemenu.Append(ID_MOVE_TEXT_OVERWRITE, "Set Text &Overwrite...", "...") - - menubar = wx.MenuBar() - menubar.Append(filemenu, "&File") -# menubar.Append(editmenu, "&Edit") - menubar.Append(helpmenu, "&Help") - menubar.Append(movemenu, "&Move") - - self.SetMenuBar(menubar) - # end menubar code - - # begin event code - wx.EVT_MENU(self, ID_EXIT, self.OnExit) - wx.EVT_MENU(self, ID_EDIT, self.EditSong) - wx.EVT_MENU(self, ID_SAVE, self.OnSave) - wx.EVT_MENU(self, ID_SAVE_AS, self.OnSaveAs) - wx.EVT_MENU(self, ID_OPEN, self.OnOpen) - wx.EVT_MENU(self, ID_HELP, self.OnHelp) - wx.EVT_MENU(self, ID_ABOUT, self.OnAbout) - wx.EVT_MENU(self, ID_NEW, self.CreateSong) - wx.EVT_MENU(self, ID_MOVE_NEW, self.CreateMove) - wx.EVT_MENU(self, ID_MOVE_IMPORT, self.OnMoveImport) - wx.EVT_MENU(self, ID_MOVE_SPLIT, self.OnMoveSplit) - wx.EVT_MENU(self, ID_MOVE_MERGE, self.OnMoveMerge) - wx.EVT_MENU(self, ID_MOVE_SHIFT, self.OnMoveShift) - wx.EVT_MENU(self, ID_MOVE_TEXT, self.OnMoveText) - wx.EVT_MENU(self, ID_MOVE_TEXT_OVERWRITE, self.OnMoveTextOverwrite) - wx.EVT_MENU(self, ID_EXPORT_PDF, self.OnExport) - wx.EVT_MENU(self, ID_EXPORT_IND, self.OnIndividualExport) - # end event code - - # begin main window code - self.panel = wx.Panel(self, wx.ID_ANY) - self.panel.Bind(wx.EVT_CHAR, self.OnKey) - - self.field = GUIField.Field(self.panel, ID_FIELD, wx.BORDER_RAISED, self) - self.field.Bind(wx.EVT_PAINT, self.field.OnPaint) - self.field.Bind(wx.EVT_SIZE, self.field.OnResize) - self.field.Bind(wx.EVT_LEFT_DOWN, self.field.OnLeftClick) - self.field.Bind(wx.EVT_RIGHT_DOWN, self.field.OnRightClick) - self.field.Bind(wx.EVT_LEFT_UP, self.field.OnLeftUnclick) - self.field.Bind(wx.EVT_RIGHT_UP, self.field.OnRightUnclick) - self.field.Bind(wx.EVT_MOTION, self.field.OnMouseMove) - self.field.Bind(wx.EVT_LEAVE_WINDOW, self.field.OnMouseExit) - self.field.Bind(wx.EVT_CHAR, self.OnKey) -#self.field.Bind(wx.EVT_KILL_FOCUS, lambda x: self.field.SetFocus()) -#self.field.SetFocus() - -# self.statusBarShowLabel = wx.StaticText(self, wx.ID_ANY, "SHOW NAME") - self.statusBarSongLabel = wx.StaticText(self.panel, wx.ID_ANY, "") - self.statusBarMoveLabel = wx.StaticText(self.panel, wx.ID_ANY, "") -# self.statusBarCountLabel = wx.StaticText(self.panel, wx.ID_ANY, "") - - self.statusBar = wx.BoxSizer(wx.HORIZONTAL) - self.statusBar.Add((1,1), 1, wx.EXPAND) -# self.statusBar.Add(statusBarShowLabel, 2, wx.EXPAND) - self.statusBar.Add(self.statusBarSongLabel, 0, wx.ALIGN_CENTRE) - self.statusBar.Add((100,1), 1, wx.EXPAND) - self.statusBar.Add(self.statusBarMoveLabel, 0, wx.ALIGN_CENTRE) -# self.statusBar.Add((150,1), 0, wx.EXPAND) # reserve 150px for statusBarMoveLabel -# self.statusBar.Add(self.statusBarCountLabel, 2, wx.EXPAND) - self.statusBar.Add((1,1), 1, wx.EXPAND) - - self.RefreshStatusBar() - - self.fieldpanel = wx.BoxSizer(wx.VERTICAL) - self.fieldpanel.Add(self.statusBar, 0, wx.ALIGN_CENTER) - self.fieldpanel.Add(self.field, 1, wx.EXPAND) - - - - self.rankNameUnicode = wx.Button(self.panel, wx.ID_ANY, "Unicode") - self.rankNameUnicode.Bind(wx.EVT_BUTTON, self.OnRankNameUnicode) - self.holdRankButton = wx.BitmapButton(self.panel, wx.ID_ANY, wx.Bitmap('icons/holdicon.png')) - self.holdRankButton.Bind(wx.EVT_BUTTON, self.OnHoldRank) - self.curveRankButton = wx.BitmapButton(self.panel, wx.ID_ANY, wx.Bitmap('icons/curveicon.png')) - self.curveRankButton.Bind(wx.EVT_BUTTON, self.OnCurveRank) - self.straightRankButton = wx.BitmapButton(self.panel, wx.ID_ANY, wx.Bitmap('icons/zigzagicon.png')) - self.straightRankButton.Bind(wx.EVT_BUTTON, self.OnStraightRank) - self.switchEndpointsButton = wx.BitmapButton(self.panel, wx.ID_ANY, wx.Bitmap('icons/switchendpointsicon.png')) - self.switchEndpointsButton.Bind(wx.EVT_BUTTON, self.OnSwitchEndpoints) - self.switchLabelButton = wx.BitmapButton(self.panel, wx.ID_ANY, wx.Bitmap('icons/switchlabelicon.png')) - self.switchLabelButton.Bind(wx.EVT_BUTTON, self.OnSwitchLabel) -# TODO(astory): discuss whether this button should exist or not -# self.deleteRankButton = wx.BitmapButton(self.panel, wx.ID_ANY, wx.Bitmap('icons/deleteicon.png')) -# self.deleteRankButton.Bind(wx.EVT_BUTTON, self.OnDeleteRank) - self.snapEndButton = wx.BitmapButton(self.panel, wx.ID_ANY, wx.Bitmap('icons/snaptoendicon.png')) - self.snapEndButton.Bind(wx.EVT_BUTTON, self.OnSnapEnd) - self.snapBeginButton = wx.BitmapButton(self.panel, wx.ID_ANY, wx.Bitmap('icons/snaptobeginicon.png')) - self.snapBeginButton.Bind(wx.EVT_BUTTON, self.OnSnapBegin) - self.displayAtCountButton = wx.BitmapButton(self.panel, wx.ID_ANY, wx.Bitmap('icons/displayicon.png')) - self.displayAtCountButton.Bind(wx.EVT_BUTTON, self.OnDisplayAtCount) - - self.toolbar = wx.BoxSizer(wx.HORIZONTAL) - self.toolbar.Add(self.rankNameUnicode, 0, wx.EXPAND) - self.toolbar.Add(self.holdRankButton, 0, wx.EXPAND) - self.toolbar.Add(self.curveRankButton, 0, wx.EXPAND) - self.toolbar.Add(self.straightRankButton, 0, wx.EXPAND) - self.toolbar.Add(self.switchEndpointsButton, 0, wx.EXPAND) - self.toolbar.Add(self.switchLabelButton, 0, wx.EXPAND) -# TODO(astory): discuss whether this button should exist or not -# self.toolbar.Add(self.deleteRankButton, 0, wx.EXPAND) - self.toolbar.Add(self.snapEndButton, 0, wx.EXPAND) - self.toolbar.Add(self.snapBeginButton, 0, wx.EXPAND) - self.toolbar.Add(self.displayAtCountButton, 0, wx.EXPAND) - - self.fieldbar = GUITimeBar.TimeBar(self.panel, wx.ID_ANY, self) - self.fieldbar.Bind(wx.EVT_PAINT, self.fieldbar.OnPaint) - self.fieldbar.Bind(wx.EVT_SIZE, self.fieldbar.OnResize) - self.fieldbar.Bind(wx.EVT_LEFT_DOWN, self.fieldbar.OnLeftClick) - self.fieldbar.Bind(wx.EVT_RIGHT_DOWN, self.fieldbar.OnRightClick) #right clicks dont actually do anything, ATM. - self.fieldbar.Bind(wx.EVT_LEFT_UP, self.fieldbar.OnLeftUnclick) - self.fieldbar.Bind(wx.EVT_RIGHT_UP, self.fieldbar.OnRightUnclick) - self.fieldbar.Bind(wx.EVT_MOTION, self.fieldbar.OnMouseMove) - self.fieldbar.Bind(wx.EVT_LEAVE_WINDOW, self.fieldbar.OnMouseExit) -# self.fieldbar.Bind(wx.EVT_CHAR, self.fieldbar.OnKey) - - self.moveSetList = wx.ImageList(GUIField.MINI_FIELD_SIZE[0], GUIField.MINI_FIELD_SIZE[1], False, 0) - self.moveList = wx.ListCtrl(self.panel, ID_MOVE_LIST, style = wx.LC_SINGLE_SEL | wx.LC_ICON | wx.LC_ALIGN_TOP) - self.moveList.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnMoveSelect) - self.moveList.AssignImageList(self.moveSetList, wx.IMAGE_LIST_NORMAL) - - self.moveListCaption = wx.StaticText(self.panel, wx.ID_ANY, "Sets/Moves") - - self.sidepanel = wx.BoxSizer(wx.VERTICAL) - self.sidepanel.Add(self.moveListCaption, 0, wx.ALIGN_CENTRE) - self.sidepanel.Add(self.moveList, 1, wx.EXPAND) - self.sidepanel.Add((GUIField.MINI_FIELD_SIZE[0] * 1.15, 1), 0, wx.ALIGN_CENTRE) - - self.toppanel = wx.BoxSizer(wx.HORIZONTAL) - self.toppanel.Add(self.fieldpanel, 4, wx.EXPAND) - self.toppanel.Add(wx.StaticLine(self.panel, style = wx.LI_VERTICAL), 0, wx.ALL | wx.EXPAND) - self.toppanel.Add(self.sidepanel, 0, wx.EXPAND) - - self.commandListCaption = wx.StaticText(self.panel, wx.ID_ANY, "Ranks: ") - self.commandListRankCaption = wx.StaticText(self.panel, wx.ID_ANY, "") - - self.commandListCaptionPanel = wx.BoxSizer(wx.HORIZONTAL) - self.commandListCaptionPanel.Add((1,1), 1, wx.EXPAND) - self.commandListCaptionPanel.Add(self.commandListCaption, 20, wx.EXPAND) - self.commandListCaptionPanel.Add(self.commandListRankCaption, 5, wx.EXPAND) - self.commandListCaptionPanel.Add((1,1), 1, wx.EXPAND) - - self.commandList = wx.ListBox(self.panel, ID_COMMAND_LIST, style = wx.LB_SINGLE) - self.commandList.Bind(wx.EVT_LISTBOX, self.OnCommandSelect) - - self.commandButtonMoveUp = wx.Button(self.panel, ID_COMMAND_BUTTON_MOVE_UP, u"\u2191") - self.commandButtonMoveUp.Bind(wx.EVT_BUTTON, lambda event: self.OnCommandButtonMove(event, True)) - self.commandButtonMoveDown = wx.Button(self.panel, ID_COMMAND_BUTTON_MOVE_DOWN, u"\u2193") - self.commandButtonMoveDown.Bind(wx.EVT_BUTTON, lambda event: self.OnCommandButtonMove(event, False)) - self.commandButtonRename = wx.Button(self.panel, ID_COMMAND_BUTTON_SPLIT, "Rename") - self.commandButtonRename.Bind(wx.EVT_BUTTON, self.OnCommandButtonRename) - self.commandButtonSplit = wx.Button(self.panel, ID_COMMAND_BUTTON_SPLIT, "Split") - self.commandButtonSplit.Bind(wx.EVT_BUTTON, self.OnCommandButtonSplit) - self.commandButtonMerge = wx.Button(self.panel, ID_COMMAND_BUTTON_MERGE, "Merge") - self.commandButtonMerge.Bind(wx.EVT_BUTTON, self.OnCommandButtonMerge) - self.commandButtonDelete = wx.Button(self.panel, ID_COMMAND_BUTTON_DELETE, "Delete") - self.commandButtonDelete.Bind(wx.EVT_BUTTON, self.OnCommandButtonDelete) - - self.commandListButtonPanel = wx.BoxSizer(wx.VERTICAL) - self.commandListButtonPanel.Add(self.commandButtonMoveUp, 5, wx.EXPAND) - self.commandListButtonPanel.Add(self.commandButtonMoveDown, 5, wx.EXPAND) - self.commandListButtonPanel.Add(self.commandButtonRename, 5, wx.EXPAND) - self.commandListButtonPanel.Add(self.commandButtonSplit, 5, wx.EXPAND) - self.commandListButtonPanel.Add(self.commandButtonMerge, 5, wx.EXPAND) - self.commandListButtonPanel.Add(self.commandButtonDelete, 5, wx.EXPAND) - - self.commandListPanel = wx.BoxSizer(wx.HORIZONTAL) - self.commandListPanel.Add(self.commandList, 5, wx.EXPAND) - self.commandListPanel.Add(self.commandListButtonPanel, 2, wx.EXPAND) - - self.bottompanelcentre = wx.BoxSizer(wx.VERTICAL) - self.bottompanelcentre.Add(self.commandListCaptionPanel, 0, wx.ALIGN_CENTRE) - self.bottompanelcentre.Add(self.commandListPanel, 20, wx.EXPAND) - -# self.rankNameListCaption = wx.StaticText(self.panel, wx.ID_ANY, "Ranks: ") - self.rankNameList = GUIRankList.RankList(self.panel, wx.ID_ANY, self) - self.rankNameList.Bind(wx.EVT_PAINT, self.rankNameList.OnPaint) - self.rankNameList.Bind(wx.EVT_SIZE, self.rankNameList.OnResize) - -# self.rankNameListPanel = wx.BoxSizer(wx.HORIZONTAL) -# self.rankNameListPanel.Add(self.rankNameListCaption, 0, wx.ALIGN_RIGHT) -# self.rankNameListPanel.Add(self.rankNameList, 0, wx.ALIGN_LEFT) - - self.rankNameListBox = wx.StaticBox(self.panel, wx.ID_ANY, "Ranks") - self.rankNameListPanel = wx.StaticBoxSizer(self.rankNameListBox, wx.HORIZONTAL) - self.rankNameListPanel.Add(self.rankNameList, 1, wx.EXPAND) - - self.rankNamePanel = wx.BoxSizer(wx.HORIZONTAL) - self.rankNamePanel.Add(self.rankNameListPanel, 1, wx.EXPAND) - - #TODO Make this a static Module level or Class level variable. - self.commandAddChoices = ['MT', 'Hlt', 'FM', 'BM', 'RS', 'LS', 'Flat'] - self.commandAddButtons = [] - - self.commandAddSpecialChoices = ['GT', 'PW', 'Exp', 'Cond', 'DTP', 'FTA', 'Curv'] - self.commandAddSpecialButtons = [] - - self.commandAddButtonsPanel = wx.BoxSizer(wx.HORIZONTAL) - - self.commandAddSpecialButtonsBox = wx.StaticBox(self.panel, wx.ID_ANY, "Special Commands") - self.commandAddSpecialButtonsPanel = wx.StaticBoxSizer(self.commandAddSpecialButtonsBox, wx.HORIZONTAL) - - for i in range(len(self.commandAddChoices)): - self.commandAddButtons.append(wx.ToggleButton(self.panel, wx.ID_ANY, self.commandAddChoices[i])) -# VERY HACKISH: we have the argument j and give it the default value of i instead of just doing -# lambda event: self.OnCommandAddButtons(event, i) -# because that way, we establish a link to the variable i, so every binding will be using the same value for i -# By setting j to a default value of i, we implicitly create a new variable and set it to the value of i at that time, -# thus circumventing this problem - self.commandAddButtons[i].Bind(wx.EVT_TOGGLEBUTTON, lambda event, j = i: self.OnCommandAddButtons(event, j)) - self.commandAddButtonsPanel.Add(self.commandAddButtons[i], 1, wx.EXPAND) - - self.commandAddButtons[0].SetValue(True) # make sure at least one thing is always selected - self.commandAddButtonSelected = 0 # keep track of which is selected - - for i in range(len(self.commandAddSpecialChoices)): - self.commandAddSpecialButtons.append(wx.Button(self.panel, wx.ID_ANY, self.commandAddSpecialChoices[i])) -# VERY HACKISH: we have the argument j and give it the default value of i instead of just doing -# lambda event: self.OnCommandAddButtons(event, i) -# because that way, we establish a link to the variable i, so every binding will be using the same value for i -# By setting j to a default value of i, we implicitly create a new variable and set it to the value of i at that time, -# thus circumventing this problem - self.commandAddSpecialButtons[i].Bind(wx.EVT_BUTTON, lambda event, j = i: self.OnCommandAddSpecialButtons(event, j)) - self.commandAddSpecialButtonsPanel.Add(self.commandAddSpecialButtons[i], 1, wx.EXPAND) - - self.commandLengthCaption = wx.StaticText(self.panel, wx.ID_ANY, "Steps: ") - self.commandLengthText = wx.TextCtrl(self.panel, wx.ID_ANY, "", size = (35, -1)) - self.commandAddButton = wx.Button(self.panel, wx.ID_ANY, "Add >>", style = wx.BU_EXACTFIT) - self.commandAddButton.Bind(wx.EVT_BUTTON, self.OnCommandAdd) - - self.commandLengthPanel = wx.BoxSizer(wx.HORIZONTAL) - self.commandLengthPanel.Add(self.commandLengthCaption, 0, wx.ALIGN_CENTRE) - self.commandLengthPanel.Add(self.commandLengthText, 0, wx.ALIGN_CENTRE) - self.commandLengthPanel.Add((1,1), 1, wx.EXPAND) - self.commandLengthPanel.Add(self.commandAddButton, 0, wx.ALIGN_CENTRE) - - self.commandAddButtonsBox = wx.StaticBox(self.panel, wx.ID_ANY, "Simple Commands") - self.commandAddButtonsPanelPanel = wx.StaticBoxSizer(self.commandAddButtonsBox, wx.HORIZONTAL) - self.commandAddButtonsPanelPanel.Add(self.commandAddButtonsPanel, 1, wx.EXPAND) - self.commandAddButtonsPanelPanel.Add((5,1), 0, wx.ALIGN_CENTRE) - self.commandAddButtonsPanelPanel.Add(wx.StaticLine(self.panel, style = wx.LI_VERTICAL), 0, wx.ALL | wx.EXPAND) - self.commandAddButtonsPanelPanel.Add((5,1), 0, wx.ALIGN_CENTRE) - self.commandAddButtonsPanelPanel.Add(self.commandLengthPanel, 0, wx.ALIGN_CENTRE) - - self.bottompanelleft = wx.BoxSizer(wx.VERTICAL) - self.bottompanelleft.Add(self.rankNamePanel, 0, wx.EXPAND) -# self.bottompanelleft.Add((1,1), 1, wx.EXPAND) - self.bottompanelleft.Add(self.commandAddButtonsPanelPanel, 0, wx.ALL | wx.EXPAND) - self.bottompanelleft.Add(self.commandAddSpecialButtonsPanel, 0, wx.ALL | wx.EXPAND) - self.bottompanelleft.Add((1,1), 1, wx.EXPAND) - - self.animationCaption = wx.StaticText(self.panel, wx.ID_ANY, "Animation Controls") - - self.playButton = wx.BitmapButton(self.panel, wx.ID_ANY, wx.Bitmap('icons/playicon.png')) - self.playButton.Bind(wx.EVT_BUTTON, self.OnAnimationBegin) - self.stopButton = wx.BitmapButton(self.panel, wx.ID_ANY, wx.Bitmap('icons/stopicon.png')) - self.stopButton.Bind(wx.EVT_BUTTON, self.OnAnimationEnd) - self.addWaypointButton = wx.BitmapButton(self.panel, wx.ID_ANY, wx.Bitmap('icons/addwaypointicon.png')) - self.addWaypointButton.Bind(wx.EVT_BUTTON, self.OnAddWaypoint) - self.removeWaypointButton = wx.BitmapButton(self.panel, wx.ID_ANY, wx.Bitmap('icons/removewaypointicon.png')) - self.removeWaypointButton.Bind(wx.EVT_BUTTON, self.OnRemoveWaypoint) - - self.bottompanelrightbuttons = wx.FlexGridSizer(2, 2) - self.bottompanelrightbuttons.Add(self.playButton, 0, wx.EXPAND) - self.bottompanelrightbuttons.Add(self.stopButton, 0, wx.EXPAND) - self.bottompanelrightbuttons.Add(self.addWaypointButton, 0, wx.EXPAND) - self.bottompanelrightbuttons.Add(self.removeWaypointButton, 0, wx.EXPAND) - - self.bottompanelright = wx.BoxSizer(wx.VERTICAL) - self.bottompanelright.Add(self.animationCaption, 0, wx.ALIGN_CENTRE) - self.bottompanelright.Add(self.bottompanelrightbuttons, 0, wx.ALIGN_CENTRE) - - self.bottompanel = wx.BoxSizer(wx.HORIZONTAL) -# self.bottompanel.Add((10,1), 0, wx.ALIGN_CENTRE) - self.bottompanel.Add(self.bottompanelleft, 3, wx.EXPAND) - self.bottompanel.Add(wx.StaticLine(self.panel, style = wx.LI_VERTICAL), 0, wx.ALL | wx.EXPAND) - self.bottompanel.Add(self.bottompanelcentre, 2, wx.EXPAND) - self.bottompanel.Add(wx.StaticLine(self.panel, style = wx.LI_VERTICAL), 0, wx.ALL | wx.EXPAND) - self.bottompanel.Add(self.bottompanelright, 1, wx.EXPAND) -# self.bottompanel.Add((1,1), 1, wx.EXPAND) # TODO -# self.bottompanel.Add(self.bottomcontrolleft, 0, wx.ALIGN_CENTRE) -# self.bottompanel.Add(self.bottomline, 0, wx.ALIGN_CENTRE) -# self.bottompanel.Add(self.commandList, 2, wx.EXPAND) - - self.mainpanel = wx.BoxSizer(wx.VERTICAL) - self.mainpanel.Add(self.toppanel, 15, wx.EXPAND) - self.mainpanel.Add(wx.StaticLine(self.panel), 0, wx.ALL | wx.EXPAND) - self.mainpanel.Add(self.toolbar, 0, wx.EXPAND) - self.mainpanel.Add(wx.StaticLine(self.panel), 0, wx.ALL | wx.EXPAND) - self.mainpanel.Add(self.fieldbar, 0, wx.EXPAND) - self.mainpanel.Add(wx.StaticLine(self.panel), 0, wx.ALL | wx.EXPAND) - self.mainpanel.Add(self.bottompanel, 5, wx.EXPAND) - - self.panel.SetSizer(self.mainpanel) -#self.SetAutoLayout(1) - # end main window code - - -# ensure that self intercepts all keypress events -# self.field.Bind(wx.EVT_KILL_FOCUS, lambda x: self.field.SetFocus()) -# self.field.SetFocus() - -# DEBUG - if DEBUG: - self.core.MoveAdded(1, 16, None) - self.core.MoveAdded(17, 32, None) -# DEBUG - - self.RefreshTitleBar() - self.RefreshMoveList() - self.RefreshStatusBar() - self.Show(True) - -# begin variables - - self.filename = None # this is the name of the currently-open file; None if not yet saved - self.animranks = None # list of animated rank locations when animating; None when not - self.animtimer = wx.Timer(self, ID_ANIM_TIMER) - wx.EVT_TIMER(self, ID_ANIM_TIMER, self.OnAnimationTimer) - - - def RefreshTitleBar(self): - if self.filename is None: - self.SetTitle("Rank Panda") - else: - self.SetTitle("Rank Panda (" + self.filename + ")") - - def RefreshMoveList(self): - self.moveList.Freeze() - self.moveList.ClearAll() - moves = self.core.GetMoves() - self.moveSetList.RemoveAll() - - i = 0 - - for m in moves: - item = wx.ListItem() - item.SetId(i) - item.SetText(m[1]) - item.SetImage(i) - - self.moveSetList.Add(self.field.RenderSet(self.core.GetRanks(moveNumber = i))) #(Brady) changed from GetRanksGivenMove(i) to GetRanks(moveNumber=i) - - self.moveList.InsertItem(item) - - i += 1 - - curr = self.core.GetCurrentMove()[0] - self.moveList.SetItemState(curr, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED) - self.moveList.Thaw() - - def RefreshCurrentMove(self): - curr = self.core.GetCurrentMove()[0] - self.moveSetList.Replace(curr, self.field.RenderSet(self.core.GetRanks(moveNumber = curr))) - self.moveList.RefreshItem(curr) - - def RefreshRankList(self): - ranks = self.core.GetRanks() - selected = self.core.GetSelectedRanks() - - rankLabels = [] - selectedRankLabels = [] - - i = 0 - - for r in ranks: - if r[1] is not None: #checks the name - isSelected = False - - for s in selected: #(Brady) This seems inefficient, try to fix - if r[0] == s[0]: - isSelected = True - - label = r[1] - if self.core.IsRankHeld(r[0]): - label += '*' - - label += ' ' - - rankLabels.append(label) - - if isSelected: - selectedRankLabels.append(i) - - i += 1 - - self.rankNameList.SetRanks(rankLabels, selectedRankLabels) - - def RefreshCommandList(self): - self.commandList.Clear() - ranks = self.core.GetSelectedRanks() - self.commandListRankCaption.SetLabel("") - - label = "" - for r in ranks: - if r[1] is not None: - label += r[1] - if self.core.IsRankHeld(r[0]): - label += '*' - label += ' ' - - self.commandListRankCaption.SetLabel(label) - - commands = self.core.GetCommands() - for c in commands: -# DEBUG - if DEBUG: - self.commandList.Append(c[0] + " " + str(c[1])) - else: - self.commandList.Append(c[0] + " " + str(int(round(c[1])))) -# DEBUG - selected = self.commandList.GetSelections() - self.core.SetListOfSelectedCommandNumbers(selected) - - def RefreshStatusBar(self): - self.statusBarSongLabel.SetLabel(self.core.GetSong()) - - currMove = self.core.GetCurrentMove() - info = self.core.GetMoveInfo(currMove[0]) - if currMove[2] == 1: - count = "count" - else: - count = "counts" - - if int(info[0]) == int(info[1]): - measure = "Measure" - else: - measure = "Measures" - - if currMove is not None: - self.statusBarMoveLabel.SetLabel(currMove[1] + ": " + str(currMove[2]) + " " + count + " (" + measure + " " + str(int(info[0])) + u"\u2013" + str(int(info[1])) + ")") -# self.statusBarCountLabel.SetLabel(str(currMove[2])) - else: - self.statusBarMoveLabel.SetLabel("") -# self.statusBarCountLabel.SetLabel("") - - - def CreateSong(self, event): - d = GUIDialogs.SongCreationDialog(self) - if d.ShowModal() == 0: - try: - core = CoreWrapper.CoreWrapper(*d.output) - except NameError as exception: - e = wx.MessageDialog(self, "Exception thrown while creating song!\nSong NOT created.\nException: " + str(exception), "Song Creation Exception", wx.OK) - e.ShowModal() - e.Destroy() - else: - self.core = core - self.field.Refresh(False) - self.fieldbar.Refresh(False) - self.RefreshMoveList() - self.RefreshRankList() - self.RefreshCommandList() - self.RefreshStatusBar() - - def EditSong(self, event): #try not having event. - d = GUIDialogs.SongCreationDialog(self) - if d.ShowModal() == 0: - try: - self.core.EditSongInfo(*d.output) - except NameError as exception: - e = wx.MessageDialog(self, "Exception thrown while creating song!\nSong NOT created.\nException: " + str(exception), "Song Edit Exception", wx.OK) - e.ShowModal() - e.Destroy() - else: - self.core = core - self.field.Refresh(False) - self.fieldbar.Refresh(False) - self.RefreshMoveList() - self.RefreshRankList() - self.RefreshCommandList() - self.RefreshStatusBar() - - def CreateMove(self, event, beginMeasure = "", endMeasure = ""): - moves = self.core.GetMoves() - moveNames = [] - - for m in moves: - moveNames.append(m[1]) - - d = GUIDialogs.MoveCreationDialog(self, moveNames, beginMeasure, endMeasure) - if d.ShowModal() == 0: - m = self.core.MoveAdded(*d.output) - importTarget = d.output2 - if importTarget >= m: # check if our insertion of a new move affected the index of the move to insert from, and correct - importTarget += 1 - - if m is None: - e = wx.MessageDialog(self, "New move overlaps with pre-existing moves!\nMove NOT created.", "Move Creation Exception", wx.OK) - e.ShowModal() - e.Destroy() - elif importTarget != -1: - self.core.ImportRankLocation(importTarget, m) - - self.field.Refresh(False) - self.fieldbar.Refresh(False) - self.RefreshMoveList() - self.RefreshRankList() - self.RefreshCommandList() - self.RefreshStatusBar() - - d.Destroy() - - def OnMoveSelect(self, event): - index = event.m_itemIndex - self.core.ChangeMove(index) - self.field.Refresh(False) - self.fieldbar.Refresh(False) - self.RefreshRankList() - self.RefreshCommandList() - self.RefreshStatusBar() - - def OnMoveImport(self, event): - move = self.core.GetCurrentMove() - - if move is None: - return - - moves = self.core.GetMoves() - moveNames = [] - - for m in moves: - moveNames.append(m[1]) - - d = GUIDialogs.ImportRanksDialog(self, moveNames) - if d.ShowModal() == 0: - self.core.ImportRankLocation(d.output, move[0]) - - self.field.Refresh(False) - self.RefreshMoveList() - self.RefreshRankList() - self.RefreshCommandList() - - d.Destroy() - - def OnMoveSplit(self, event): - move = self.core.GetCurrentMove() - - if move is None: - return - - d = wx.GetNumberFromUser("", "At count:", "Split Move", 1, 1, move[2], self) - if d != -1: - self.core.MoveEdited('Split', [move[0], d]) - - self.fieldbar.Refresh(False) - self.RefreshMoveList() - self.RefreshStatusBar() - - def OnMoveMerge(self, event): - move = self.core.GetCurrentMove() - - if move is None: - return - - self.core.MoveEdited('Merge', [move[0]]) - - self.fieldbar.Refresh(False) - self.RefreshMoveList() - self.RefreshStatusBar() - - def OnMoveShift(self, event): - move = self.core.GetCurrentMove() - - if move is None: - return - - d = wx.GetTextFromUser("Shift by how many counts:", "Shift Move", "0", self) - - try: - n = int(d) - except Exception: - d = wx.MessageDialog(self, "Invalid entry for number of counts to shift by!", "Parse Error", wx.OK) - d.ShowModal() - d.Destroy() - return - - self.fieldbar.Refresh(False) - self.RefreshMoveList() - self.RefreshStatusBar() - -#TODO -#self.core.MoveEdited('Shift', [move[0], n]) - self.RefreshMoveList() - self.RefreshStatusBar() - - def OnMoveText(self, event): - move = self.core.GetCurrentMove() - - if move is None: - return - - text = self.core.GetMoveText() - if text is None: - text = "" - - d = GUIDialogs.AddText(self, text, "Entering Text for " + move[1]) - if d.ShowModal() == 0: - if d.output == "": - self.core.SetMoveText(None) - else: - self.core.SetMoveText(d.output) - d.Destroy() - - def OnMoveTextOverwrite(self, event): - move = self.core.GetCurrentMove() - - if move is None: - return - - text = self.core.GetMoveTextOverwrite() - if text is None: - text = "" - - d = GUIDialogs.AddText(self, text, "Entering Overwrite Text for " + move[1]) - if d.ShowModal() == 0: - if d.output == "": - self.core.SetMoveTextOverwrite(None) - else: - self.core.SetMoveTextOverwrite(d.output) - d.Destroy() - - def OnCommandSelect(self, event): # TODO allow handling multiple contiguous selections - index = event.GetSelection() - self.core.SetListOfSelectedCommandNumbers([index]) - self.field.Refresh(False) - - def OnCommandAddButtons(self, event, i): -# don't allow deselecting of selected button (just so something is selected at all times) - self.commandAddButtons[i].SetValue(True) - self.commandAddButtonSelected = i # keep track of which is selected -# set all of the other buttons to disabled - for j in range(len(self.commandAddChoices)): - if i != j: - self.commandAddButtons[j].SetValue(False) - - def BitmapGet(self): - return self.field.PrintBitmap() - - def OnCommandAddSpecialButtons(self, event, i): - ranks = self.core.GetSelectedRanks() - #if len(ranks) != 1: - #d = wx.MessageDialog(self, "Currently, only adding commands to a single rank at a time is supported.", "Selection Error", wx.OK) - #d.ShowModal() - #d.Destroy() - #return - - #orig. code - #n=0 - #while(n 0: - self.RefreshCommandList() - self.commandList.SetSelection(selected[0] - 1) - elif not dir: # moved down - if selected[0] < self.commandList.GetCount() - 1: - self.RefreshCommandList() - self.commandList.SetSelection(selected[0] + 1) - - def OnCommandButtonRename(self, event): - selected = self.commandList.GetSelections() - if len(selected) != 1: - return - - label = self.core.GetCommands()[selected[0]][0] - d = wx.GetTextFromUser("New name:", "Rename Command", label, self) - if d != "": - ranks = self.core.GetSelectedRanks() - self.core.CommandEdited(ranks[0][0], selected[0], d) - self.RefreshCommandList() - - def OnCommandButtonSplit(self, event): - selected = self.commandList.GetSelections() - if len(selected) != 1: - return - - d = wx.GetNumberFromUser("", "At count:", "Split Command", 1, 1, self.core.GetCommands()[selected[0]][1], self) - if d != -1: - ranks = self.core.GetSelectedRanks() - self.core.CommandSplit(ranks[0][0], selected[0], d) - self.RefreshCommandList() - - def OnCommandButtonMerge(self, event): - selected = self.commandList.GetSelections() - if len(selected) != 1: - return - - ranks = self.core.GetSelectedRanks() - self.core.CommandMerge(ranks[0][0], selected[0]) - self.RefreshCommandList() - - def OnCommandButtonDelete(self, event): - selected = self.commandList.GetSelections() - if len(selected) != 1: - return - -# d = wx.MessageBox("Are you sure?", "Delete Command", wx.YES_NO, self) -# if d == wx.YES: -# ranks = self.core.GetSelectedRanks() -# if len(ranks) != 1: -# c = wx.MessageBox("Deleting commands for multiple ranks not yet implemented...", "Delete Command", wx.OK, self) -# c.ShowModal() -# c.destroy() -# else: -# self.core.CommandDeleted(ranks[0][0], selected[0]) - - ranks = self.core.GetSelectedRanks() - if len(ranks) != 1: - c = wx.MessageBox("Deleting commands for multiple ranks not yet implemented...", "Delete Command", wx.OK, self) - c.ShowModal() - c.destroy() - else: - self.core.CommandDeleted(ranks[0][0], selected[0]) - - self.RefreshCommandList() - -# TODO - def OnExit(self, event): - """ Exit menu item pressed """ - self.Close(True) - - def OnOpen(self, event): - """ Load file """ - dirname = '' - - while True: - d = wx.FileDialog(self, "Open File", dirname, "", "*.panda", wx.OPEN) - if d.ShowModal() == wx.ID_OK: - self.filename = os.path.join(d.GetDirectory(), d.GetFilename()) - if self.core.Load(self.filename) == -1: # load failed - e = wx.MessageDialog(self, "Unable to open file!", "File Access Exception", wx.OK) - e.ShowModal() - e.Destroy() - else: - self.RefreshTitleBar() - self.field.Refresh(False) - self.fieldbar.Refresh(False) - self.RefreshMoveList() - self.RefreshRankList() - self.RefreshCommandList() - self.RefreshStatusBar() - - break - else: - break - d.Destroy() - - def OnSave(self, event): - """ Save current file under same name (same as Save As if no current file) """ - if self.filename == None: - self.OnSaveAs(event) - else: - self.core.Save(self.filename) - - def OnSaveAs(self, event): - """ Save current file under new name """ - dirname = '' - d = wx.FileDialog(self, "Save File", dirname, "", "*.panda", wx.SAVE) - if d.ShowModal() == wx.ID_OK: - self.filename = os.path.join(d.GetDirectory(), d.GetFilename()) - self.core.Save(self.filename) -# TODO check for and strip off .panda? - d.Destroy() - - self.RefreshTitleBar() - - def OnSavePdf(self, event): - """ Save current file under new name """ - dirname = '' - d = wx.FileDialog(self, "Save File", dirname, "", "*.pdf", wx.SAVE) - if d.ShowModal() == wx.ID_OK: - self.filename = os.path.join(d.GetDirectory(), d.GetFilename()) - d.Destroy() - return self.filename - - def OnExport(self, event): - moveNames=[] - i=0 - while(i>", style = wx.BU_EXACTFIT) + self.commandAddButton.Bind(wx.EVT_BUTTON, self.OnCommandAdd) + + self.commandLengthPanel = wx.BoxSizer(wx.HORIZONTAL) + self.commandLengthPanel.Add(self.commandLengthCaption, 0, wx.ALIGN_CENTRE) + self.commandLengthPanel.Add(self.commandLengthText, 0, wx.ALIGN_CENTRE) + self.commandLengthPanel.Add((1,1), 1, wx.EXPAND) + self.commandLengthPanel.Add(self.commandAddButton, 0, wx.ALIGN_CENTRE) + + self.commandAddButtonsBox = wx.StaticBox(self.panel, wx.ID_ANY, "Simple Commands") + self.commandAddButtonsPanelPanel = wx.StaticBoxSizer(self.commandAddButtonsBox, wx.HORIZONTAL) + self.commandAddButtonsPanelPanel.Add(self.commandAddButtonsPanel, 1, wx.EXPAND) + self.commandAddButtonsPanelPanel.Add((5,1), 0, wx.ALIGN_CENTRE) + self.commandAddButtonsPanelPanel.Add(wx.StaticLine(self.panel, style = wx.LI_VERTICAL), 0, wx.ALL | wx.EXPAND) + self.commandAddButtonsPanelPanel.Add((5,1), 0, wx.ALIGN_CENTRE) + self.commandAddButtonsPanelPanel.Add(self.commandLengthPanel, 0, wx.ALIGN_CENTRE) + + self.bottompanelleft = wx.BoxSizer(wx.VERTICAL) + self.bottompanelleft.Add(self.rankNamePanel, 0, wx.EXPAND) +# self.bottompanelleft.Add((1,1), 1, wx.EXPAND) + self.bottompanelleft.Add(self.commandAddButtonsPanelPanel, 0, wx.ALL | wx.EXPAND) + self.bottompanelleft.Add(self.commandAddSpecialButtonsPanel, 0, wx.ALL | wx.EXPAND) + self.bottompanelleft.Add((1,1), 1, wx.EXPAND) + + self.animationCaption = wx.StaticText(self.panel, wx.ID_ANY, "Animation Controls") + + self.playButton = wx.BitmapButton(self.panel, wx.ID_ANY, wx.Bitmap('icons/playicon.png')) + self.playButton.Bind(wx.EVT_BUTTON, self.OnAnimationBegin) + self.stopButton = wx.BitmapButton(self.panel, wx.ID_ANY, wx.Bitmap('icons/stopicon.png')) + self.stopButton.Bind(wx.EVT_BUTTON, self.OnAnimationEnd) + self.addWaypointButton = wx.BitmapButton(self.panel, wx.ID_ANY, wx.Bitmap('icons/addwaypointicon.png')) + self.addWaypointButton.Bind(wx.EVT_BUTTON, self.OnAddWaypoint) + self.removeWaypointButton = wx.BitmapButton(self.panel, wx.ID_ANY, wx.Bitmap('icons/removewaypointicon.png')) + self.removeWaypointButton.Bind(wx.EVT_BUTTON, self.OnRemoveWaypoint) + + self.bottompanelrightbuttons = wx.FlexGridSizer(2, 2) + self.bottompanelrightbuttons.Add(self.playButton, 0, wx.EXPAND) + self.bottompanelrightbuttons.Add(self.stopButton, 0, wx.EXPAND) + self.bottompanelrightbuttons.Add(self.addWaypointButton, 0, wx.EXPAND) + self.bottompanelrightbuttons.Add(self.removeWaypointButton, 0, wx.EXPAND) + + self.bottompanelright = wx.BoxSizer(wx.VERTICAL) + self.bottompanelright.Add(self.animationCaption, 0, wx.ALIGN_CENTRE) + self.bottompanelright.Add(self.bottompanelrightbuttons, 0, wx.ALIGN_CENTRE) + + self.bottompanel = wx.BoxSizer(wx.HORIZONTAL) +# self.bottompanel.Add((10,1), 0, wx.ALIGN_CENTRE) + self.bottompanel.Add(self.bottompanelleft, 3, wx.EXPAND) + self.bottompanel.Add(wx.StaticLine(self.panel, style = wx.LI_VERTICAL), 0, wx.ALL | wx.EXPAND) + self.bottompanel.Add(self.bottompanelcentre, 2, wx.EXPAND) + self.bottompanel.Add(wx.StaticLine(self.panel, style = wx.LI_VERTICAL), 0, wx.ALL | wx.EXPAND) + self.bottompanel.Add(self.bottompanelright, 1, wx.EXPAND) +# self.bottompanel.Add((1,1), 1, wx.EXPAND) # TODO +# self.bottompanel.Add(self.bottomcontrolleft, 0, wx.ALIGN_CENTRE) +# self.bottompanel.Add(self.bottomline, 0, wx.ALIGN_CENTRE) +# self.bottompanel.Add(self.commandList, 2, wx.EXPAND) + + self.mainpanel = wx.BoxSizer(wx.VERTICAL) + self.mainpanel.Add(self.toppanel, 15, wx.EXPAND) + self.mainpanel.Add(wx.StaticLine(self.panel), 0, wx.ALL | wx.EXPAND) + self.mainpanel.Add(self.toolbar, 0, wx.EXPAND) + self.mainpanel.Add(wx.StaticLine(self.panel), 0, wx.ALL | wx.EXPAND) + self.mainpanel.Add(self.fieldbar, 0, wx.EXPAND) + self.mainpanel.Add(wx.StaticLine(self.panel), 0, wx.ALL | wx.EXPAND) + self.mainpanel.Add(self.bottompanel, 5, wx.EXPAND) + + self.panel.SetSizer(self.mainpanel) +#self.SetAutoLayout(1) + # end main window code + + +# ensure that self intercepts all keypress events +# self.field.Bind(wx.EVT_KILL_FOCUS, lambda x: self.field.SetFocus()) +# self.field.SetFocus() + +# DEBUG + if DEBUG: + self.core.MoveAdded(1, 16, None) + self.core.MoveAdded(17, 32, None) +# DEBUG + + self.RefreshTitleBar() + self.RefreshMoveList() + self.RefreshStatusBar() + self.Show(True) + +# begin variables + + self.filename = None # this is the name of the currently-open file; None if not yet saved + self.animranks = None # list of animated rank locations when animating; None when not + self.animtimer = wx.Timer(self, ID_ANIM_TIMER) + wx.EVT_TIMER(self, ID_ANIM_TIMER, self.OnAnimationTimer) + + + def RefreshTitleBar(self): + if self.filename is None: + self.SetTitle("Rank Panda") + else: + self.SetTitle("Rank Panda (" + self.filename + ")") + + def RefreshMoveList(self): + self.moveList.Freeze() + self.moveList.ClearAll() + moves = self.core.GetMoves() + self.moveSetList.RemoveAll() + + i = 0 + + for m in moves: + item = wx.ListItem() + item.SetId(i) + item.SetText(m[1]) + item.SetImage(i) + + self.moveSetList.Add(self.field.RenderSet(self.core.GetRanks(moveNumber = i))) + + self.moveList.InsertItem(item) + + i += 1 + + curr = self.core.GetCurrentMove()[0] + self.moveList.SetItemState(curr, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED) + self.moveList.Thaw() + + def RefreshCurrentMove(self): + curr = self.core.GetCurrentMove()[0] + self.moveSetList.Replace(curr, self.field.RenderSet(self.core.GetRanks(moveNumber = curr))) + self.moveList.RefreshItem(curr) + + def RefreshRankList(self): + ranks = self.core.GetRanks() + selected = self.core.GetSelectedRanks() + + rankLabels = [] + selectedRankLabels = [] + + i = 0 + + for r in ranks: + if r[1] is not None: #checks the name + isSelected = False + + for s in selected: #(Brady) This seems inefficient, try to fix + if r[0] == s[0]: + isSelected = True + + label = r[1] + if self.core.IsRankHeld(r[0]): + label += '*' + + label += ' ' + + rankLabels.append(label) + + if isSelected: + selectedRankLabels.append(i) + + i += 1 + + self.rankNameList.SetRanks(rankLabels, selectedRankLabels) + + def RefreshCommandList(self): + self.commandList.Clear() + ranks = self.core.GetSelectedRanks() + self.commandListRankCaption.SetLabel("") + + label = "" + for r in ranks: + if r[1] is not None: + label += r[1] + if self.core.IsRankHeld(r[0]): + label += '*' + label += ' ' + + self.commandListRankCaption.SetLabel(label) + + commands = self.core.GetCommands() + for c in commands: +# DEBUG + if DEBUG: + self.commandList.Append(c[0] + " " + str(c[1])) + else: + self.commandList.Append(c[0] + " " + str(int(round(c[1])))) +# DEBUG + selected = self.commandList.GetSelections() + self.core.SetListOfSelectedCommandNumbers(selected) + + def RefreshStatusBar(self): + self.statusBarSongLabel.SetLabel(self.core.GetSong()) + + currMove = self.core.GetCurrentMove() + info = self.core.GetMoveInfo(currMove[0]) + if currMove[2] == 1: + count = "count" + else: + count = "counts" + + if int(info[0]) == int(info[1]): + measure = "Measure" + else: + measure = "Measures" + + if currMove is not None: + self.statusBarMoveLabel.SetLabel(currMove[1] + ": " + str(currMove[2]) + " " + count + " (" + measure + " " + str(int(info[0])) + u"\u2013" + str(int(info[1])) + ")") +# self.statusBarCountLabel.SetLabel(str(currMove[2])) + else: + self.statusBarMoveLabel.SetLabel("") +# self.statusBarCountLabel.SetLabel("") + + + def CreateSong(self, event): + d = GUIDialogs.SongCreationDialog(self) + if d.ShowModal() == 0: + try: + core = CoreWrapper.CoreWrapper(*d.output) + except NameError as exception: + e = wx.MessageDialog(self, "Exception thrown while creating song!\nSong NOT created.\nException: " + str(exception), "Song Creation Exception", wx.OK) + e.ShowModal() + e.Destroy() + else: + self.core = core + self.field.Refresh(False) + self.fieldbar.Refresh(False) + self.RefreshMoveList() + self.RefreshRankList() + self.RefreshCommandList() + self.RefreshStatusBar() + + def EditSong(self, event): #try not having event. + d = GUIDialogs.SongCreationDialog(self) + if d.ShowModal() == 0: + try: + self.core.EditSongInfo(*d.output) + except NameError as exception: + e = wx.MessageDialog(self, "Exception thrown while creating song!\nSong NOT created.\nException: " + str(exception), "Song Edit Exception", wx.OK) + e.ShowModal() + e.Destroy() + else: + self.core = core + self.field.Refresh(False) + self.fieldbar.Refresh(False) + self.RefreshMoveList() + self.RefreshRankList() + self.RefreshCommandList() + self.RefreshStatusBar() + + def CreateMove(self, event, beginMeasure = "", endMeasure = ""): + moves = self.core.GetMoves() + moveNames = [] + + for m in moves: + moveNames.append(m[1]) + + d = GUIDialogs.MoveCreationDialog(self, moveNames, beginMeasure, endMeasure) + if d.ShowModal() == 0: + m = self.core.MoveAdded(*d.output) + importTarget = d.output2 + if importTarget >= m: # check if our insertion of a new move affected the index of the move to insert from, and correct + importTarget += 1 + + if m is None: + e = wx.MessageDialog(self, "New move overlaps with pre-existing moves!\nMove NOT created.", "Move Creation Exception", wx.OK) + e.ShowModal() + e.Destroy() + elif importTarget != -1: + self.core.ImportRankLocation(importTarget, m) + + self.field.Refresh(False) + self.fieldbar.Refresh(False) + self.RefreshMoveList() + self.RefreshRankList() + self.RefreshCommandList() + self.RefreshStatusBar() + + d.Destroy() + + def OnMoveSelect(self, event): + index = event.m_itemIndex + self.core.ChangeMove(index) + self.field.Refresh(False) + self.fieldbar.Refresh(False) + self.RefreshRankList() + self.RefreshCommandList() + self.RefreshStatusBar() + + def OnMoveImport(self, event): + move = self.core.GetCurrentMove() + + if move is None: + return + + moves = self.core.GetMoves() + moveNames = [] + + for m in moves: + moveNames.append(m[1]) + + d = GUIDialogs.ImportRanksDialog(self, moveNames) + if d.ShowModal() == 0: + self.core.ImportRankLocation(d.output, move[0]) + + self.field.Refresh(False) + self.RefreshMoveList() + self.RefreshRankList() + self.RefreshCommandList() + + d.Destroy() + + def OnMoveSplit(self, event): + move = self.core.GetCurrentMove() + + if move is None: + return + + d = wx.GetNumberFromUser("", "At count:", "Split Move", 1, 1, move[2], self) + if d != -1: + self.core.MoveEdited('Split', [move[0], d]) + + self.fieldbar.Refresh(False) + self.RefreshMoveList() + self.RefreshStatusBar() + + def OnMoveMerge(self, event): + move = self.core.GetCurrentMove() + + if move is None: + return + + self.core.MoveEdited('Merge', [move[0]]) + + self.fieldbar.Refresh(False) + self.RefreshMoveList() + self.RefreshStatusBar() + + def OnMoveShift(self, event): + move = self.core.GetCurrentMove() + + if move is None: + return + + d = wx.GetTextFromUser("Shift by how many counts:", "Shift Move", "0", self) + + try: + n = int(d) + except Exception: + d = wx.MessageDialog(self, "Invalid entry for number of counts to shift by!", "Parse Error", wx.OK) + d.ShowModal() + d.Destroy() + return + + self.fieldbar.Refresh(False) + self.RefreshMoveList() + self.RefreshStatusBar() + +#TODO +#self.core.MoveEdited('Shift', [move[0], n]) + self.RefreshMoveList() + self.RefreshStatusBar() + + def OnMoveText(self, event): + move = self.core.GetCurrentMove() + + if move is None: + return + + text = self.core.GetMoveText() + if text is None: + text = "" + + d = GUIDialogs.AddText(self, text, "Entering Text for " + move[1]) + if d.ShowModal() == 0: + if d.output == "": + self.core.SetMoveText(None) + else: + self.core.SetMoveText(d.output) + d.Destroy() + + def OnMoveTextOverwrite(self, event): + move = self.core.GetCurrentMove() + + if move is None: + return + + text = self.core.GetMoveTextOverwrite() + if text is None: + text = "" + + d = GUIDialogs.AddText(self, text, "Entering Overwrite Text for " + move[1]) + if d.ShowModal() == 0: + if d.output == "": + self.core.SetMoveTextOverwrite(None) + else: + self.core.SetMoveTextOverwrite(d.output) + d.Destroy() + + def OnCommandSelect(self, event): # TODO allow handling multiple contiguous selections + index = event.GetSelection() + self.core.SetListOfSelectedCommandNumbers([index]) + self.field.Refresh(False) + + def OnCommandAddButtons(self, event, i): +# don't allow deselecting of selected button (just so something is selected at all times) + self.commandAddButtons[i].SetValue(True) + self.commandAddButtonSelected = i # keep track of which is selected +# set all of the other buttons to disabled + for j in range(len(self.commandAddChoices)): + if i != j: + self.commandAddButtons[j].SetValue(False) + + def BitmapGet(self): + return self.field.PrintBitmap() + + def OnCommandAddSpecialButtons(self, event, i): + ranks = self.core.GetSelectedRanks() + #if len(ranks) != 1: + #d = wx.MessageDialog(self, "Currently, only adding commands to a single rank at a time is supported.", "Selection Error", wx.OK) + #d.ShowModal() + #d.Destroy() + #return + +# give a message that some ranks are not locked instead of doing nothing. + unlocked = [r for r in ranks if not self.core.IsRankHeld(r[0])] + if unlocked != []: + d = wx.MessageDialog(self, "Rank(s) " + ", ".join([r[1] for r in unlocked]) + " are not locked. Could not add command.", "Unlocked Ranks", wx.OK) + d.ShowModal() + #d.Destroy() + return + +# TODO check label and stuff + type = self.commandAddSpecialChoices[i] + + selected = self.commandList.GetSelections() + index = self.commandList.GetCount() + if len(selected) == 1: + index = selected[0] + + if type == "GT": + f = GUIDialogs.GateTurnDialog(self) + + if f.ShowModal() == 0: + (dir, pt, length) = f.output + if dir == 0: + type = "GTCW" + else: + type = "GTCCW" + + type += str(pt) + + for r in ranks: + self.core.CommandAdded(r[0], type, index, length, None, None) + + f.Destroy() + + elif type == "PW": + f = GUIDialogs.PinwheelDialog(self) + + if f.ShowModal() == 0: + (dir, length) = f.output + if dir == 0: + type = "PWCW" + else: + type = "PWCCW" + + for r in ranks: + self.core.CommandAdded(r[0], type, index, length, None, None) + + f.Destroy() + + elif type == "Exp": + f = GUIDialogs.ExpandDialog(self) + + if f.ShowModal() == 0: + (pt, length) = f.output + type += str(pt) + + for r in ranks: + self.core.CommandAdded(r[0], type, index, length, None, None) + + f.Destroy() + + elif type == "Cond": + f = GUIDialogs.CondenseDialog(self) + + if f.ShowModal() == 0: + (pt, length) = f.output + type += str(pt) + + for r in ranks: + self.core.CommandAdded(r[0], type, index, length, None, None) + + f.Destroy() + + elif type == "DTP" or type == "Curv": + f = GUIDialogs.DTPDialog(self) + + if f.ShowModal() == 0: + length = f.output + + DTPranks = self.core.BeginAddingDTP(index, length, None, type == "Curv") + if DTPranks is not None: + d = GUIDialogs.AddDTPDialog(self, self, ranks[0][1], length, DTPranks) + d.ShowModal() + d.Destroy() + self.core.FinalizeAddingDTP() + + f.Destroy() + + elif type == "FTA": + f = GUIDialogs.FTADialog(self) + + if f.ShowModal() == 0: + endpoint = f.output[0] == 1 + length = f.output[1] + + if endpoint: + FTAranks = self.core.BeginAddingFTA1(index, length, None) + else: + FTAranks = self.core.BeginAddingFTA0(index, length, None) + + if FTAranks is not None: + d = GUIDialogs.AddFTADialog(self, self, endpoint, ranks[0][1], length, FTAranks) + d.ShowModal() + d.Destroy() + + if endpoint: + self.core.FinalizeAddingFTA1() + else: + self.core.FinalizeAddingFTA0() + + f.Destroy() + + else: + pass + + self.field.Refresh(False) + self.RefreshCommandList() + + def OnCommandAdd(self, event): + ranks = self.core.GetSelectedRanks() + #if len(ranks) != 1: + #d = wx.MessageDialog(self, "Currently, only adding commands to a single rank at a time is supported.", "Selection Error", wx.OK) + #d.ShowModal() + #d.Destroy() + #return + + # add dialog to tell you its not locked. + unlocked = [r for r in ranks if not self.core.IsRankHeld(r[0])] + if unlocked != []: + d = wx.MessageDialog(self, "Ranks " + ", ".join([r[1] for r in unlocked])\ + + " are not locked. Could not add command.", "Unlocked Ranks", wx.OK) + d.ShowModal() + d.Destroy() + return + + try: + length = int(self.commandLengthText.GetValue()) + except Exception: + d = wx.MessageDialog(self, "Invalid entry for Command Steps!", "Parse Error", wx.OK) + d.ShowModal() + d.Destroy() + return + +# we assume that at most one button will be selected at a time, which is reasonable + type = self.commandAddChoices[self.commandAddButtonSelected] + + selected = self.commandList.GetSelections() + index = self.commandList.GetCount() + if len(selected) == 1: + index = selected[0] + + if type == "Hlt": + n=0 + while(n 0: + self.RefreshCommandList() + self.commandList.SetSelection(selected[0] - 1) + elif not dir: # moved down + if selected[0] < self.commandList.GetCount() - 1: + self.RefreshCommandList() + self.commandList.SetSelection(selected[0] + 1) + + def OnCommandButtonRename(self, event): + selected = self.commandList.GetSelections() + if len(selected) != 1: + return + + label = self.core.GetCommands()[selected[0]][0] + d = wx.GetTextFromUser("New name:", "Rename Command", label, self) + if d != "": + ranks = self.core.GetSelectedRanks() + self.core.CommandEdited(ranks[0][0], selected[0], d) + self.RefreshCommandList() + + def OnCommandButtonSplit(self, event): + selected = self.commandList.GetSelections() + if len(selected) != 1: + return + + d = wx.GetNumberFromUser("", "At count:", "Split Command", 1, 1, self.core.GetCommands()[selected[0]][1], self) + if d != -1: + ranks = self.core.GetSelectedRanks() + self.core.CommandSplit(ranks[0][0], selected[0], d) + self.RefreshCommandList() + + def OnCommandButtonMerge(self, event): + selected = self.commandList.GetSelections() + if len(selected) != 1: + return + + ranks = self.core.GetSelectedRanks() + self.core.CommandMerge(ranks[0][0], selected[0]) + self.RefreshCommandList() + + def OnCommandButtonDelete(self, event): + selected = self.commandList.GetSelections() + if len(selected) != 1: + return + +# d = wx.MessageBox("Are you sure?", "Delete Command", wx.YES_NO, self) +# if d == wx.YES: +# ranks = self.core.GetSelectedRanks() +# if len(ranks) != 1: +# c = wx.MessageBox("Deleting commands for multiple ranks not yet implemented...", "Delete Command", wx.OK, self) +# c.ShowModal() +# c.destroy() +# else: +# self.core.CommandDeleted(ranks[0][0], selected[0]) + + ranks = self.core.GetSelectedRanks() + if len(ranks) != 1: + c = wx.MessageBox("Deleting commands for multiple ranks not yet implemented...", "Delete Command", wx.OK, self) + c.ShowModal() + c.destroy() + else: + self.core.CommandDeleted(ranks[0][0], selected[0]) + + self.RefreshCommandList() + +# TODO + def OnExit(self, event): + """ Exit menu item pressed """ + self.Close(True) + + def OnOpen(self, event): + """ Load file """ + dirname = '' + + while True: + d = wx.FileDialog(self, "Open File", dirname, "", "*.panda", wx.OPEN) + if d.ShowModal() == wx.ID_OK: + self.filename = os.path.join(d.GetDirectory(), d.GetFilename()) + if self.core.Load(self.filename) == -1: # load failed + e = wx.MessageDialog(self, "Unable to open file!", "File Access Exception", wx.OK) + e.ShowModal() + e.Destroy() + else: + self.RefreshTitleBar() + self.field.Refresh(False) + self.fieldbar.Refresh(False) + self.RefreshMoveList() + self.RefreshRankList() + self.RefreshCommandList() + self.RefreshStatusBar() + + break + else: + break + d.Destroy() + + def OnSave(self, event): + """ Save current file under same name (same as Save As if no current file) """ + if self.filename == None: + self.OnSaveAs(event) + else: + self.core.Save(self.filename) + + def OnSaveAs(self, event): + """ Save current file under new name """ + dirname = '' + d = wx.FileDialog(self, "Save File", dirname, "", "*.panda", wx.SAVE) + if d.ShowModal() == wx.ID_OK: + self.filename = os.path.join(d.GetDirectory(), d.GetFilename()) + self.core.Save(self.filename) +# TODO check for and strip off .panda? + d.Destroy() + + self.RefreshTitleBar() + + def OnSavePdf(self, event): + """ Save current file under new name """ + dirname = '' + d = wx.FileDialog(self, "Save File", dirname, "", "*.pdf", wx.SAVE) + if d.ShowModal() == wx.ID_OK: + self.filename = os.path.join(d.GetDirectory(), d.GetFilename()) + d.Destroy() + return self.filename + + def OnExport(self, event): + moveNames=[] + i=0 + while(i oldend: # if there is a gap between this move and the previous one - self.DrawMove(oldend, begin, "", GAP_BORDER_COLOUR, GAP_COLOUR, GAP_BORDER_COLOUR, dc) - self.gaps.append((oldendm, m1 - 1, oldend, begin)) - self.DrawMove(begin, end, i[1], MOVE_BORDER_COLOUR, MOVE_COLOUR, MOVE_NAME_COLOUR, dc) - oldend = end - oldendm = m2 + 1 - - i = self.main.core.GetCurrentMove() - (m1, m2, begin, end) = self.main.core.GetMoveInfo(i[0]) - begin -= 1 # since counts start at 1 not 0 - # we don't decrement end because we want the move to be drawn to the end of its last count - self.DrawMove(begin, end, i[1], SELECTED_MOVE_BORDER_COLOUR, SELECTED_MOVE_COLOUR, SELECTED_MOVE_NAME_COLOUR, dc) - - def DrawWaypoints(self, dc): - for wp in self.main.core.GetListOfWayPoints(): - self.DrawWaypoint(wp, WAYPOINT_COLOUR, dc) - - if self.animatePt != -1: - self.DrawWaypoint([self.animatePt], ANIMATE_COLOUR, dc) - - def DrawText(self, dc): -# dc.SetTextForeground(TEXT_COLOUR) - dc.SetTextForeground(self.fgColour) - - textRect = wx.Rect(0, MOVE_HEIGHT + WAYPOINT_HEIGHT, self.GetRect().GetWidth(), TEXT_HEIGHT) - - if self.animatePt != -1: - dc.DrawImageLabel("Animating @ Count: " + str(self.animatePt), wx.NullBitmap, textRect, wx.ALIGN_CENTRE) - - if self.mouseX != -1: - count = self.mouseX / self.step - dc.DrawImageLabel("Count: " + str(int(round(count))), wx.NullBitmap, textRect, wx.ALIGN_LEFT | wx.ALIGN_CENTRE_VERTICAL) - - if self.mouseY > -1 and self.mouseY <= MOVE_HEIGHT: - ret = self.PickMove(count) - if ret is not None: - (m, m1, m2, begin, end, i) = ret - if begin == end: - count = "Count" - else: - count = "Counts" - - if int(m1) == int(m2): - measure = "Measure" - else: - measure = "Measures" - - dc.DrawImageLabel(m[1] + " [" + count + ": " + str(begin) + u"\u2013" + str(end) + ", " + measure + ": " + str(int(m1)) + u"\u2013" + str(int(m2)) + "]", wx.NullBitmap, textRect, wx.ALIGN_RIGHT | wx.ALIGN_CENTRE_VERTICAL) - else: - gap = self.PickGap(count) - if gap is not None: - (m1, m2, begin, end) = gap - if begin == end: - count = "Count" - else: - count = "Counts" - - if int(m1) == int(m2): - measure = "Measure" - else: - measure = "Measures" - - dc.DrawImageLabel("[" + count + ": " + str(begin) + u"\u2013" + str(end) + ", " + measure + ": " + str(int(m1)) + u"\u2013" + str(int(m2)) + "]", wx.NullBitmap, textRect, wx.ALIGN_RIGHT | wx.ALIGN_CENTRE_VERTICAL) - - elif self.mouseY > MOVE_HEIGHT and self.mouseY <= MOVE_HEIGHT + WAYPOINT_HEIGHT: - wp = self.PickWaypoint(count) - if wp is not None: - dc.DrawImageLabel("Waypoint[Count: " + str(wp[0]) + ", Time: " + str(wp[1]) + "]", wx.NullBitmap, textRect, wx.ALIGN_RIGHT | wx.ALIGN_CENTRE_VERTICAL) - - def DrawMove(self, begin, end, name, borderColour, moveColour, nameColour, dc): - dc.SetPen(wx.Pen(borderColour, MOVE_BORDER_WIDTH)) - dc.SetBrush(wx.Brush(moveColour)) - dc.SetTextForeground(nameColour) - - moveRect = wx.Rect(begin * self.step, 0, (end - begin) * self.step, MOVE_HEIGHT) - dc.DrawRoundedRectangleRect(moveRect, MOVE_BORDER_CORNER_RADIUS) - if name != "" and moveRect.GetWidth() >= dc.GetFullTextExtent(name)[0]: - dc.DrawImageLabel(name, wx.NullBitmap, moveRect, wx.ALIGN_CENTRE) - - def DrawWaypoint(self, wp, colour, dc): - dc.SetPen(wx.Pen(colour, 0)) - dc.SetBrush(wx.Brush(colour)) - - x = wp[0] * self.step; - list = [] - list.append(wx.Point(x - WAYPOINT_WIDTH / 2.0, MOVE_HEIGHT + WAYPOINT_HEIGHT)) - list.append(wx.Point(x + WAYPOINT_WIDTH / 2.0, MOVE_HEIGHT + WAYPOINT_HEIGHT)) - list.append(wx.Point(x, MOVE_HEIGHT)) - - dc.DrawPolygon(list) - - def PickMove(self, count): - i = 0 - for m in self.main.core.GetMoves(): - (m1, m2, begin, end) = self.main.core.GetMoveInfo(m[0]) - if count >= begin and count <= end: - return (m, m1, m2, begin, end, i) - i += 1 - - return None - - def PickGap(self, count): - for m in self.gaps: - (m1, m2, begin, end) = m - if count >= begin and count <= end: - return (m1, m2, begin, end) - - return None - - def PickWaypoint(self, count): - for wp in self.main.core.GetListOfWayPoints(): - if abs(wp[0] - count) <= WAYPOINT_SELECT_RADIUS: - return wp - - return None - - def OnResize(self, event): - (w, h) = self.GetSizeTuple() - self.rect = wx.Rect(0, 0, w, h) - self.step = float(w) / self.main.core.GetTotalCounts() - - self.Refresh(False) - - def AnimateCount(self, count): # tell the timebar that we are animating and are at the given count (-1 for stop animating) - self.animatePt = count - self.Refresh(False) - - def OnLeftClick(self, event): - count = self.mouseX / self.step - - if self.mouseY > -1 and self.mouseY <= MOVE_HEIGHT: - ret = self.PickMove(count) - if ret is not None: - self.main.moveList.SetItemState(ret[5], wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED) - else: - gap = self.PickGap(count) - if gap is not None: - (m1, m2, begin, end) = gap - self.main.CreateMove(None, str(int(m1)), str(int(m2))) - - self.Refresh(False) - - def OnRightClick(self, event): - pass - - def OnLeftUnclick(self, event): - pass - - def OnRightUnclick(self, event): - pass - - def OnMouseMove(self, event): - self.mouseX = event.m_x - self.mouseY = event.m_y - self.Refresh(False) - - def OnMouseExit(self, event): - self.mouseX = -1 - self.mouseY = -1 - self.Refresh(False) +#!/usr/bin/env python +#GUI Time Bar: renders the song time bar + +import wx +#import CoreWrapper + +# TODO: maybe fix height and draw waypoints under moves and have 'status bar' at bottom? + +# constants +TOTAL_HEIGHT = 50.0 +MOVE_HEIGHT = 20.0 +WAYPOINT_HEIGHT = 10.0 +TEXT_HEIGHT = 20.0 + +MOVE_BORDER_WIDTH = 2.0 +MOVE_BORDER_CORNER_RADIUS = 5.0 + +EMPTY_COLOUR = wx.Colour(0, 0, 0) + +GAP_COLOUR = wx.Colour(127, 0, 0) +GAP_BORDER_COLOUR = wx.Colour(255, 0, 0) + +MOVE_COLOUR = wx.Colour(0, 127, 127) +MOVE_BORDER_COLOUR = wx.Colour(127, 127, 0) +MOVE_NAME_COLOUR = wx.Colour(127, 127, 0) + +SELECTED_MOVE_COLOUR = wx.Colour(0, 191, 191) +SELECTED_MOVE_BORDER_COLOUR = wx.Colour(255, 255, 0) +SELECTED_MOVE_NAME_COLOUR = wx.Colour(255, 255, 0) + +WAYPOINT_WIDTH = 10.0 +WAYPOINT_SELECT_RADIUS = 2.0 + +WAYPOINT_COLOUR = wx.Colour(0, 0, 255) +SELECTED_WAYPOINT_COLOUR = wx.Colour(255, 0, 0) +ANIMATE_COLOUR = wx.Colour(0, 255, 0) + +TEXT_COLOUR = wx.Colour(255, 255, 255) + +class TimeBar(wx.Panel): + + + def __init__(self, parent, id, main): + wx.Panel.__init__(self, parent, id, size = (-1, 50)) + self.main = main + + self.bgColour = self.main.panel.GetBackgroundColour() + self.fgColour = self.main.panel.GetForegroundColour() + + self.gaps = [] +# gaps will be stored in the format (beginMeasure, endMeasure, beginCount, endCount) + self.animatePt = -1 + self.mouseX = -1 + + self.OnResize(None) + + def OnPaint(self, event): + self.Draw() + bardc = wx.PaintDC(self) + bardc.DrawBitmap(self.paintBitmap, 0, 0, False) # manual double-buffering since nothing else seems to work... + + def Draw(self): + self.paintBitmap = wx.EmptyBitmap(*self.GetSizeTuple()) + dc = wx.MemoryDC() + dc.SetFont(wx.Font(10,wx.FONTFAMILY_MODERN,wx.FONTSTYLE_NORMAL,wx.FONTWEIGHT_NORMAL, faceName="DejaVu Sans")) + dc.SelectObject(self.paintBitmap) + +# dc.SetBackground(wx.Brush(EMPTY_COLOUR)) + dc.SetBackground(wx.Brush(self.bgColour)) + dc.Clear() + + self.DrawMoves(dc) + self.DrawWaypoints(dc) + self.DrawText(dc) + +# TODO add in draw waypoint functionality + + def DrawMoves(self, dc): + oldend = 0 # end of previous move (need this for gap detection) + oldendm = 1 # end of previous move in measures (need this so we can display beginning/ending measures of gaps + for i in self.main.core.GetMoves(): + (m1, m2, begin, end) = self.main.core.GetMoveInfo(i[0]) + begin -= 1 # since counts start at 1 not 0 + # we don't decrement end because we want the move to be drawn to the end of its last count + if begin > oldend: # if there is a gap between this move and the previous one + self.DrawMove(oldend, begin, "", GAP_BORDER_COLOUR, GAP_COLOUR, GAP_BORDER_COLOUR, dc) + self.gaps.append((oldendm, m1 - 1, oldend, begin)) + self.DrawMove(begin, end, i[1], MOVE_BORDER_COLOUR, MOVE_COLOUR, MOVE_NAME_COLOUR, dc) + oldend = end + oldendm = m2 + 1 + + i = self.main.core.GetCurrentMove() + (m1, m2, begin, end) = self.main.core.GetMoveInfo(i[0]) + begin -= 1 # since counts start at 1 not 0 + # we don't decrement end because we want the move to be drawn to the end of its last count + self.DrawMove(begin, end, i[1], SELECTED_MOVE_BORDER_COLOUR, SELECTED_MOVE_COLOUR, SELECTED_MOVE_NAME_COLOUR, dc) + + def DrawWaypoints(self, dc): + for wp in self.main.core.GetListOfWayPoints(): + self.DrawWaypoint(wp, WAYPOINT_COLOUR, dc) + + if self.animatePt != -1: + self.DrawWaypoint([self.animatePt], ANIMATE_COLOUR, dc) + + def DrawText(self, dc): +# dc.SetTextForeground(TEXT_COLOUR) + dc.SetTextForeground(self.fgColour) + + textRect = wx.Rect(0, MOVE_HEIGHT + WAYPOINT_HEIGHT, self.GetRect().GetWidth(), TEXT_HEIGHT) + + if self.animatePt != -1: + dc.DrawImageLabel("Animating @ Count: " + str(self.animatePt), wx.NullBitmap, textRect, wx.ALIGN_CENTRE) + + if self.mouseX != -1: + count = self.mouseX / self.step + dc.DrawImageLabel("Count: " + str(int(round(count))), wx.NullBitmap, textRect, wx.ALIGN_LEFT | wx.ALIGN_CENTRE_VERTICAL) + + if self.mouseY > -1 and self.mouseY <= MOVE_HEIGHT: + ret = self.PickMove(count) + if ret is not None: + (m, m1, m2, begin, end, i) = ret + if begin == end: + count = "Count" + else: + count = "Counts" + + if int(m1) == int(m2): + measure = "Measure" + else: + measure = "Measures" + + dc.DrawImageLabel(m[1] + " [" + count + ": " + str(begin) + u"\u2013" + str(end) + ", " + measure + ": " + str(int(m1)) + u"\u2013" + str(int(m2)) + "]", wx.NullBitmap, textRect, wx.ALIGN_RIGHT | wx.ALIGN_CENTRE_VERTICAL) + else: + gap = self.PickGap(count) + if gap is not None: + (m1, m2, begin, end) = gap + if begin == end: + count = "Count" + else: + count = "Counts" + + if int(m1) == int(m2): + measure = "Measure" + else: + measure = "Measures" + + dc.DrawImageLabel("[" + count + ": " + str(begin) + u"\u2013" + str(end) + ", " + measure + ": " + str(int(m1)) + u"\u2013" + str(int(m2)) + "]", wx.NullBitmap, textRect, wx.ALIGN_RIGHT | wx.ALIGN_CENTRE_VERTICAL) + + elif self.mouseY > MOVE_HEIGHT and self.mouseY <= MOVE_HEIGHT + WAYPOINT_HEIGHT: + wp = self.PickWaypoint(count) + if wp is not None: + dc.DrawImageLabel("Waypoint[Count: " + str(wp[0]) + ", Time: " + str(wp[1]) + "]", wx.NullBitmap, textRect, wx.ALIGN_RIGHT | wx.ALIGN_CENTRE_VERTICAL) + + def DrawMove(self, begin, end, name, borderColour, moveColour, nameColour, dc): + dc.SetPen(wx.Pen(borderColour, MOVE_BORDER_WIDTH)) + dc.SetBrush(wx.Brush(moveColour)) + dc.SetTextForeground(nameColour) + + moveRect = wx.Rect(begin * self.step, 0, (end - begin) * self.step, MOVE_HEIGHT) + dc.DrawRoundedRectangleRect(moveRect, MOVE_BORDER_CORNER_RADIUS) + if name != "" and moveRect.GetWidth() >= dc.GetFullTextExtent(name)[0]: + dc.DrawImageLabel(name, wx.NullBitmap, moveRect, wx.ALIGN_CENTRE) + + def DrawWaypoint(self, wp, colour, dc): + dc.SetPen(wx.Pen(colour, 0)) + dc.SetBrush(wx.Brush(colour)) + + x = wp[0] * self.step; + list = [] + list.append(wx.Point(x - WAYPOINT_WIDTH / 2.0, MOVE_HEIGHT + WAYPOINT_HEIGHT)) + list.append(wx.Point(x + WAYPOINT_WIDTH / 2.0, MOVE_HEIGHT + WAYPOINT_HEIGHT)) + list.append(wx.Point(x, MOVE_HEIGHT)) + + dc.DrawPolygon(list) + + def PickMove(self, count): + i = 0 + for m in self.main.core.GetMoves(): + (m1, m2, begin, end) = self.main.core.GetMoveInfo(m[0]) + if count >= begin and count <= end: + return (m, m1, m2, begin, end, i) + i += 1 + + return None + + def PickGap(self, count): + for m in self.gaps: + (m1, m2, begin, end) = m + if count >= begin and count <= end: + return (m1, m2, begin, end) + + return None + + def PickWaypoint(self, count): + for wp in self.main.core.GetListOfWayPoints(): + if abs(wp[0] - count) <= WAYPOINT_SELECT_RADIUS: + return wp + + return None + + def OnResize(self, event): + (w, h) = self.GetSizeTuple() + self.rect = wx.Rect(0, 0, w, h) + self.step = float(w) / self.main.core.GetTotalCounts() + + self.Refresh(False) + + def AnimateCount(self, count): # tell the timebar that we are animating and are at the given count (-1 for stop animating) + self.animatePt = count + self.Refresh(False) + + def OnLeftClick(self, event): + count = self.mouseX / self.step + + if self.mouseY > -1 and self.mouseY <= MOVE_HEIGHT: + ret = self.PickMove(count) + if ret is not None: + self.main.moveList.SetItemState(ret[5], wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED) + else: + gap = self.PickGap(count) + if gap is not None: + (m1, m2, begin, end) = gap + self.main.CreateMove(None, str(int(m1)), str(int(m2))) + + self.Refresh(False) + + def OnRightClick(self, event): + pass + + def OnLeftUnclick(self, event): + pass + + def OnRightUnclick(self, event): + pass + + def OnMouseMove(self, event): + self.mouseX = event.m_x + self.mouseY = event.m_y + self.Refresh(False) + + def OnMouseExit(self, event): + self.mouseX = -1 + self.mouseY = -1 + self.Refresh(False) diff --git a/RankPanda/Move.py b/RankPanda/Move.py index 6cc7e0d..55d2ae7 100755 --- a/RankPanda/Move.py +++ b/RankPanda/Move.py @@ -1,309 +1,309 @@ -import RankIDGen -import Rank -import pprint - - -# TODO(astory): look into python attributes. If we're going to go with -# heavyweight java-style classes, we might as well let python do the heavy -# lifting and code generation. -class Move(object): - # TODO(astory): optional arguments - def __init__(self, startCount, length, prior, following, number): - self._startCount = startCount - self._length = length - self._idRankIndex = dict() #maps rank ID to rank object - self._nameRankIndex = dict() #maps rank name to rank object - self._RankIDGen = RankIDGen.RankIDGen() - self._name = None - self._number = None - self.SetNumber(number) - self._prior = prior - self._following = following - self._listOfActiveRanks = [] #selected ranks - self._listOfActiveCommands = [] #selected commands - self._moveText = None - self._moveTextOverwrite = None - - def GetNameRankIndex(self): - return self._nameRankIndex - - def GetStartCount(self): - return self._startCount - - def SetStartCount(self, startCount): - self._startCount = startCount - - def GetListOfActiveCommands(self): - return self._listOfActiveCommands - - def GetSelectedRanks(self): - return self._listOfActiveRanks - - def SetSelectedRanks(self, newList): - self._listOfActiveRanks = newList - - def GetLength(self): - return self._length - - def SetLength(self, newLength): - """Set length of move to newLength and update all ranks' commands.""" - self._length = newLength - self.UpdateAllRanksCommandList() - - def GetRankIDGen(self): - return self._RankIDGen - - def GetNumber(self): - return self._number - - def SetNumber(self, number): - if (number is not None and - ((self._name is None) or - (self._name == ('Move ' + str(self._number))))): - self._name = ('Move ' + str(number)) #If the move has a nonstandard name (not 'Move' + num), dont change it. - self._number = number - - def GetName(self): - return self._name - - def SetName(self, name): - self._name = name - - def GetPrior(self): - return self._prior - - def SetPrior(self, newPrior): - """Set the prior move to this move and update all ranks' commands.""" - self._prior = newPrior - self.UpdateAllRanksCommandList() - - def GetAllRanks(self): - return self._idRankIndex.values() - - def GetAllNamedRanks(self): - return self._nameRankIndex.values() - - # Shortcut - def UpdateAllRanksCommandList(self): - i = 0 - items = self._nameRankIndex.items() - while (i < len(items)): - items[i][1].UpdateCommandList() - i = i + 1 - - def GetFollowing(self): - return self._following - - def SetFollowing(self, following): - self._following = following - - # TODO(astory): make name an optional argument - def CreateRank(self, location, name=None): - """Create a new rank for this move. - - Creates a new rank and adds it to the IDRankIndex. Names the rank if a - name is given. - """ - r = Rank.Rank(location, self) - self._idRankIndex[r.GetID()] = r - if (name is not None): - self.NameRank(r.GetID(), name) - r.UpdateCommandList() - return r - - def NameRank(self, ID, name): - """Set the name of a rank in this move.""" - if (self.LookUpName(name) is None): #doesnt allow you to change name? - r = self._idRankIndex[ID] - if (r.GetName() in self._nameRankIndex): - del self._nameRankIndex[r.GetName()] - r.SetName(name) - self._nameRankIndex[name] = r - - # Deletes a rank. Note that this will also need to reset the command list - # for the rank in the following move, if applicable. - def DeleteRank(self, id): - """Delete a rank, and update the following move's corresponding rank.""" - r = self._idRankIndex[id] - # TODO(astory): if r in _listOfActiveRanks - if (self._listOfActiveRanks.count(r) != 0): - self._listOfActiveRanks.remove(r) - del self._idRankIndex[id] - name = r.GetName() - if (name is not None): - del self._nameRankIndex[name] - if (self._following is not None): - if (self._following.LookUpName(name) is not None): - self._following.LookUpName(name).UpdateCommandList() - - # Should be self-explanatory. - def DeleteAllRanks(self): - i = 0 - items = self._idRankIndex.items() - while (i < len(items)): - ID = items[i][0] - self.DeleteRank(ID) - i = i + 1 - - # TODO(astory): this is ugly, and also possibly crash-laden. FIXME - def MergeWithPrior(self): - """Merge this move with the previous one. - - See song.MergeMoves() for more documentation. Also resets the prior and - following references of moves immediately before and after the merged - moves. - - Returns: - The new move created - """ - prior = self.GetPrior() - priorprior = prior.GetPrior() - following = self.GetFollowing() - newMove = Move(prior._startCount, - (prior._length + self._length), - priorprior, - following, - prior.GetNumber()) - if (priorprior is not None): - priorprior.SetFollowing(newMove) - if (following is not None): - following.SetPrior(newMove) - PriorIDMarkedIndex = dict() - i = 0 - Iterator = prior._idRankIndex.items() - l = len(Iterator) - while (i < l): - PriorIDMarkedIndex[Iterator[i][0]] = False - i = i + 1 - i = 0 - Iterator = self._idRankIndex.items() - l = len(Iterator) - while (i < l): - rank = Iterator[i][1] - name = rank.GetName() - newMove.CreateRank(rank.GetEndLocation(), name=name) - if (name is not None): - oldRank = prior.LookUpName(name) - if (priorprior is not None): - oldoldRank = priorprior.LookUpName(name) - else: - oldoldRank = None - newRank = newMove.LookUpName(name) - newRank.hold = rank.hold - if (oldRank is not None): - newRank.hold = (rank.hold or oldRank.hold) - oldID = oldRank.GetID() - PriorIDMarkedIndex[oldID] = True - if (oldoldRank is not None): - if (newRank.hold): - newRank.SetCommandList( - oldRank.GetCommandList().extend( - rank.GetCommandList())) - else: - newRank.UpdateCommandList() - i = i + 1 - i = 0 - Iterator = PriorIDMarkedIndex.items() - l = len(Iterator) - while (i < l): - if (Iterator[i][1]): - pass - else: - ID = Iterator[i][0] - rank = prior.LookUpID(ID) - name = rank.GetName() - newMove.CreateRank(rank.GetEndLocation(), name=name) - newRank = newMove.LookUpName(name) - newRank.hold = rank.hold - if (name is not None): - if (priorprior is not None): - oldoldRank = priorprior.LookUpName(name) - if (oldoldRank is not None): - commandList = rank.GetCommantList() - commandList.append(Command.MarkTime(self._length, rank.GetEndLocation())) - newRank.SetCommandList(commandList) - if (following is not None): - followingRank = following.LookUpName(name) - if (followingRank is not None): - followingRank.UpdateCommandList() - newRank.UpdateCommandList() - i = i + 1 - return newMove - - # TODO(astory): really? return a list? FIXME - def Split(self, count): - """Split this move at count. - - Also resets the prior and following references of moves immediately - before and after the split move. - - Returns: - A list of the two new moves created - """ - newMoveFirst = Move(self._startCount, count, self._prior, - None, self.GetNumber()) - newMoveSecond = Move((self._startCount + count), (self._length - count), - newMoveFirst, self._following, - (self.GetNumber() + 1)) - newMoveFirst.SetFollowing(newMoveSecond) - if (self._prior is not None): - self._prior.SetFollowing(newMoveFirst) - if (self._following is not None): - self._following.SetPrior(newMoveSecond) - i = 0 - Iterator = self._idRankIndex.items() - l = len(Iterator) - while (i < l): - rank = Iterator[i][1] - name = rank.GetName() - newMoveFirst.CreateRank(rank.GetCalculatedLocation(count), name=name) - newMoveSecond.CreateRank(rank.GetEndLocation(), name=name) - if ((rank.hold) and (rank.GetName() is not None) and - (self._prior.LookUpName(name) is not None)): - newRank1 = newMoveFirst.LookUpName(name) - newRank2 = newMoveSecond.LookUpName(name) - newRank1.hold = rank.hold - newRank2.hold = rank.hold - i = 0 - tot = count - while (tot > 0): - tot = tot - rank.GetCommandList()[i].GetLength() - i = i + 1 - i = i - 1 - tot = tot + rank.GetCommandList()[i].GetLength() - beginLoc = rank.GetCommandList()[i - 1].GetEndLocation() - splitCommand = rank.GetCommandList()[i].Split(tot, beginLoc) - firstCommandList = rank.GetCommandList()[0:(i - 1)] - firstCommandList.append(splitCommand[0]) - secondCommandList = [splitCommand[1]] - secondCommandList.extend(rank.GetCommandList()[(i + 1):-1]) - newRank1.SetCommandList(firstCommandList) - newRank2.SetCommandList(secondCommandList) - i = i + 1 - return [newMoveFirst, newMoveSecond] - - def LookUpName(self, name): - """Return the rank with name in this move, or None.""" - if (name in self._nameRankIndex): - return self._nameRankIndex[name] - else: - return None - - def LookUpID(self, ID): - """Return the rank with ID in this move, or None.""" - if (ID in self._idRankIndex): - return self._idRankIndex[ID] - else: - return None - - def GetMoveText(self): - return self._moveText - - def SetMoveText(self, moveText): - self._moveText = moveText - - def GetMoveTextOverwrite(self): - return self._moveTextOverwrite - - def SetMoveTextOverwrite(self, moveTextOverwrite): - self._moveTextOverwrite = moveTextOverwrite +import RankIDGen +import Rank +import pprint + + +# TODO(astory): look into python attributes. If we're going to go with +# heavyweight java-style classes, we might as well let python do the heavy +# lifting and code generation. +class Move(object): + # TODO(astory): optional arguments + def __init__(self, startCount, length, prior, following, number): + self._startCount = startCount + self._length = length + self._idRankIndex = dict() #maps rank ID to rank object + self._nameRankIndex = dict() #maps rank name to rank object + self._RankIDGen = RankIDGen.RankIDGen() + self._name = None + self._number = None + self.SetNumber(number) + self._prior = prior + self._following = following + self._listOfActiveRanks = [] #selected ranks + self._listOfActiveCommands = [] #selected commands + self._moveText = None + self._moveTextOverwrite = None + + def GetNameRankIndex(self): + return self._nameRankIndex + + def GetStartCount(self): + return self._startCount + + def SetStartCount(self, startCount): + self._startCount = startCount + + def GetListOfActiveCommands(self): + return self._listOfActiveCommands + + def GetSelectedRanks(self): + return self._listOfActiveRanks + + def SetSelectedRanks(self, newList): + self._listOfActiveRanks = newList + + def GetLength(self): + return self._length + + def SetLength(self, newLength): + """Set length of move to newLength and update all ranks' commands.""" + self._length = newLength + self.UpdateAllRanksCommandList() + + def GetRankIDGen(self): + return self._RankIDGen + + def GetNumber(self): + return self._number + + def SetNumber(self, number): + if (number is not None and + ((self._name is None) or + (self._name == ('Move ' + str(self._number))))): + self._name = ('Move ' + str(number)) #If the move has a nonstandard name (not 'Move' + num), dont change it. + self._number = number + + def GetName(self): + return self._name + + def SetName(self, name): + self._name = name + + def GetPrior(self): + return self._prior + + def SetPrior(self, newPrior): + """Set the prior move to this move and update all ranks' commands.""" + self._prior = newPrior + self.UpdateAllRanksCommandList() + + def GetAllRanks(self): + return self._idRankIndex.values() + + def GetAllNamedRanks(self): + return self._nameRankIndex.values() + + # Shortcut + def UpdateAllRanksCommandList(self): + i = 0 + items = self._nameRankIndex.items() + while (i < len(items)): + items[i][1].UpdateCommandList() + i = i + 1 + + def GetFollowing(self): + return self._following + + def SetFollowing(self, following): + self._following = following + + # TODO(astory): make name an optional argument + def CreateRank(self, location, name=None): + """Create a new rank for this move. + + Creates a new rank and adds it to the IDRankIndex. Names the rank if a + name is given. + """ + r = Rank.Rank(location, self) + self._idRankIndex[r.GetID()] = r + if (name is not None): + self.NameRank(r.GetID(), name) + r.UpdateCommandList() + return r + + def NameRank(self, ID, name): + """Set the name of a rank in this move.""" + if (self.LookUpName(name) is None): #doesnt allow you to change name? + r = self._idRankIndex[ID] + if (r.GetName() in self._nameRankIndex): + del self._nameRankIndex[r.GetName()] + r.SetName(name) + self._nameRankIndex[name] = r + + # Deletes a rank. Note that this will also need to reset the command list + # for the rank in the following move, if applicable. + def DeleteRank(self, id): + """Delete a rank, and update the following move's corresponding rank.""" + r = self._idRankIndex[id] + # TODO(astory): if r in _listOfActiveRanks + if (self._listOfActiveRanks.count(r) != 0): + self._listOfActiveRanks.remove(r) + del self._idRankIndex[id] + name = r.GetName() + if (name is not None): + del self._nameRankIndex[name] + if (self._following is not None): + if (self._following.LookUpName(name) is not None): + self._following.LookUpName(name).UpdateCommandList() + + # Should be self-explanatory. + def DeleteAllRanks(self): + i = 0 + items = self._idRankIndex.items() + while (i < len(items)): + ID = items[i][0] + self.DeleteRank(ID) + i = i + 1 + + # TODO(astory): this is ugly, and also possibly crash-laden. FIXME + def MergeWithPrior(self): + """Merge this move with the previous one. + + See song.MergeMoves() for more documentation. Also resets the prior and + following references of moves immediately before and after the merged + moves. + + Returns: + The new move created + """ + prior = self.GetPrior() + priorprior = prior.GetPrior() + following = self.GetFollowing() + newMove = Move(prior._startCount, + (prior._length + self._length), + priorprior, + following, + prior.GetNumber()) + if (priorprior is not None): + priorprior.SetFollowing(newMove) + if (following is not None): + following.SetPrior(newMove) + PriorIDMarkedIndex = dict() + i = 0 + Iterator = prior._idRankIndex.items() + l = len(Iterator) + while (i < l): + PriorIDMarkedIndex[Iterator[i][0]] = False + i = i + 1 + i = 0 + Iterator = self._idRankIndex.items() + l = len(Iterator) + while (i < l): + rank = Iterator[i][1] + name = rank.GetName() + newMove.CreateRank(rank.GetEndLocation(), name=name) + if (name is not None): + oldRank = prior.LookUpName(name) + if (priorprior is not None): + oldoldRank = priorprior.LookUpName(name) + else: + oldoldRank = None + newRank = newMove.LookUpName(name) + newRank.hold = rank.hold + if (oldRank is not None): + newRank.hold = (rank.hold or oldRank.hold) + oldID = oldRank.GetID() + PriorIDMarkedIndex[oldID] = True + if (oldoldRank is not None): + if (newRank.hold): + newRank.SetCommandList( + oldRank.GetCommandList().extend( + rank.GetCommandList())) + else: + newRank.UpdateCommandList() + i = i + 1 + i = 0 + Iterator = PriorIDMarkedIndex.items() + l = len(Iterator) + while (i < l): + if (Iterator[i][1]): + pass + else: + ID = Iterator[i][0] + rank = prior.LookUpID(ID) + name = rank.GetName() + newMove.CreateRank(rank.GetEndLocation(), name=name) + newRank = newMove.LookUpName(name) + newRank.hold = rank.hold + if (name is not None): + if (priorprior is not None): + oldoldRank = priorprior.LookUpName(name) + if (oldoldRank is not None): + commandList = rank.GetCommantList() + commandList.append(Command.MarkTime(self._length, rank.GetEndLocation())) + newRank.SetCommandList(commandList) + if (following is not None): + followingRank = following.LookUpName(name) + if (followingRank is not None): + followingRank.UpdateCommandList() + newRank.UpdateCommandList() + i = i + 1 + return newMove + + # TODO(astory): really? return a list? FIXME + def Split(self, count): + """Split this move at count. + + Also resets the prior and following references of moves immediately + before and after the split move. + + Returns: + A list of the two new moves created + """ + newMoveFirst = Move(self._startCount, count, self._prior, + None, self.GetNumber()) + newMoveSecond = Move((self._startCount + count), (self._length - count), + newMoveFirst, self._following, + (self.GetNumber() + 1)) + newMoveFirst.SetFollowing(newMoveSecond) + if (self._prior is not None): + self._prior.SetFollowing(newMoveFirst) + if (self._following is not None): + self._following.SetPrior(newMoveSecond) + i = 0 + Iterator = self._idRankIndex.items() + l = len(Iterator) + while (i < l): + rank = Iterator[i][1] + name = rank.GetName() + newMoveFirst.CreateRank(rank.GetCalculatedLocation(count), name=name) + newMoveSecond.CreateRank(rank.GetEndLocation(), name=name) + if ((rank.hold) and (rank.GetName() is not None) and + (self._prior.LookUpName(name) is not None)): + newRank1 = newMoveFirst.LookUpName(name) + newRank2 = newMoveSecond.LookUpName(name) + newRank1.hold = rank.hold + newRank2.hold = rank.hold + i = 0 + tot = count + while (tot > 0): + tot = tot - rank.GetCommandList()[i].GetLength() + i = i + 1 + i = i - 1 + tot = tot + rank.GetCommandList()[i].GetLength() + beginLoc = rank.GetCommandList()[i - 1].GetEndLocation() + splitCommand = rank.GetCommandList()[i].Split(tot, beginLoc) + firstCommandList = rank.GetCommandList()[0:(i - 1)] + firstCommandList.append(splitCommand[0]) + secondCommandList = [splitCommand[1]] + secondCommandList.extend(rank.GetCommandList()[(i + 1):-1]) + newRank1.SetCommandList(firstCommandList) + newRank2.SetCommandList(secondCommandList) + i = i + 1 + return [newMoveFirst, newMoveSecond] + + def LookUpName(self, name): + """Return the rank with name in this move, or None.""" + if (name in self._nameRankIndex): + return self._nameRankIndex[name] + else: + return None + + def LookUpID(self, ID): + """Return the rank with ID in this move, or None.""" + if (ID in self._idRankIndex): + return self._idRankIndex[ID] + else: + return None + + def GetMoveText(self): + return self._moveText + + def SetMoveText(self, moveText): + self._moveText = moveText + + def GetMoveTextOverwrite(self): + return self._moveTextOverwrite + + def SetMoveTextOverwrite(self, moveTextOverwrite): + self._moveTextOverwrite = moveTextOverwrite diff --git a/RankPanda/Point.py b/RankPanda/Point.py index 1bdfa5f..f36fd8b 100755 --- a/RankPanda/Point.py +++ b/RankPanda/Point.py @@ -1,63 +1,63 @@ -import math - -"""A simple point module. - -Exported Methods: - -Distance -- Euclidian distance between two points - -Exported Classes: - -Point -- a simple point class with an x and y position. -""" - -def Distance(p1, p2): - """Get the Euclidean distance between two points""" - return math.sqrt((p1.x - p2.x)**2 + (p1.y - p2.y)**2) - -# TODO(astory): differentiate slopes and points. They shouldn't be represented -# with the same class because not all of the functions make sense. -class Point(object): - """ A simple point class with an x and y position. - - This class simply represents a point. It's often an x,y coordinate - on-screen, thought it's also sometimes a pair of slopes (dx/dt and dy/dt). - - Possible modification: Get rid of this and convert to all wx.Point perhaps? - - Public functions: - CompareTo -- sees if two points are within epsilon of each other - Clone -- produce a deep copy. - """ - def __init__(self, x, y): - self.x = x - self.y = y - - def CompareTo(self, p1, epsilon=0.5): - """Compare two points, and return if they are within epsilon - - Args: - p1: the other point to which to compare - epsilon: the minimum distance for equality. - - Returns: - A boolean, whether the points are within epsilon of each other or - not. - """ - return Distance(self, p1) < epsilon - - def __eq__(self, other): - return self.CompareTo(other, epsilon=0.00001) - - def __ne__(self, other): - return not self.__eq__(other) - - def Clone(self): - """Produce a deep copy of this point""" - return Point(self.x, self.y) - - def __repr__(self): - return ('Point with x = ' + str(self.x) + ', y = ' + str(self.y)) - - def __str__(self): - return self.__repr__() +import math + +"""A simple point module. + +Exported Methods: + +Distance -- Euclidian distance between two points + +Exported Classes: + +Point -- a simple point class with an x and y position. +""" + +def Distance(p1, p2): + """Get the Euclidean distance between two points""" + return math.sqrt((p1.x - p2.x)**2 + (p1.y - p2.y)**2) + +# TODO(astory): differentiate slopes and points. They shouldn't be represented +# with the same class because not all of the functions make sense. +class Point(object): + """ A simple point class with an x and y position. + + This class simply represents a point. It's often an x,y coordinate + on-screen, thought it's also sometimes a pair of slopes (dx/dt and dy/dt). + + Possible modification: Get rid of this and convert to all wx.Point perhaps? + + Public functions: + CompareTo -- sees if two points are within epsilon of each other + Clone -- produce a deep copy. + """ + def __init__(self, x, y): + self.x = x + self.y = y + + def CompareTo(self, p1, epsilon=0.5): + """Compare two points, and return if they are within epsilon + + Args: + p1: the other point to which to compare + epsilon: the minimum distance for equality. + + Returns: + A boolean, whether the points are within epsilon of each other or + not. + """ + return Distance(self, p1) < epsilon + + def __eq__(self, other): + return self.CompareTo(other, epsilon=0.00001) + + def __ne__(self, other): + return not self.__eq__(other) + + def Clone(self): + """Produce a deep copy of this point""" + return Point(self.x, self.y) + + def __repr__(self): + return ('Point with x = ' + str(self.x) + ', y = ' + str(self.y)) + + def __str__(self): + return self.__repr__() diff --git a/RankPanda/Printer.py b/RankPanda/Printer.py index 2a33bd5..2346a41 100644 --- a/RankPanda/Printer.py +++ b/RankPanda/Printer.py @@ -1,170 +1,170 @@ -# Testing reportlab - -#import reportlab -import os -from reportlab.pdfgen import canvas -from reportlab.lib.units import inch, cm -from reportlab.lib.pagesizes import LETTER -from reportlab.lib.fonts import addMapping -from reportlab.pdfbase import pdfmetrics, ttfonts -import PIL -from PIL import Image -import CoreWrapper -import GUIField - -#register DejaVuSansMono -pdfmetrics.registerFont(ttfonts.TTFont('DejaVuSansMono', 'DejaVuSansMono.ttf')) -addMapping('DejaVuSansMono', 0, 0, 'DejaVuSansMono') - -#constants -page_size = (LETTER[1],LETTER[0]) #landscape of letter standard -field_size = (9*inch, 5*inch) -field_height = 3*inch -widthHeightRatio= GUIField.FIELD_LENGTH_STEPS/GUIField.FIELD_WIDTH_STEPS - -top_margin = page_size[1] - inch -bottom_margin = inch -left_margin = inch -right_margin = page_size[0] - inch -frame_width = right_margin - left_margin - -def splitString(commStr, charLim, splitChar): - strLength=0 - aStr="" - bStr=commStr - i=0 - while (strLength<(charLim-2)): - partStr=bStr.partition(splitChar) - aStrNew = aStr + partStr[0] + splitChar - bStrNew = partStr[2] - strLength=len(aStrNew) - if(strLength charactersPerCol): - splStr=splitString(commString, charactersPerCol, ",") - if (k == columns-1): textObj.textLine(splStr[0]) - else: textObj.textOut(splStr[0]) - if (splStr[1] != ""): - if(i+columns > len(stringList)): - j=0 - lenStrL=len(stringList) - while(j < (i+columns-lenStrL+1)): - stringList.insert(len(stringList),"") - j=j+1 - stringList.insert(i+columns, " " + splStr[1]) - i=i+1 - else: - if (k == columns-1): textObj.textLine(commString) - else: textObj.textOut(commString) - i=i+1 - k=k+1 - textObj.setXPos((right_margin-left_margin)/columns) - - -def printDrill(song, stringPath, moveNames, commandStrings, fontSize, columnsArr, measureInfo, movetexts): - - if (not (stringPath.endswith(".pdf"))): - stringPath=stringPath + ".pdf" - - if (os.path.exists(stringPath)): - if (os.path.isfile(stringPath)): - #check if open? - os.remove(stringPath) - - canv = canvas.Canvas(stringPath, page_size) - i=0 - while (i charactersPerCol): + splStr=splitString(commString, charactersPerCol, ",") + if (k == columns-1): textObj.textLine(splStr[0]) + else: textObj.textOut(splStr[0]) + if (splStr[1] != ""): + if(i+columns > len(stringList)): + j=0 + lenStrL=len(stringList) + while(j < (i+columns-lenStrL+1)): + stringList.insert(len(stringList),"") + j=j+1 + stringList.insert(i+columns, " " + splStr[1]) + i=i+1 + else: + if (k == columns-1): textObj.textLine(commString) + else: textObj.textOut(commString) + i=i+1 + k=k+1 + textObj.setXPos((right_margin-left_margin)/columns) + + +def printDrill(song, stringPath, moveNames, commandStrings, fontSize, columnsArr, measureInfo, movetexts): + + if (not (stringPath.endswith(".pdf"))): + stringPath=stringPath + ".pdf" + + if (os.path.exists(stringPath)): + if (os.path.isfile(stringPath)): + #check if open? + os.remove(stringPath) + + canv = canvas.Canvas(stringPath, page_size) + i=0 + while (i 0: - # if the last one is MT - if (self._commandList[l - 1].GetName() == 'MT'): - i = l - 1 - # go backwards until you run out of commands or it's not a MT - while ((i >= 0) and (self._commandList[i].GetName() == 'MT')): - i = i - 1 - # if you didn't reach the beginning - if (i >= 0): - # reset to the first end MT - i = i + 1 - # remove all the trailing MTs - while (len(self._commandList) > i): - self._commandList.pop() - else: # we reached the beginning - # slap on a mark time for the right length - self._commandList =\ - [Commands.MarkTime(self._move.GetLength(),\ - self._commandList[-1].GetEndLocation())] - # Add a marktime to the end to fill in the gaps - total = self.CalculateTotalCountsOfCommands() - if (total < self._move.GetLength()): - self._commandList.append(\ - Commands.MarkTime(\ - self._move.GetLength() - total,\ - self._commandList[-1].GetEndLocation())) - - def GetPrior(self): - """Returns this rank from the previous move if it exists, else, None""" - if (self._name is None): - return None - p = self._move.GetPrior() - if (p is None): - return None - return (p.LookUpName(self._name)) - - def GetFollowing(self): - """Returns this rank from the next move if it exists, else, None""" - if (self._name is None): - return None - p = self._move.GetFollowing() - if (p is None): - return None - return (p.LookUpName(self._name)) - - - # Starting from the prior Rank's _endLocation, go through each Command in - # the Command List, generating the end location of each, until you've used - # up enough counts to hit the given count value. Note that this may not be - # the entire command list. This doesn't actually make any permanent - # changes. - - # Return None if you can't calcuclate the location (because of no - # command list) - def GetCalculatedLocation(self, count): - """Returns this rank's location at count count of the move, which may - not be the end location if count < the number of counts of commands""" - priorRank = self.GetPrior() - if (priorRank is None): - return self.GetEndLocation() - # TODO(astory): this should be condensible, do so after writing tests - if (len(self._commandList) == 0): - return self.GetEndLocation() - if (count > self.CalculateTotalCountsOfCommands()): - return self.GetEndLocation() - curLocation = priorRank.GetEndLocation() - i = 0 - countsLeft = count - while (countsLeft > 0): - curCommand = self._commandList[i] - # TODO(astory): consider speeding up by adding whole length of - # command if it fits in our count total - if (countsLeft >= curCommand.GetLength()): - curLocation = curCommand.CalcLocation(curCommand.GetLength(), curLocation) - else: - curLocation = curCommand.CalcLocation(countsLeft, curLocation) - countsLeft = countsLeft - curCommand.GetLength() - i = i + 1 - return curLocation - - def GetEndLocation(self): - """Returns the rank's final location""" - return copy.deepcopy(self._endLocation) - - # TODO(astory): this might be superfluous since GetCalculatedLocation never - # returns none AFAICT - def GetLocationAtCount(self, count): - """Returns the rank's location at the given count""" - loc = self.GetCalculatedLocation(count) - if (loc is None): - loc = self.GetEndLocation() - return loc - - - # TODO(astory): clean - # Used when a rank is moved around. If self.hold == false and - # self.grabbed == false, re-generate the command list. - - # Should never be called! -# def SetEndLocation(self, newEndLocation): -# pass - - - - # Adds the given command to the command list in the desired location - # Number should be the number in the command list that the new command - # will occupy. So, if you're inserting before the fourth command, - # number should be 3 (because it's 0-indexed.) - def AddBasicCommand(self, commandName, number, length, name): - """Insert a command of type commandName at index number of the command - list, for length counts with name name, but only if the rank is locked, - and this rank is represented in a previous command""" - if ((not self.hold) or (self.GetPrior() is None)): - return - if (number == 0): - beginLocation = self.GetPrior().GetEndLocation() - else: - beginLocation = self._commandList[number - 1].GetEndLocation() - - #(Brady) simplified this by adding a dictionary to hash names to the function - newCommand = commandHash[commandName](length, beginLocation) #May want to catch a KeyError in case of typo or something... - - self._commandList.insert(number, newCommand) - if (name is not None): - newCommand.SetName(name) - self.UpdateCommandList() - - # TODO(astory): merge into the previous with optional arguments - def AddDTP(self, number, length, name, endLocation): - """Add a DTP, which requires an end location as well""" - if ((not self.hold) or (self.GetPrior() is None)): - return - if (number == 0): - beginLocation = self.GetPrior().GetEndLocation() - else: - beginLocation = self._commandList[number - 1].GetEndLocation() - newCommand = Commands.DTP(length, beginLocation, endLocation) - self._commandList.insert(number, newCommand) - if (name is not None): - newCommand.SetName(name) - self.UpdateCommandList() - - # TODO(astory): merge into the previous with optional arguments - def AddCurve(self, number, length, name, endLocation): - """Add a curve, which requires an end location""" - if ((not self.hold) or (self.GetPrior() is None)): - return - if (number == 0): - beginLocation = self.GetPrior().GetEndLocation() - else: - beginLocation = self._commandList[number - 1].GetEndLocation() - newCommand = Commands.Curve(length, beginLocation, endLocation) - self._commandList.insert(number, newCommand) - if (name is not None): - newCommand.SetName(name) - self.UpdateCommandList() - - # TODO(astory): merge into the previous with optional arguments - def AddFTA1(self, number, length, name, endLocation, listOfFTAPoints): - """Add an FTA at 1""" - if ((not self.hold) or (self.GetPrior() is None)): - return - if (number == 0): - beginLocation = self.GetPrior().GetEndLocation() - else: - beginLocation = self._commandList[number - 1].GetEndLocation() - newCommand = Commands.FTA1(length, beginLocation, endLocation, listOfFTAPoints) - self._commandList.insert(number, newCommand) - if (name is not None): - newCommand.SetName(name) - self.UpdateCommandList() - - # TODO(astory): merge into the previous with optional arguments - def AddFTA0(self, number, length, name, endLocation, listOfFTAPoints): - """Add an FTA at 0""" - if ((not self.hold) or (self.GetPrior() is None)): - return - if (number == 0): - beginLocation = self.GetPrior().GetEndLocation() - else: - beginLocation = self._commandList[number - 1].GetEndLocation() - newCommand = Commands.FTA0(length, beginLocation, endLocation, listOfFTAPoints) - self._commandList.insert(number, newCommand) - if (name is not None): - newCommand.SetName(name) - self.UpdateCommandList() - - def MoveCommandUp(self, commandNumber): - """Move commandNumber command up the command list""" - if ((not self.hold) or (self.GetPrior() is None) or (commandNumber == 0)): - return - command = self._commandList.pop(commandNumber) - self._commandList.insert(commandNumber - 1, command) - self.UpdateCommandList() - - def MoveCommandDown(self, commandNumber): - """Move commandNumber command down the command list""" - if ((not self.hold) or (self.GetPrior() is None)\ - or (commandNumber == (len(self._commandList) - 1))): - return - command = self._commandList.pop(commandNumber) - self._commandList.insert(commandNumber + 1, command) - self.UpdateCommandList() - - def DeleteCommand(self, commandNumber): - """Remove commandNumber command""" - if ((not self.hold) or (self.GetPrior() is None)): - return - self._commandList.pop(commandNumber) - tot = 0 - i = 0 - while (i < len(self._commandList)): - tot = tot + self._commandList[i].GetLength() - i = i + 1 - self.UpdateCommandList() - # TODO(astory): clean up -# if (tot < self._move.GetLength()): -# if (self._commandList[-1].GetName() == "MarkTime"): -# tempLength = self._commandList[-1].GetLength() -# self._commandList.pop(len(self._commandList) - 1) -# self.AddBasicCommand("MarkTime", len(self._commandList), (self._move.GetLength() - tot + tempLength), "MarkTime") -# else: -# self.AddBasicCommand("MarkTime", len(self._commandList), (self._move.GetLength() - tot), "MarkTime") - - def ReNameCommand(self, commandNumber, newName): - """Change the name of commandNumber command to newName""" - if ((not self.hold) or (self.GetPrior() is None)): - return - self._commandList[commandNumber].SetName(newName) - - def SplitCommand(self, commandNumber, count): - """Split commandNumber command at count""" - if ((not self.hold) or (self.GetPrior() is None)): - return - commandToSplit = self._commandList.pop(commandNumber) - if (commandNumber == 0): - newCommands = commandToSplit.Split(\ - count, self.GetPrior().GetEndLocation()) - else: - newCommands = commandToSplit.Split(\ - count, self._commandList[commandNumber - 1].GetEndLocation()) - self._commandList.insert(commandNumber, newCommands[1]) - self._commandList.insert(commandNumber, newCommands[0]) - - def MergeCommands(self, firstCommandNumber): - """Merge firstCommandNumber and the following command together""" - if ((not self.hold) or (self.GetPrior() is None)): - return - if ((len(self._commandList)) > firstCommandNumber + 1): - return - firstCommand = self._commandList[firstCommandNumber] - secondCommand = self._commandList[secondCommandNumber] - newCommand = firstCommand.MergeWithFollowing(secondCommand) - if (newCommand is not None): - self._commandList.pop(firstCommandNumber) - self._commandList[firstCommandNumber] = newCommand - - # TODO(astory): why do we need this? - def GetCalculatedBeginLocation(self, frontCount): - """Calculate the beginning location from the end location and the - commands""" - count = self._move.GetLength() - frontCount - tot = 0 - i = 0 - resid = 0 - while (tot < count): - curLength = self._commandList[i].GetLength() - if (curLength + tot >= count): - resid = count - tot - else: - i = i + 1 - tot = tot + curLength - curLocation = self._endLocation - curLocation = self._commandList[i].CalcBeginLocation(resid, curLocation) - i = i - 1 - while (i >= 0): - curLocation =\ - self._commandList[i].CalcBeginLocation(\ - self._commandList[i].GetLength(), curLocation) - i = i - 1 - return curLocation - - def AddSplinePoint(self, number): - """Add a spline point to the rank after point number. Does not add a - spline point after the final point""" - listOfPoints = self._endLocation.GetListOfPoints() - if (number == len(listOfPoints) - 1): - return - p = self._endLocation.GetPointAtT(0.5, number) - listOfPoints.insert(number + 1, p) - self._endLocation.SetListOfPoints(listOfPoints, None) - - def DeleteSplinePoint(self, number): - """Removes number point if it is not one of the last two""" - listOfPoints = self._endLocation.GetListOfPoints() - if (len(listOfPoints) > 2): - listOfPoints.pop(number) - self._endLocation.SetListOfPoints(listOfPoints, None) - - # TODO(astory): this is never used - def DeleteAllSplinePoints(self): - """Removes all points except the two end points""" - listOfPoints = self._endLocation.GetListOfPoints() - newList = (listOfPoints[0], listOfPoints[-1]) - self._endLocation.SetListOfPoints(newList, None) - - def SnapEndLocation(self): - """Set each command's end location and the rank's end location to as - calculated based on the command and the previous move""" - if (self.GetPrior() is None): - return - self._endLocation = self.GetCalculatedLocation(self._move.GetLength()) - self.UpdateCommandList() - - # Starting from self._endLocation, work backwards through the command list. - # Snap each command to the requisite beginning location, and snap the - # _endLocation of the rank of the prior move as well. - def SnapBeginLocation(self): - """Set each command's beginning location and the rank's end location to - as calculated based on the command and the previous move""" - prior = self.GetPrior() - if (self.GetPrior() is None): - return - prior._endLocation = self.GetCalculatedBeginLocation(0) - prior.UpdateCommandList() - - # TODO(astory): convert to wrappers to a parameterized function - def SetStraight(self): - """Set the rank and all of its commands to be non-curved""" - self._endLocation.SetCurved(False) - i = 0 - while (i < len(self._commandList)): - self._commandList[i].GetEndLocation().SetCurved(False) - i = i + 1 - - def SetCurved(self): - """Set the rank and all of its commands to be curved""" - self._endLocation.SetCurved(True) - i = 0 - while (i < len(self._commandList)): - self._commandList[i].GetEndLocation().SetCurved(True) - i = i + 1 - - # TODO(astory): rewrite as a list comprehension - def CalculateCountFromCommandNumber(self, commandNumber): - """Returns the count at which commandNumber command starts""" - if (commandNumber == 0): - return 0 - i = 0 - accum = 0 - while (i < commandNumber): - accum = accum + self._commandList[i].GetLength() - i = i + 1 - return accum - - # TODO(astory): rewrite as a list comprehension - def CalculateTotalCountsOfCommands(self): - """Return the sum of lengths of the commands""" - i = 0 - accum = 0 - while (i < len(self._commandList)): - accum = accum + self._commandList[i].GetLength() - i = i + 1 - return accum - - # TODO(astory): break into own file? - def _GenerateCommandList(self, beginLocation, endLocation, length): - """Generate a list of commands to take the rank from beginLocation to - endLocation in length counts""" - # The algorithm: - # First, check to see if this is a 'special case'. If so, even if the - # rank is curved, it can be treated as a straight line. Otherwise, - # flatten the rank. The number of counts taken is equal to the length - # of the furthest spline point from that straight line. Then, use the - # straight line generator to get form one straight line to another, and - # then recurve, if nececssary. As a final step, append a MarkTime. - # This step may be able to be skipped, as the UpdateCommandList - # function, the only thing calling this, should append it automatically - # if necessary. - if (self._IsSpecialCaseCMDGen(beginLocation, endLocation)): - return self._GenerateCommandListStraightLine(\ - beginLocation, endLocation, length, [])[0] - commandListSoFar = [] - beginPointList = beginLocation.GetListOfPoints() - endPointList = endLocation.GetListOfPoints() - if (not beginLocation.IsStraight()): - i = 1 - lengthsBeginMax = 0 - while (i < (len(beginPointList) - 1)): - valsBegin = self._CalcLengthsHelper(beginLocation, i) - lengthsBeginMax = max(lengthsBeginMax, valsBegin[0]) - i = i + 1 - newCommand = Commands.Flatten(lengthsBeginMax, beginLocation) - commandListSoFar.append(newCommand) - length = length - newCommand.GetLength() - beginLocation = newCommand.GetEndLocation() - - # TODO(astory): assign into tuple - commandListSoFarTuple =\ - self._GenerateCommandListStraightLine(\ - beginLocation,\ - endLocation,\ - length,\ - commandListSoFar) - commandListSoFar = commandListSoFarTuple[0] - length = commandListSoFarTuple[1] - beginLocation = commandListSoFarTuple[2] - - if (not endLocation.IsStraight()): - i = 1 - lengthsEndMax = 0 - while (i < (len(endPointList) - 1)): - valsEnd = self._CalcLengthsHelper(endLocation, i) - lengthsEndMax = max(lengthsEndMax, valsEnd[0]) - i = i + 1 - newCommand = Commands.Curve(lengthsEndMax, beginLocation, endLocation) - commandListSoFar.append(newCommand) - length = length - newCommand.GetLength() - beginLocation = newCommand.GetEndLocation() - - if (length > 0): - newCommand = Commands.MarkTime(length, endLocation) - commandListSoFar.append(newCommand) - length = length - newCommand.GetLength() - - return commandListSoFar - - def _IsSpecialCaseCMDGen(self, beginLocation, endLocation): - """Return true if expand, condense, GT, PW, and slides are enough to - perform the relocation""" - # The purpose of this method is to determine if a rank can be gotten - # from one RankLocation to another with just Expand, Condense, GT, PW, - # RS/LS, and FM/BM. I find the length from each spline point to the - # straight line connecting the first and last point. If these lengths - # are the same, then it is this special case and return True. - beginPointList = beginLocation.GetListOfPoints() - endPointList = endLocation.GetListOfPoints() - if ((len(beginPointList) == 2)\ - or (len(endPointList) == 2)\ - or (len(beginPointList) != len(endPointList))): - return False - i = 1 - while (i < (len(beginPointList) - 1)): - valsBegin = self._CalcLengthsHelper(beginLocation, i) - valsEnd = self._CalcLengthsHelper(endLocation, i) - if ((valsBegin[0] != valsEnd[0]) or (valsBegin[1] != valsEnd[1])): - return False - i = i + 1 - return True - - # Takes in a RankLocation and a number, namely the point number of which - # you wish to learn the length of. Assume that the RankLocation looks - # like: - # . - # / \ - # / \ - # . \ . - # \ / - # \ / - # . - - # This method should calculate the following lengths: (the added lines): - - # . - # /|\ - # / | \ - # . - \ - . - # \ | / - # \|/ - # . - - # This helps to determine if one (possibly curved) RankLocation is a - # translation of another, meaning you can get from one to another - # using just Expand, Condense, GT, PW, RS/LS, FM/BM - - # TODO(astory): should be moved to a util since it doesn't use self. - # TODO(astory): figure out what t is - def _CalcLengthsHelper(self, location, number): - """Calculates the length between the endpoints of the location""" - pointList = location.GetListOfPoints() - x0 = pointList[0].x - y0 = pointList[0].y - x1 = pointList[-1].x - y1 = pointList[-1].y - xp = pointList[number].x - yp = pointList[number].y - t = (((xp - x0)*(x1 - x0))\ - + ((yp - y0)*(y1 - y0)))/(((y1 - y0)*(y1 - y0))\ - + ((x1 - x0)*(x1 - x0))) - yt = y0 + t*(y1 - y0) - xt = x0 + t*(x1 - x0) - pointLineLength = math.sqrt(((xp - xt)*(xp - xt))\ - + ((yp - yt)*(yp - yt))) - return[pointLineLength, t] - - # Algorithm: - # If you go from straight rank to straight rank, do the following: - # Expand/Condense to be the correct length - # GT/PW to be the correct orientation - # RS/LS - # FM/BM - # Use a heuristic to figure out whether to Condense0 or Condense1, - # Same with GT/PW. - def _GenerateCommandListStraightLine(self, beginLocation, endLocation,\ - length, commandListSoFar): - begin0 = beginLocation.GetListOfPoints()[0] #(Brady) "Left" end - begin1 = beginLocation.GetListOfPoints()[-1] #(Brady) "Right" end (i think...) - end0 = endLocation.GetListOfPoints()[0] - end1 = endLocation.GetListOfPoints()[-1] - endMid = endLocation.GetMidPoint() - beginMid = beginLocation.GetMidPoint() - #LMD change to round - beginLength = int(round(math.sqrt(\ - (begin1.x - begin0.x)**2 + (begin1.y - begin0.y)**2))) - #LMD change to round - endLength = int(round(math.sqrt(\ - (end1.x - end0.x)**2 + (end1.y - end0.y)**2))) - #(Brady) Pretty sure this assumes that in usually ranks are 16 steps long (which is generally true) - if (beginLength != 16): - newMid1x = (begin1.x - begin0.x)*(8/beginLength) + begin0.x - newMid1y = (begin1.y - begin0.y)*(8/beginLength) + begin0.y - newMid0x = (begin0.x - begin1.x)*(8/beginLength) + begin1.x - newMid0y = (begin0.y - begin1.y)*(8/beginLength) + begin1.y - length0 = math.sqrt((endMid.x - newMid0x)*(endMid.x - newMid0x)\ - + (endMid.y - newMid0y)*(endMid.y - newMid0y)) - length1 = math.sqrt((endMid.x - newMid1x)*(endMid.x - newMid1x)\ - + (endMid.y - newMid1y)*(endMid.y - newMid1y)) - if (beginLength > 16): - if (length0 > length1): - newCommand = Commands.Condense1((beginLength - 16),\ - beginLocation) - else: - newCommand = Commands.Condense0((beginLength - 16),\ - beginLocation) - else: - if (length0 > length1): - newCommand = Commands.Expand1((16 - beginLength),\ - beginLocation) - else: - newCommand = Commands.Expand0((16 - beginLength),\ - beginLocation) - commandListSoFar.append(newCommand) - length = length - newCommand.GetLength() - beginLocation = newCommand.GetEndLocation() - begin0 = beginLocation.GetListOfPoints()[0] - begin1 = beginLocation.GetListOfPoints()[-1] - beginMid = beginLocation.GetMidPoint() - beginLength = 16 - - beginAng = math.atan2((begin1.y - begin0.y),(begin1.x - begin0.x)) - endAng = math.atan2((end1.y - end0.y),(end1.x - end0.x)) - if (beginAng != endAng): - c = math.cos(endAng - beginAng) - s = math.sin(endAng - beginAng) - newMid1x = (c*(beginMid.x - begin0.x) - s*(beginMid.y - begin0.y))\ - + begin0.x - newMid1y = (s*(beginMid.x - begin0.x) + c*(beginMid.y - begin0.y))\ - + begin0.y - newMid0x = (c*(beginMid.x - begin1.x) - s*(beginMid.y - begin1.y))\ - + begin1.x - newMid0y = (s*(beginMid.x - begin1.x) + c*(beginMid.y - begin1.y))\ - + begin1.y -#####################################################################LMD fix - #angDiff is CCW angle change from beginning to end angle - angDiff= endAng-beginAng - if (endAng < beginAng): angDiff=angDiff+360 - if (angDiff<180): stepsPW = angDiff*(16/math.pi) - else: stepsPW = (360-angDiff)*(16/math.pi) - - length0 = math.fabs(endMid.x - newMid0x)\ - + math.fabs(endMid.y - newMid0y) + 2*stepsPW - length1 = math.fabs(endMid.x - newMid1x)\ - + math.fabs(endMid.y - newMid1y) + 2*stepsPW - lengthPW = math.fabs(endMid.x - beginMid.x)\ - + math.fabs(endMid.y - beginMid.y) + stepsPW - - if (angDiff<180): - #CCW - if (lengthPW <= length0): - if (lengthPW <= length1): - newCommand = Commands.PWCCW(stepsPW, beginLocation) - else: - newCommand = Commands.GTCCW1(2*stepsPW, beginLocation) - else: - if (length0 <= length1): - newCommand = Commands.GTCCW0(2*stepsPW, beginLocation) - else: - newCommand = Commands.GTCCW1(2*stepsPW, beginLocation) - else: - #CW - if (lengthPW <= length0): - if (lengthPW <= length1): - newCommand = Commands.PWCW(stepsPW, beginLocation) - else: - newCommand = Commands.GTCW1(2*stepsPW, beginLocation) - else: - if (length0 <= length1): - newCommand = Commands.GTCW0(2*stepsPW, beginLocation) - else: - newCommand = Commands.GTCW1(2*stepsPW, beginLocation) -############################################################################### - commandListSoFar.append(newCommand) - length = length - newCommand.GetLength() - beginLocation = newCommand.GetEndLocation() - begin0 = beginLocation.GetListOfPoints()[0] - begin1 = beginLocation.GetListOfPoints()[-1] - beginMid = beginLocation.GetMidPoint() - - movePoint0 = True - NumSteps0 = math.fabs(end0.x - begin0.x) + math.fabs(end0.y - begin0.y) - NumSteps1 = math.fabs(end1.x - begin1.x) + math.fabs(end1.y - begin1.y) - if (NumSteps0 > NumSteps1): - movePoint0 = False - - if(movePoint0): - if (end0.x > begin0.x): - newCommand = Commands.LeftSlide(\ - end0.x - begin0.x, beginLocation) - commandListSoFar.append(newCommand) - length = length - newCommand.GetLength() - beginLocation = newCommand.GetEndLocation() - begin0 = beginLocation.GetListOfPoints()[0] - begin1 = beginLocation.GetListOfPoints()[-1] - beginMid = beginLocation.GetMidPoint() - elif (end0.x < begin0.x): - newCommand = Commands.RightSlide(\ - begin0.x - end0.x, beginLocation) - commandListSoFar.append(newCommand) - length = length - newCommand.GetLength() - beginLocation = newCommand.GetEndLocation() - begin0 = beginLocation.GetListOfPoints()[0] - begin1 = beginLocation.GetListOfPoints()[-1] - beginMid = beginLocation.GetMidPoint() - if (end0.y > begin0.y): - newCommand = Commands.BackMarch(\ - end0.y - begin0.y, beginLocation) - commandListSoFar.append(newCommand) - length = length - newCommand.GetLength() - beginLocation = newCommand.GetEndLocation() - begin0 = beginLocation.GetListOfPoints()[0] - begin1 = beginLocation.GetListOfPoints()[-1] - beginMid = beginLocation.GetMidPoint() - elif (end0.y < begin0.y): - newCommand = Commands.ForwardMarch(\ - begin0.y - end0.y, beginLocation) - commandListSoFar.append(newCommand) - length = length - newCommand.GetLength() - beginLocation = newCommand.GetEndLocation() - begin0 = beginLocation.GetListOfPoints()[0] - begin1 = beginLocation.GetListOfPoints()[-1] - beginMid = beginLocation.GetMidPoint() - else: - if (end1.x > begin1.x): - newCommand = Commands.LeftSlide(\ - end1.x - begin1.x, beginLocation) - commandListSoFar.append(newCommand) - length = length - newCommand.GetLength() - beginLocation = newCommand.GetEndLocation() - begin0 = beginLocation.GetListOfPoints()[0] - begin1 = beginLocation.GetListOfPoints()[-1] - beginMid = beginLocation.GetMidPoint() - elif (end1.x < begin1.x): - newCommand = Commands.RightSlide(\ - begin1.x - end1.x, beginLocation) - commandListSoFar.append(newCommand) - length = length - newCommand.GetLength() - beginLocation = newCommand.GetEndLocation() - begin0 = beginLocation.GetListOfPoints()[0] - begin1 = beginLocation.GetListOfPoints()[-1] - beginMid = beginLocation.GetMidPoint() - if (end1.y > begin1.y): - newCommand = Commands.BackMarch(\ - end1.y - begin1.y, beginLocation) - commandListSoFar.append(newCommand) - length = length - newCommand.GetLength() - beginLocation = newCommand.GetEndLocation() - begin0 = beginLocation.GetListOfPoints()[0] - begin1 = beginLocation.GetListOfPoints()[-1] - beginMid = beginLocation.GetMidPoint() - elif (end1.y < begin1.y): - newCommand = Commands.ForwardMarch(\ - begin1.y - end1.y, beginLocation) - commandListSoFar.append(newCommand) - length = length - newCommand.GetLength() - beginLocation = newCommand.GetEndLocation() - begin0 = beginLocation.GetListOfPoints()[0] - begin1 = beginLocation.GetListOfPoints()[-1] - beginMid = beginLocation.GetMidPoint() - - if (endLength != 16): - if (endLength < 16): - if (movePoint0): - newCommand = Commands.Condense1(\ - (16 - endLength), beginLocation) - else: - newCommand = Commands.Condense0(\ - (16 - endLength), beginLocation) - else: - if (movePoint0): - newCommand = Commands.Expand1(\ - (endLength - 16), beginLocation) - else: - newCommand = Commands.Expand0(\ - (endLength - 16), beginLocation) - commandListSoFar.append(newCommand) - length = length - newCommand.GetLength() - beginLocation = newCommand.GetEndLocation() - begin0 = beginLocation.GetListOfPoints()[0] - begin1 = beginLocation.GetListOfPoints()[-1] - beginMid = beginLocation.GetMidPoint() - beginLength = 16 - - return (commandListSoFar, length, beginLocation) - - def GetLabelLocation(self): - """Returns the location of this rank's label""" - return self._labelLocation - - def SwitchLabelLocation(self): - """Twiddle the location of this rank's label""" - self._labelLocation = not self._labelLocation - - def SwitchEndpoints(self): - """Switch the endpoints""" - self._endLocation.SwitchEndpoints() - self.UpdateCommandList() - if (self.GetFollowing() is not None): - self.GetFollowing().UpdateCommandList() +import copy +import math +import Commands +import Point +import pprint + +# matches command names to function references to call in AddBasicCommand() +commandHash = {"MarkTime": Commands.MarkTime, "MT": Commands.MarkTime, \ + "ForwardMarch":Commands.ForwardMarch, "FM":Commands.ForwardMarch, + "BackMarch":Commands.BackMarch, "BM":Commands.BackMarch, \ + "RightSlide":Commands.RightSlide, "RS":Commands.RightSlide, + "LeftSlide":Commands.LeftSlide, "LS":Commands.LeftSlide, \ + "GTCCW0":Commands.GTCCW0, "GTCW0":Commands.GTCW0, \ + "GTCCW1":Commands.GTCCW1, "GTCW1":Commands.GTCW1, \ + "PWCCW":Commands.PWCCW, "PWCW":Commands.PWCW, \ + "Expand0":Commands.Expand0, "Exp0":Commands.Expand0, \ + "Expand1":Commands.Expand1, "Exp1":Commands.Expand1,\ + "Condense0":Commands.Condense0, "Cond0":Commands.Condense0, \ + "Condense1":Commands.Condense1, "Cond1":Commands.Condense1,\ + "Flatten":Commands.Flatten, "Flat":Commands.Flatten} + +# TODO(Brady) Have an option to display (and to edit) the information of a rank +# such as length (steps), rotation (degrees hor/vert), instrument, etc. +# Also make rank labels draggable (fairly minor importance) +class Rank(object): + def __init__(self, endLocation, move): + self._endLocation = endLocation + self._id = move.GetRankIDGen().GetID() + self._name = None + self._commandList = [] + #TODO(brady): try keeping just one rank for the whole song to help + #carry changes through. Perhaps keep a basic version of this class + #for each move but make them a child of the rank for whole song. + self._move = move + # TODO(astory): why isn't this called 'locked'? + self.hold = False + self.grabbed = False + self.grabbedPoint = None + self.instrument = None + self.listOfSelectedCommandNumbers = [] + self._labelLocation = True + + + def GetName(self): + """Simply returns the name of the rank.""" + return self._name + + + def SetName(self, name): + """Sets this rank's _name field. Also, auto-generates the command list + (if applicable).""" + self._name = name + self.UpdateCommandList() + if (self.GetFollowing() is not None): + self.GetFollowing().UpdateCommandList() + + + + def GetID(self): + """Returns the rank's _id.""" + return self._id + + def GetCommandList(self): + """Returns a copy of the rank's Command List.""" + return copy.deepcopy(self._commandList) + + def SetCommandList(self, commandList): + """Set the rank's command list""" + self._commandList = commandList + + + # The next few methods are used for grabbing a rank and moving it. + # Necessary because although a rank's _endLocation will be changing as you + # drag it around (so that it can be seen to move), there's no need to + # re-generate the command list every time. + def RankGrabbed(self): + """Start grabbing a rank""" + self.grabbed = True + def RankDragged(self, dx, dy): + """Move the rank by dx and dy and update the list of points, but don't + update the command list""" + i = 0 + listOfPoints = self._endLocation.GetListOfPoints() + newPoints = [] + l = len(listOfPoints) + #Perhaps use list comprehension? + #newPoints = [Point.Point(p.x + dx, p.y +dy) for p in listOfPoints] + while (i < l): + newPoints.append(\ + Point.Point(listOfPoints[i].x + dx, listOfPoints[i].y + dy)) + i = i + 1 + self._endLocation.SetListOfPoints(\ + newPoints, self._endLocation.GetListOfSlopes()) + def RankDropped(self): + """Stop grabbing a rank and update the command list for its new + location""" + self.grabbed = False + self.UpdateCommandList() + if (self.GetFollowing() is not None): + self.GetFollowing().UpdateCommandList() + + # Similar to above. However, unless the rank is a straight line, it will + # need to be re-splined every timestep (again for drawing purposes.) + # This should be ok; the spline algorithm takes time linear in the number + # of spline points. + # TODO(astory): storing the grabbed point in Rank seems like bad design, it + # would be better to have the user of these functions store the point and + # manipulate them that way; grabbed would need to be a counter + def PointGrabbed(self, number): + """Start grabbing a (spline) point""" + self.grabbed = True + self.grabbedPoint = number + def PointDragged(self, dx, dy): + """Move a point by dx and dy, but don't update the command list for its + rank""" + listOfPoints = self._endLocation.GetListOfPoints() + listOfPoints[self.grabbedPoint] =\ + Point.Point(listOfPoints[self.grabbedPoint].x + dx,\ + listOfPoints[self.grabbedPoint].y + dy) + self._endLocation.SetListOfPoints(\ + listOfPoints, self._endLocation.GetListOfSlopes()) + def PointDropped(self): + """Stop dragging a point and update the command list for the new + location""" + self.grabbed = False + self.grabbedPoint = None + self.UpdateCommandList() + if (self.GetFollowing() is not None): + self.GetFollowing().UpdateCommandList() + + # When this is called, check to see if Rank.hold == true and if the + # prior move has a rank of the same name. If not, go ahead + # and auto-generate the command list. + def UpdateCommandList(self): + """If this rank is in the previous move, and the rank is not locked, + regenerate the command list. If it is locked, update the command list + for the new location, but don't change the commands.""" + if (self.GetPrior() is not None): + if (self.hold): + i = 0 + l = len(self._commandList) + priorLocation = self.GetPrior().GetEndLocation() + while (i < l): + self._commandList[i].SnapEndLocation(priorLocation) + priorLocation = self._commandList[i].GetEndLocation() + i = i + 1 + self.FixTrailingMT() + else: + self._commandList =\ + self._GenerateCommandList(self.GetPrior().GetEndLocation(),\ + self._endLocation, self._move.GetLength()) + else: + self._commandList = [] + + def FixTrailingMT(self): + """Merge duplicate trailing mark times, and fill in space to end of move + with a single mark time""" + # TODO(astory): this method is written poorly. Fix. + # TODO(astory): make general so that we can use whatever end-move + # command we want + l = len(self._commandList) + # if there are commands + if l > 0: + # if the last one is MT + if (self._commandList[l - 1].GetName() == 'MT'): + i = l - 1 + # go backwards until you run out of commands or it's not a MT + while ((i >= 0) and (self._commandList[i].GetName() == 'MT')): + i = i - 1 + # if you didn't reach the beginning + if (i >= 0): + # reset to the first end MT + i = i + 1 + # remove all the trailing MTs + while (len(self._commandList) > i): + self._commandList.pop() + else: # we reached the beginning + # slap on a mark time for the right length + self._commandList =\ + [Commands.MarkTime(self._move.GetLength(),\ + self._commandList[-1].GetEndLocation())] + # Add a marktime to the end to fill in the gaps + total = self.CalculateTotalCountsOfCommands() + if (total < self._move.GetLength()): + self._commandList.append(\ + Commands.MarkTime(\ + self._move.GetLength() - total,\ + self._commandList[-1].GetEndLocation())) + + def GetPrior(self): + """Returns this rank from the previous move if it exists, else, None""" + if (self._name is None): + return None + p = self._move.GetPrior() + if (p is None): + return None + return (p.LookUpName(self._name)) + + #TODO(Brady) This whole framework of matching positions to the next move seems + #wierd and buggy. try to keep locations in current move, or connect some other way? + def GetFollowing(self): + """Returns this rank from the next move if it exists, else, None""" + if (self._name is None): + return None + p = self._move.GetFollowing() + if (p is None): + return None + return (p.LookUpName(self._name)) + + + # Starting from the prior Rank's _endLocation, go through each Command in + # the Command List, generating the end location of each, until you've used + # up enough counts to hit the given count value. Note that this may not be + # the entire command list. This doesn't actually make any permanent + # changes. + + # Return None if you can't calcuclate the location (because of no + # command list) + def GetCalculatedLocation(self, count): + """Returns this rank's location at count count of the move, which may + not be the end location if count < the number of counts of commands""" + priorRank = self.GetPrior() + if (priorRank is None): + return self.GetEndLocation() + # TODO(astory): this should be condensible, do so after writing tests + if (len(self._commandList) == 0): + return self.GetEndLocation() + if (count > self.CalculateTotalCountsOfCommands()): + return self.GetEndLocation() + curLocation = priorRank.GetEndLocation() + i = 0 + countsLeft = count + while (countsLeft > 0): + curCommand = self._commandList[i] + # TODO(astory): consider speeding up by adding whole length of + # command if it fits in our count total + if (countsLeft >= curCommand.GetLength()): + curLocation = curCommand.CalcLocation(curCommand.GetLength(), curLocation) + else: + curLocation = curCommand.CalcLocation(countsLeft, curLocation) + countsLeft = countsLeft - curCommand.GetLength() + i = i + 1 + return curLocation + + def GetEndLocation(self): + """Returns the rank's final location""" + return copy.deepcopy(self._endLocation) + + # TODO(astory): this might be superfluous since GetCalculatedLocation never + # returns none AFAICT + def GetLocationAtCount(self, count): + """Returns the rank's location at the given count""" + loc = self.GetCalculatedLocation(count) + if (loc is None): + loc = self.GetEndLocation() + return loc + + + # TODO(astory): clean + # Used when a rank is moved around. If self.hold == false and + # self.grabbed == false, re-generate the command list. + + # Should never be called! +# def SetEndLocation(self, newEndLocation): +# pass + + + + # Adds the given command to the command list in the desired location + # Number should be the number in the command list that the new command + # will occupy. So, if you're inserting before the fourth command, + # number should be 3 (because it's 0-indexed.) + def AddBasicCommand(self, commandName, number, length, name): + """Insert a command of type commandName at index number of the command + list, for length counts with name name, but only if the rank is locked, + and this rank is represented in a previous command""" + if ((not self.hold) or (self.GetPrior() is None)): + return + if (number == 0): + beginLocation = self.GetPrior().GetEndLocation() + else: + beginLocation = self._commandList[number - 1].GetEndLocation() + try: + newCommand = commandHash[commandName](length, beginLocation) + except KeyError: + # assumes command line is open. Rank doesn't have access to the GUI + # to make a dialog box. + print("Unknown command: " + str(commandName)) + + self._commandList.insert(number, newCommand) + if (name is not None): + newCommand.SetName(name) + self.UpdateCommandList() + + # TODO(astory): merge into the previous with optional arguments + def AddDTP(self, number, length, name, endLocation): + """Add a DTP, which requires an end location as well""" + if ((not self.hold) or (self.GetPrior() is None)): + return + if (number == 0): + beginLocation = self.GetPrior().GetEndLocation() + else: + beginLocation = self._commandList[number - 1].GetEndLocation() + newCommand = Commands.DTP(length, beginLocation, endLocation) + self._commandList.insert(number, newCommand) + if (name is not None): + newCommand.SetName(name) + self.UpdateCommandList() + + # TODO(astory): merge into the previous with optional arguments + def AddCurve(self, number, length, name, endLocation): + """Add a curve, which requires an end location""" + if ((not self.hold) or (self.GetPrior() is None)): + return + if (number == 0): + beginLocation = self.GetPrior().GetEndLocation() + else: + beginLocation = self._commandList[number - 1].GetEndLocation() + newCommand = Commands.Curve(length, beginLocation, endLocation) + self._commandList.insert(number, newCommand) + if (name is not None): + newCommand.SetName(name) + self.UpdateCommandList() + + # TODO(astory): merge into the previous with optional arguments + def AddFTA1(self, number, length, name, endLocation, listOfFTAPoints): + """Add an FTA at 1""" + if ((not self.hold) or (self.GetPrior() is None)): + return + if (number == 0): + beginLocation = self.GetPrior().GetEndLocation() + else: + beginLocation = self._commandList[number - 1].GetEndLocation() + newCommand = Commands.FTA1(length, beginLocation, endLocation, listOfFTAPoints) + self._commandList.insert(number, newCommand) + if (name is not None): + newCommand.SetName(name) + self.UpdateCommandList() + + # TODO(astory): merge into the previous with optional arguments + def AddFTA0(self, number, length, name, endLocation, listOfFTAPoints): + """Add an FTA at 0""" + if ((not self.hold) or (self.GetPrior() is None)): + return + if (number == 0): + beginLocation = self.GetPrior().GetEndLocation() + else: + beginLocation = self._commandList[number - 1].GetEndLocation() + newCommand = Commands.FTA0(length, beginLocation, endLocation, listOfFTAPoints) + self._commandList.insert(number, newCommand) + if (name is not None): + newCommand.SetName(name) + self.UpdateCommandList() + + def MoveCommandUp(self, commandNumber): + """Move commandNumber command up the command list""" + if ((not self.hold) or (self.GetPrior() is None) or (commandNumber == 0)): + return + command = self._commandList.pop(commandNumber) + self._commandList.insert(commandNumber - 1, command) + self.UpdateCommandList() + + def MoveCommandDown(self, commandNumber): + """Move commandNumber command down the command list""" + if ((not self.hold) or (self.GetPrior() is None)\ + or (commandNumber == (len(self._commandList) - 1))): + return + command = self._commandList.pop(commandNumber) + self._commandList.insert(commandNumber + 1, command) + self.UpdateCommandList() + + def DeleteCommand(self, commandNumber): + """Remove commandNumber command""" + if ((not self.hold) or (self.GetPrior() is None)): + return + self._commandList.pop(commandNumber) + tot = 0 + i = 0 + while (i < len(self._commandList)): + tot = tot + self._commandList[i].GetLength() + i = i + 1 + self.UpdateCommandList() + # TODO(astory): clean up +# if (tot < self._move.GetLength()): +# if (self._commandList[-1].GetName() == "MarkTime"): +# tempLength = self._commandList[-1].GetLength() +# self._commandList.pop(len(self._commandList) - 1) +# self.AddBasicCommand("MarkTime", len(self._commandList), (self._move.GetLength() - tot + tempLength), "MarkTime") +# else: +# self.AddBasicCommand("MarkTime", len(self._commandList), (self._move.GetLength() - tot), "MarkTime") + + def ReNameCommand(self, commandNumber, newName): + """Change the name of commandNumber command to newName""" + if ((not self.hold) or (self.GetPrior() is None)): + return + self._commandList[commandNumber].SetName(newName) + + def SplitCommand(self, commandNumber, count): + """Split commandNumber command at count""" + if ((not self.hold) or (self.GetPrior() is None)): + return + commandToSplit = self._commandList.pop(commandNumber) + if (commandNumber == 0): + newCommands = commandToSplit.Split(\ + count, self.GetPrior().GetEndLocation()) + else: + newCommands = commandToSplit.Split(\ + count, self._commandList[commandNumber - 1].GetEndLocation()) + self._commandList.insert(commandNumber, newCommands[1]) + self._commandList.insert(commandNumber, newCommands[0]) + + def MergeCommands(self, firstCommandNumber): + """Merge firstCommandNumber and the following command together""" + if ((not self.hold) or (self.GetPrior() is None)): + return + if ((len(self._commandList)) > firstCommandNumber + 1): + return + firstCommand = self._commandList[firstCommandNumber] + secondCommand = self._commandList[secondCommandNumber] + newCommand = firstCommand.MergeWithFollowing(secondCommand) + if (newCommand is not None): + self._commandList.pop(firstCommandNumber) + self._commandList[firstCommandNumber] = newCommand + + # TODO(astory): why do we need this? + def GetCalculatedBeginLocation(self, frontCount): + """Calculate the beginning location from the end location and the + commands""" + count = self._move.GetLength() - frontCount + tot = 0 + i = 0 + resid = 0 + while (tot < count): + curLength = self._commandList[i].GetLength() + if (curLength + tot >= count): + resid = count - tot + else: + i = i + 1 + tot = tot + curLength + curLocation = self._endLocation + curLocation = self._commandList[i].CalcBeginLocation(resid, curLocation) + i = i - 1 + while (i >= 0): + curLocation =\ + self._commandList[i].CalcBeginLocation(\ + self._commandList[i].GetLength(), curLocation) + i = i - 1 + return curLocation + + def AddSplinePoint(self, number): + """Add a spline point to the rank after point number. Does not add a + spline point after the final point""" + listOfPoints = self._endLocation.GetListOfPoints() + if (number == len(listOfPoints) - 1): + return + p = self._endLocation.GetPointAtT(0.5, number) + listOfPoints.insert(number + 1, p) + self._endLocation.SetListOfPoints(listOfPoints, None) + + def DeleteSplinePoint(self, number): + """Removes number point if it is not one of the last two""" + listOfPoints = self._endLocation.GetListOfPoints() + if (len(listOfPoints) > 2): + listOfPoints.pop(number) + self._endLocation.SetListOfPoints(listOfPoints, None) + + # TODO(astory): this is never used + def DeleteAllSplinePoints(self): + """Removes all points except the two end points""" + listOfPoints = self._endLocation.GetListOfPoints() + newList = (listOfPoints[0], listOfPoints[-1]) + self._endLocation.SetListOfPoints(newList, None) + + def SnapEndLocation(self): + """Set each command's end location and the rank's end location to as + calculated based on the command and the previous move""" + if (self.GetPrior() is None): + return + self._endLocation = self.GetCalculatedLocation(self._move.GetLength()) + self.UpdateCommandList() + + # Starting from self._endLocation, work backwards through the command list. + # Snap each command to the requisite beginning location, and snap the + # _endLocation of the rank of the prior move as well. + def SnapBeginLocation(self): + """Set each command's beginning location and the rank's end location to + as calculated based on the command and the previous move""" + prior = self.GetPrior() + if (self.GetPrior() is None): + return + prior._endLocation = self.GetCalculatedBeginLocation(0) + prior.UpdateCommandList() + + # TODO(astory): convert to wrappers to a parameterized function + def SetStraight(self): + """Set the rank and all of its commands to be non-curved""" + self._endLocation.SetCurved(False) + i = 0 + while (i < len(self._commandList)): + self._commandList[i].GetEndLocation().SetCurved(False) + i = i + 1 + + def SetCurved(self): + """Set the rank and all of its commands to be curved""" + self._endLocation.SetCurved(True) + i = 0 + while (i < len(self._commandList)): + self._commandList[i].GetEndLocation().SetCurved(True) + i = i + 1 + + # TODO(astory): rewrite as a list comprehension + def CalculateCountFromCommandNumber(self, commandNumber): + """Returns the count at which commandNumber command starts""" + if (commandNumber == 0): + return 0 + i = 0 + accum = 0 + while (i < commandNumber): + accum = accum + self._commandList[i].GetLength() + i = i + 1 + return accum + + # TODO(astory): rewrite as a list comprehension + def CalculateTotalCountsOfCommands(self): + """Return the sum of lengths of the commands""" + i = 0 + accum = 0 + while (i < len(self._commandList)): + accum = accum + self._commandList[i].GetLength() + i = i + 1 + return accum + + # TODO(astory): break into own file? + def _GenerateCommandList(self, beginLocation, endLocation, length): + """Generate a list of commands to take the rank from beginLocation to + endLocation in length counts""" + # The algorithm: + # First, check to see if this is a 'special case'. If so, even if the + # rank is curved, it can be treated as a straight line. Otherwise, + # flatten the rank. The number of counts taken is equal to the length + # of the furthest spline point from that straight line. Then, use the + # straight line generator to get form one straight line to another, and + # then recurve, if nececssary. As a final step, append a MarkTime. + # This step may be able to be skipped, as the UpdateCommandList + # function, the only thing calling this, should append it automatically + # if necessary. + if (self._IsSpecialCaseCMDGen(beginLocation, endLocation)): + return self._GenerateCommandListStraightLine(\ + beginLocation, endLocation, length, [])[0] + commandListSoFar = [] + beginPointList = beginLocation.GetListOfPoints() + endPointList = endLocation.GetListOfPoints() + if (not beginLocation.IsStraight()): + i = 1 + lengthsBeginMax = 0 + while (i < (len(beginPointList) - 1)): + valsBegin = self._CalcLengthsHelper(beginLocation, i) + lengthsBeginMax = max(lengthsBeginMax, valsBegin[0]) + i = i + 1 + newCommand = Commands.Flatten(lengthsBeginMax, beginLocation) + commandListSoFar.append(newCommand) + length = length - newCommand.GetLength() + beginLocation = newCommand.GetEndLocation() + + # TODO(astory): assign into tuple + commandListSoFarTuple =\ + self._GenerateCommandListStraightLine(\ + beginLocation,\ + endLocation,\ + length,\ + commandListSoFar) + commandListSoFar = commandListSoFarTuple[0] + length = commandListSoFarTuple[1] + beginLocation = commandListSoFarTuple[2] + + if (not endLocation.IsStraight()): + i = 1 + lengthsEndMax = 0 + while (i < (len(endPointList) - 1)): + valsEnd = self._CalcLengthsHelper(endLocation, i) + lengthsEndMax = max(lengthsEndMax, valsEnd[0]) + i = i + 1 + newCommand = Commands.Curve(lengthsEndMax, beginLocation, endLocation) + commandListSoFar.append(newCommand) + length = length - newCommand.GetLength() + beginLocation = newCommand.GetEndLocation() + + if (length > 0): + newCommand = Commands.MarkTime(length, endLocation) + commandListSoFar.append(newCommand) + length = length - newCommand.GetLength() + + return commandListSoFar + + def _IsSpecialCaseCMDGen(self, beginLocation, endLocation): + """Return true if expand, condense, GT, PW, and slides are enough to + perform the relocation""" + # The purpose of this method is to determine if a rank can be gotten + # from one RankLocation to another with just Expand, Condense, GT, PW, + # RS/LS, and FM/BM. I find the length from each spline point to the + # straight line connecting the first and last point. If these lengths + # are the same, then it is this special case and return True. + beginPointList = beginLocation.GetListOfPoints() + endPointList = endLocation.GetListOfPoints() + if ((len(beginPointList) == 2)\ + or (len(endPointList) == 2)\ + or (len(beginPointList) != len(endPointList))): + return False + i = 1 + while (i < (len(beginPointList) - 1)): + valsBegin = self._CalcLengthsHelper(beginLocation, i) + valsEnd = self._CalcLengthsHelper(endLocation, i) + if ((valsBegin[0] != valsEnd[0]) or (valsBegin[1] != valsEnd[1])): + return False + i = i + 1 + return True + + # Takes in a RankLocation and a number, namely the point number of which + # you wish to learn the length of. Assume that the RankLocation looks + # like: + # . + # / \ + # / \ + # . \ . + # \ / + # \ / + # . + + # This method should calculate the following lengths: (the added lines): + + # . + # /|\ + # / | \ + # . - \ - . + # \ | / + # \|/ + # . + + # This helps to determine if one (possibly curved) RankLocation is a + # translation of another, meaning you can get from one to another + # using just Expand, Condense, GT, PW, RS/LS, FM/BM + + # TODO(astory): should be moved to a util since it doesn't use self. + # TODO(astory): figure out what t is + def _CalcLengthsHelper(self, location, number): + """Calculates the length between the endpoints of the location""" + pointList = location.GetListOfPoints() + x0 = pointList[0].x + y0 = pointList[0].y + x1 = pointList[-1].x + y1 = pointList[-1].y + xp = pointList[number].x + yp = pointList[number].y + t = (((xp - x0)*(x1 - x0))\ + + ((yp - y0)*(y1 - y0)))/(((y1 - y0)*(y1 - y0))\ + + ((x1 - x0)*(x1 - x0))) + yt = y0 + t*(y1 - y0) + xt = x0 + t*(x1 - x0) + pointLineLength = math.sqrt(((xp - xt)*(xp - xt))\ + + ((yp - yt)*(yp - yt))) + return[pointLineLength, t] + + # Algorithm: + # If you go from straight rank to straight rank, do the following: + # Expand/Condense to be the correct length + # GT/PW to be the correct orientation + # RS/LS + # FM/BM + # Use a heuristic to figure out whether to Condense0 or Condense1, + # Same with GT/PW. + def _GenerateCommandListStraightLine(self, beginLocation, endLocation,\ + length, commandListSoFar): + begin0 = beginLocation.GetListOfPoints()[0] #(Brady) "Left" end + begin1 = beginLocation.GetListOfPoints()[-1] #(Brady) "Right" end (i think...) + end0 = endLocation.GetListOfPoints()[0] + end1 = endLocation.GetListOfPoints()[-1] + endMid = endLocation.GetMidPoint() + beginMid = beginLocation.GetMidPoint() + #LMD change to round + beginLength = int(round(math.sqrt(\ + (begin1.x - begin0.x)**2 + (begin1.y - begin0.y)**2))) + #LMD change to round + endLength = int(round(math.sqrt(\ + (end1.x - end0.x)**2 + (end1.y - end0.y)**2))) + #(Brady) Pretty sure this assumes that in usually ranks are 16 steps long (which is generally true) + #however should allow for nonstandard lengths + if (beginLength != 16): + newMid1x = (begin1.x - begin0.x)*(8/beginLength) + begin0.x + newMid1y = (begin1.y - begin0.y)*(8/beginLength) + begin0.y + newMid0x = (begin0.x - begin1.x)*(8/beginLength) + begin1.x + newMid0y = (begin0.y - begin1.y)*(8/beginLength) + begin1.y + length0 = math.sqrt((endMid.x - newMid0x)*(endMid.x - newMid0x)\ + + (endMid.y - newMid0y)*(endMid.y - newMid0y)) + length1 = math.sqrt((endMid.x - newMid1x)*(endMid.x - newMid1x)\ + + (endMid.y - newMid1y)*(endMid.y - newMid1y)) + if (beginLength > 16): + if (length0 > length1): + newCommand = Commands.Condense1((beginLength - 16),\ + beginLocation) + else: + newCommand = Commands.Condense0((beginLength - 16),\ + beginLocation) + else: + if (length0 > length1): + newCommand = Commands.Expand1((16 - beginLength),\ + beginLocation) + else: + newCommand = Commands.Expand0((16 - beginLength),\ + beginLocation) + commandListSoFar.append(newCommand) + length = length - newCommand.GetLength() + beginLocation = newCommand.GetEndLocation() + begin0 = beginLocation.GetListOfPoints()[0] + begin1 = beginLocation.GetListOfPoints()[-1] + beginMid = beginLocation.GetMidPoint() + beginLength = 16 + + beginAng = math.atan2((begin1.y - begin0.y),(begin1.x - begin0.x)) + endAng = math.atan2((end1.y - end0.y),(end1.x - end0.x)) + if (beginAng != endAng): + c = math.cos(endAng - beginAng) + s = math.sin(endAng - beginAng) + newMid1x = (c*(beginMid.x - begin0.x) - s*(beginMid.y - begin0.y))\ + + begin0.x + newMid1y = (s*(beginMid.x - begin0.x) + c*(beginMid.y - begin0.y))\ + + begin0.y + newMid0x = (c*(beginMid.x - begin1.x) - s*(beginMid.y - begin1.y))\ + + begin1.x + newMid0y = (s*(beginMid.x - begin1.x) + c*(beginMid.y - begin1.y))\ + + begin1.y +#####################################################################LMD fix + #angDiff is CCW angle change from beginning to end angle + angDiff= endAng-beginAng + if (endAng < beginAng): angDiff=angDiff+360 + if (angDiff<180): stepsPW = angDiff*(16/math.pi) + else: stepsPW = (360-angDiff)*(16/math.pi) + + length0 = math.fabs(endMid.x - newMid0x)\ + + math.fabs(endMid.y - newMid0y) + 2*stepsPW + length1 = math.fabs(endMid.x - newMid1x)\ + + math.fabs(endMid.y - newMid1y) + 2*stepsPW + lengthPW = math.fabs(endMid.x - beginMid.x)\ + + math.fabs(endMid.y - beginMid.y) + stepsPW + + if (angDiff<180): + #CCW + if (lengthPW <= length0): + if (lengthPW <= length1): + newCommand = Commands.PWCCW(stepsPW, beginLocation) + else: + newCommand = Commands.GTCCW1(2*stepsPW, beginLocation) + else: + if (length0 <= length1): + newCommand = Commands.GTCCW0(2*stepsPW, beginLocation) + else: + newCommand = Commands.GTCCW1(2*stepsPW, beginLocation) + else: + #CW + if (lengthPW <= length0): + if (lengthPW <= length1): + newCommand = Commands.PWCW(stepsPW, beginLocation) + else: + newCommand = Commands.GTCW1(2*stepsPW, beginLocation) + else: + if (length0 <= length1): + newCommand = Commands.GTCW0(2*stepsPW, beginLocation) + else: + newCommand = Commands.GTCW1(2*stepsPW, beginLocation) +############################################################################### + commandListSoFar.append(newCommand) + length = length - newCommand.GetLength() + beginLocation = newCommand.GetEndLocation() + begin0 = beginLocation.GetListOfPoints()[0] + begin1 = beginLocation.GetListOfPoints()[-1] + beginMid = beginLocation.GetMidPoint() + + movePoint0 = True + NumSteps0 = math.fabs(end0.x - begin0.x) + math.fabs(end0.y - begin0.y) + NumSteps1 = math.fabs(end1.x - begin1.x) + math.fabs(end1.y - begin1.y) + if (NumSteps0 > NumSteps1): + movePoint0 = False + + if(movePoint0): + if (end0.x > begin0.x): + newCommand = Commands.LeftSlide(\ + end0.x - begin0.x, beginLocation) + commandListSoFar.append(newCommand) + length = length - newCommand.GetLength() + beginLocation = newCommand.GetEndLocation() + begin0 = beginLocation.GetListOfPoints()[0] + begin1 = beginLocation.GetListOfPoints()[-1] + beginMid = beginLocation.GetMidPoint() + elif (end0.x < begin0.x): + newCommand = Commands.RightSlide(\ + begin0.x - end0.x, beginLocation) + commandListSoFar.append(newCommand) + length = length - newCommand.GetLength() + beginLocation = newCommand.GetEndLocation() + begin0 = beginLocation.GetListOfPoints()[0] + begin1 = beginLocation.GetListOfPoints()[-1] + beginMid = beginLocation.GetMidPoint() + if (end0.y > begin0.y): + newCommand = Commands.BackMarch(\ + end0.y - begin0.y, beginLocation) + commandListSoFar.append(newCommand) + length = length - newCommand.GetLength() + beginLocation = newCommand.GetEndLocation() + begin0 = beginLocation.GetListOfPoints()[0] + begin1 = beginLocation.GetListOfPoints()[-1] + beginMid = beginLocation.GetMidPoint() + elif (end0.y < begin0.y): + newCommand = Commands.ForwardMarch(\ + begin0.y - end0.y, beginLocation) + commandListSoFar.append(newCommand) + length = length - newCommand.GetLength() + beginLocation = newCommand.GetEndLocation() + begin0 = beginLocation.GetListOfPoints()[0] + begin1 = beginLocation.GetListOfPoints()[-1] + beginMid = beginLocation.GetMidPoint() + else: + if (end1.x > begin1.x): + newCommand = Commands.LeftSlide(\ + end1.x - begin1.x, beginLocation) + commandListSoFar.append(newCommand) + length = length - newCommand.GetLength() + beginLocation = newCommand.GetEndLocation() + begin0 = beginLocation.GetListOfPoints()[0] + begin1 = beginLocation.GetListOfPoints()[-1] + beginMid = beginLocation.GetMidPoint() + elif (end1.x < begin1.x): + newCommand = Commands.RightSlide(\ + begin1.x - end1.x, beginLocation) + commandListSoFar.append(newCommand) + length = length - newCommand.GetLength() + beginLocation = newCommand.GetEndLocation() + begin0 = beginLocation.GetListOfPoints()[0] + begin1 = beginLocation.GetListOfPoints()[-1] + beginMid = beginLocation.GetMidPoint() + if (end1.y > begin1.y): + newCommand = Commands.BackMarch(\ + end1.y - begin1.y, beginLocation) + commandListSoFar.append(newCommand) + length = length - newCommand.GetLength() + beginLocation = newCommand.GetEndLocation() + begin0 = beginLocation.GetListOfPoints()[0] + begin1 = beginLocation.GetListOfPoints()[-1] + beginMid = beginLocation.GetMidPoint() + elif (end1.y < begin1.y): + newCommand = Commands.ForwardMarch(\ + begin1.y - end1.y, beginLocation) + commandListSoFar.append(newCommand) + length = length - newCommand.GetLength() + beginLocation = newCommand.GetEndLocation() + begin0 = beginLocation.GetListOfPoints()[0] + begin1 = beginLocation.GetListOfPoints()[-1] + beginMid = beginLocation.GetMidPoint() + + if (endLength != 16): + if (endLength < 16): + if (movePoint0): + newCommand = Commands.Condense1(\ + (16 - endLength), beginLocation) + else: + newCommand = Commands.Condense0(\ + (16 - endLength), beginLocation) + else: + if (movePoint0): + newCommand = Commands.Expand1(\ + (endLength - 16), beginLocation) + else: + newCommand = Commands.Expand0(\ + (endLength - 16), beginLocation) + commandListSoFar.append(newCommand) + length = length - newCommand.GetLength() + beginLocation = newCommand.GetEndLocation() + begin0 = beginLocation.GetListOfPoints()[0] + begin1 = beginLocation.GetListOfPoints()[-1] + beginMid = beginLocation.GetMidPoint() + beginLength = 16 + + return (commandListSoFar, length, beginLocation) + + def GetLabelLocation(self): + """Returns the location of this rank's label""" + return self._labelLocation + + def SwitchLabelLocation(self): + """Twiddle the location of this rank's label""" + self._labelLocation = not self._labelLocation + + def SwitchEndpoints(self): + """Switch the endpoints""" + self._endLocation.SwitchEndpoints() + self.UpdateCommandList() + if (self.GetFollowing() is not None): + self.GetFollowing().UpdateCommandList() diff --git a/RankPanda/RankIDGen.py b/RankPanda/RankIDGen.py index 9fa2dd8..354d0a0 100755 --- a/RankPanda/RankIDGen.py +++ b/RankPanda/RankIDGen.py @@ -1,11 +1,11 @@ - - -class RankIDGen(object): - - def __init__(self): - self.nextID = 0 - - def GetID(self): - self.nextID = self.nextID + 1 - return self.nextID - + + +class RankIDGen(object): + + def __init__(self): + self.nextID = 0 + + def GetID(self): + self.nextID = self.nextID + 1 + return self.nextID + diff --git a/RankPanda/RankLocation.py b/RankPanda/RankLocation.py index 63ef584..c1467fb 100755 --- a/RankPanda/RankLocation.py +++ b/RankPanda/RankLocation.py @@ -1,267 +1,265 @@ -import copy -import Point -import CubicHermiteSpline as CHS -import RankLocation -import Commands -import math - -class RankLocationError(Exception): - """Base class for exceptions in this module""" -class InvalidLocationListError(RankLocationError): - """Exception raised for invalid location lists""" - pass - -def Compare(l1, l2): - """Figure out if two RankLocations represent the same location on the field. - - Simply compare each point of one to each point of the other. - If the slopes are different, it'll still return true. - Slopes should almost always be auto generated; which means that for all - practical purposes this won't affect anything. - """ - if (len(l1._listOfPoints) != len(l2._listOfPoints)): - return False - for (p1, p2) in zip(l1._listOfPoints, l2._listOfPoints): - if p1 != p2: - return False - return True - -def IsListOfPointsLengthZero(pointList): - """Returns true if all the points are on top of each other and the rank - is of length 0. - """ - if not pointList: - return True - p0 = pointList[0] - for p in pointList: - if p != p0: - return False - return True - -class RankLocation(object): - """Class to represent the location of a rank. - - This class represents the location of a rank that can be drawn on the - screen. In part, it serves as an object-oriented wrapper for the - SplineGenerator class. However, it also supports straight-line ranks (only - two points) and ranks that aren't curved/splined (think a zigzag). - """ - - def __init__(self, listOfPoints, curved=True): - """Generate a rank location based on a list of points - - Pass in your list of points upon creation. It'll auto-generate the - slopes; if you'd like to artificially set the slopes list you can do it - later with the SetListOfPoints() function. - """ - self.curved = curved - self._drawingPoints = [] - self._splineFunctions = None - self._listOfSlopes = None - self.SetListOfPoints(listOfPoints, self._listOfSlopes) - - - def SetCurved(self, val): - """Sets if the rank is curved or zigzagged.""" - self.curved = val - self.SetListOfPoints(self._listOfPoints, self._listOfSlopes) - - def GetListOfPoints(self): - return copy.deepcopy(self._listOfPoints) - - def GetListOfDrawingPoints(self): - return self._drawingPoints - - def GetListOfSlopes(self): - return copy.deepcopy(self._listOfSlopes) - - def SetListOfPoints(self, listOfPoints, listOfSlopes): - """Sets the list of points and list of slopes. - - First, we determine if it's a straight line or not, meaning only two - points. - - If None was passed in for the slopes, we generate a slopelist consisting - of Nones, because this is the interface expected by the SplineGenerator - class. - - We then just get the spline functions and drawing points. - - If it's a straight line or a zig-zag, just store the list of points. - The straight lines will be drawn on the GUI end. - """ - if (len(listOfPoints) < 2): - raise InvalidLocationListError("Tried to set a list of %d points" - % len(listOfPoints)) - self._listOfPoints = listOfPoints - - if ((self.curved) and (not self.IsStraight())): - if (listOfSlopes is None): - self._listOfSlopes = [None for _ in listOfPoints] - else: - self._listOfSlopes = listOfSlopes - - self._splineFunctions = \ - CHS.GetSplines(self._listOfPoints, - self._listOfSlopes) - self._drawingPoints = \ - CHS.GetPoints(self._splineFunctions) - else: - self._listOfSlopes = None - self._splineFunctions = None - self._drawingPoints = None - - def IsStraight(self): - """Determine whether the rank is straight: iff there are two points.""" - # TODO(astory): make this be based on collinearity - return len(self._listOfPoints) == 2 - - def Clone(self): - return copy.deepcopy(self) - - def CompareRankLocation(self, l2): - return Compare(self, l2) - - def GetMidPoint(self): - """The midpoint of a line connecting the first and last points.""" - p0 = self._listOfPoints[0] - p1 = self._listOfPoints[len(self._listOfPoints) - 1] - xmid = (p0.x + p1.x)/2 - ymid = (p0.y + p1.y)/2 - return Point.Point(xmid, ymid) - - def _Respline(self): - self._splineFunctions = \ - CHS.GetSplines(self._listOfPoints, - self._listOfSlopes) - - def GetPointAtT(self, t, number): - """Get the number-th point in the rank's location at time t - - A straightforward function - takes in a t value, and a number in the - spline list. Depending on what kind of RankLocation it is, finds the - (x,y) point at that t value. - """ - if (self.IsStraight()): - x = self._listOfPoints[0].x + t*(self._listOfPoints[1].x - self._listOfPoints[0].x) - y = self._listOfPoints[0].y + t*(self._listOfPoints[1].y - self._listOfPoints[0].y) - return Point.Point(x,y) - elif (not self.curved): - pfirst = self._listOfPoints[number] - psecond = self._listOfPoints[number + 1] - x = pfirst.x + t*(psecond.x - pfirst.x) - y = pfirst.y + t*(psecond.y - pfirst.y) - return Point.Point(x,y) - else: - x = CHS.EvalCubic(t, self._splineFunctions[number][0]) - y = CHS.EvalCubic(t, self._splineFunctions[number][1]) - return Point.Point(x,y) - - - def GetInformationAtLengthFraction(self, lengthFraction): - """Return information at a fraction of the length of the rank - - Pass in a fraction of the total length at which you wish to get - information. - If the rank is curved, simply call the equivalent function in the - SplineGenerator. - If not, do basically the same thing that that function does, only it's - easier because everything's a straight line. Find between which two - points the length fraction lies, and then find how far along is needed - to get the requisite length. From there, get x, y, dx, and dy. Return - the same thing that the equivalent function in the SplineGenerator does: - [(x,y), (dx,dy), i]. - """ - - if (not self.IsStraight()): - if (self.curved): - return CHS.GetInformationAtLengthFraction(self._splineFunctions, lengthFraction) - - lengths = self.GetLengths() - totalLength = sum(lengths) - lengthNeeded = lengthFraction * totalLength - - i = 0 - if (lengthNeeded > 0.1): - while (lengthNeeded > 0.1): - lengthNeeded = lengthNeeded - lengths[i] - i = i + 1 - i = i - 1 - lengthNeeded = lengthNeeded + lengths[i] - if (lengthNeeded <= 0.1): - t = 0 - else: - t = lengthNeeded/float(lengths[i]) - x = t*(self._listOfPoints[i + 1].x - self._listOfPoints[i].x) + self._listOfPoints[i].x #SplineGenerator.EvalCubic(t, splineList[i][0]) - y = t*(self._listOfPoints[i + 1].y - self._listOfPoints[i].y) + self._listOfPoints[i].y - dx = (self._listOfPoints[i + 1].x - self._listOfPoints[i].x) - dy = (self._listOfPoints[i + 1].y - self._listOfPoints[i].y) - return [(Point.Point(x, y)),(Point.Point(dx, dy)), i] - - def GetLengthFractions(self): - """Returns the lengths fractions at each point. - - The first point lies at lelngth fraction 0, and the last at length - fraction 1. If a point is exactly in the middle, it's at fraction 0.5. - """ - fracs = [] - lengths = self.GetLengths() - lengthtot = sum(lengths) - i = 0 - fracs.append(0.0) - while (i < len(lengths)): - fracs.append(fracs[i] + lengths[i]/float(lengthtot)) - i = i + 1 - return fracs - - def GetLengths(self): - """Returns the length of each part of the RankLocation. - - Behaves differently depending on RankLocation type. If it's curved, call - the equivalent function in the SplineGenerator. - If not, use the Pythogoran Theorem to find out how long each part is. - """ - if (not self.IsStraight()): - if (self.curved): - return CHS.GetLengths( - self._splineFunctions) - i = 1 - lengths = [] - while (i < len(self._listOfPoints)): - lengths.append( - math.sqrt( - (self._listOfPoints[i - 1].x - self._listOfPoints[i].x) * - (self._listOfPoints[i - 1].x - self._listOfPoints[i].x) + - (self._listOfPoints[i - 1].y - self._listOfPoints[i].y) * - (self._listOfPoints[i - 1].y - self._listOfPoints[i].y))) - i += 1 - return lengths - - - def IsTranslated(self, l2): - """ If the input is a translated version of self, returns the amount - it's been translated by as a Point. Returns input - self. Else, - returns None. - """ - myList = self._listOfPoints - otherList = l2.GetListOfPoints() - if (len(myList) != len(otherList)): - return None - else: - same = True - dx = otherList[0].x - myList[0].x - dy = otherList[0].y - myList[0].y - i = 1 - while ((i < len(myList)) and same): - same = not (((otherList[i].x - myList[i].x) == dx) and ((otherList[i].y - myList[i].y) == dy)) - i = i + 1 - if (same): - return Point.Point(dx, dy) - else: - return None - - def SwitchEndpoints(self): - """Switch the endpoints by reversing the entire list""" - self._listOfPoints.reverse() - self.SetListOfPoints(self._listOfPoints, self._listOfSlopes) - +import copy +import Point +import CubicHermiteSpline as CHS +import RankLocation +import Commands +import math + +class RankLocationError(Exception): + """Base class for exceptions in this module""" +class InvalidLocationListError(RankLocationError): + """Exception raised for invalid location lists""" + pass + +def Compare(l1, l2): + """Figure out if two RankLocations represent the same location on the field. + + Simply compare each point of one to each point of the other. + If the slopes are different, it'll still return true. + Slopes should almost always be auto generated; which means that for all + practical purposes this won't affect anything. + """ + if (len(l1._listOfPoints) != len(l2._listOfPoints)): + return False + for (p1, p2) in zip(l1._listOfPoints, l2._listOfPoints): + if p1 != p2: + return False + return True + +def IsListOfPointsLengthZero(pointList): + """Returns true if all the points are on top of each other and the rank + is of length 0. + """ + if not pointList: + return True + p0 = pointList[0] + for p in pointList: + if p != p0: + return False + return True + +class RankLocation(object): + """Class to represent the location of a rank. + + This class represents the location of a rank that can be drawn on the + screen. In part, it serves as an object-oriented wrapper for the + SplineGenerator class. However, it also supports straight-line ranks (only + two points) and ranks that aren't curved/splined (think a zigzag). + """ + + def __init__(self, listOfPoints, curved=True): + """Generate a rank location based on a list of points + + Pass in your list of points upon creation. It'll auto-generate the + slopes; if you'd like to artificially set the slopes list you can do it + later with the SetListOfPoints() function. + """ + self.curved = curved + self._drawingPoints = [] + self._splineFunctions = None + self._listOfSlopes = None + self.SetListOfPoints(listOfPoints, self._listOfSlopes) + + + def SetCurved(self, val): + """Sets if the rank is curved or zigzagged.""" + self.curved = val + self.SetListOfPoints(self._listOfPoints, self._listOfSlopes) + + def GetListOfPoints(self): + return copy.deepcopy(self._listOfPoints) + + def GetListOfDrawingPoints(self): + return self._drawingPoints + + def GetListOfSlopes(self): + return copy.deepcopy(self._listOfSlopes) + + def SetListOfPoints(self, listOfPoints, listOfSlopes): + """Sets the list of points and list of slopes. + + First, we determine if it's a straight line or not, meaning only two + points. + + If None was passed in for the slopes, we generate a slopelist consisting + of Nones, because this is the interface expected by the SplineGenerator + class. + + We then just get the spline functions and drawing points. + + If it's a straight line or a zig-zag, just store the list of points. + The straight lines will be drawn on the GUI end. + """ + if (len(listOfPoints) < 2): + raise InvalidLocationListError("Tried to set a list of %d points" + % len(listOfPoints)) + self._listOfPoints = listOfPoints + + if ((self.curved) and (not self.IsStraight())): + if (listOfSlopes is None): + self._listOfSlopes = [None for _ in listOfPoints] + else: + self._listOfSlopes = listOfSlopes + + self._splineFunctions = \ + CHS.GetSplines(self._listOfPoints, self._listOfSlopes) + self._drawingPoints = \ + CHS.GetPoints(self._splineFunctions) + else: + self._listOfSlopes = None + self._splineFunctions = None + self._drawingPoints = None + + def IsStraight(self): + """Determine whether the rank is straight: iff there are two points.""" + # TODO(astory): make this be based on collinearity + return len(self._listOfPoints) == 2 + + def Clone(self): + return copy.deepcopy(self) + + def CompareRankLocation(self, l2): + return Compare(self, l2) + + def GetMidPoint(self): + """The midpoint of a line connecting the first and last points.""" + p0 = self._listOfPoints[0] + p1 = self._listOfPoints[len(self._listOfPoints) - 1] + xmid = (p0.x + p1.x)/2 + ymid = (p0.y + p1.y)/2 + return Point.Point(xmid, ymid) + + def _Respline(self): + self._splineFunctions = \ + CHS.GetSplines(self._listOfPoints, + self._listOfSlopes) + + def GetPointAtT(self, t, number): + """Get the number-th point in the rank's location at time t + + A straightforward function - takes in a t value, and a number in the + spline list. Depending on what kind of RankLocation it is, finds the + (x,y) point at that t value. + """ + if (self.IsStraight()): + x = self._listOfPoints[0].x + t*(self._listOfPoints[1].x - self._listOfPoints[0].x) + y = self._listOfPoints[0].y + t*(self._listOfPoints[1].y - self._listOfPoints[0].y) + return Point.Point(x,y) + elif (not self.curved): + pfirst = self._listOfPoints[number] + psecond = self._listOfPoints[number + 1] + x = pfirst.x + t*(psecond.x - pfirst.x) + y = pfirst.y + t*(psecond.y - pfirst.y) + return Point.Point(x,y) + else: + x = CHS.EvalCubic(t, self._splineFunctions[number][0]) + y = CHS.EvalCubic(t, self._splineFunctions[number][1]) + return Point.Point(x,y) + + + def GetInformationAtLengthFraction(self, lengthFraction): + """Return information at a fraction of the length of the rank + + Pass in a fraction of the total length at which you wish to get + information. + If the rank is curved, simply call the equivalent function in the + SplineGenerator. + If not, do basically the same thing that that function does, only it's + easier because everything's a straight line. Find between which two + points the length fraction lies, and then find how far along is needed + to get the requisite length. From there, get x, y, dx, and dy. Return + the same thing that the equivalent function in the SplineGenerator does: + [(x,y), (dx,dy), i]. + """ + + if (not self.IsStraight()): + if (self.curved): + return CHS.GetInformationAtLengthFraction(self._splineFunctions, lengthFraction) + + lengths = self.GetLengths() + totalLength = sum(lengths) + lengthNeeded = lengthFraction * totalLength + + i = 0 + if (lengthNeeded > 0.1): + while (lengthNeeded > 0.1): + lengthNeeded = lengthNeeded - lengths[i] + i = i + 1 + i = i - 1 + lengthNeeded = lengthNeeded + lengths[i] + if (lengthNeeded <= 0.1): + t = 0 + else: + t = lengthNeeded/float(lengths[i]) + x = t*(self._listOfPoints[i + 1].x - self._listOfPoints[i].x) + self._listOfPoints[i].x #SplineGenerator.EvalCubic(t, splineList[i][0]) + y = t*(self._listOfPoints[i + 1].y - self._listOfPoints[i].y) + self._listOfPoints[i].y + dx = (self._listOfPoints[i + 1].x - self._listOfPoints[i].x) + dy = (self._listOfPoints[i + 1].y - self._listOfPoints[i].y) + return [(Point.Point(x, y)),(Point.Point(dx, dy)), i] + + def GetLengthFractions(self): + """Returns the lengths fractions at each point. + + The first point lies at lelngth fraction 0, and the last at length + fraction 1. If a point is exactly in the middle, it's at fraction 0.5. + """ + fracs = [] + lengths = self.GetLengths() + lengthtot = sum(lengths) + i = 0 + fracs.append(0.0) + while (i < len(lengths)): + fracs.append(fracs[i] + lengths[i]/float(lengthtot)) + i = i + 1 + return fracs + + def GetLengths(self): + """Returns the length of each part of the RankLocation. + + Behaves differently depending on RankLocation type. If it's curved, call + the equivalent function in the SplineGenerator. + If not, use the Pythogoran Theorem to find out how long each part is. + """ + if (not self.IsStraight()): + if (self.curved): + return CHS.GetLengths(self._splineFunctions) + i = 1 + lengths = [] + while (i < len(self._listOfPoints)): + lengths.append( + math.sqrt( + (self._listOfPoints[i - 1].x - self._listOfPoints[i].x) * + (self._listOfPoints[i - 1].x - self._listOfPoints[i].x) + + (self._listOfPoints[i - 1].y - self._listOfPoints[i].y) * + (self._listOfPoints[i - 1].y - self._listOfPoints[i].y))) + i += 1 + return lengths + + + def IsTranslated(self, l2): + """ If the input is a translated version of self, returns the amount + it's been translated by as a Point. Returns input - self. Else, + returns None. + """ + myList = self._listOfPoints + otherList = l2.GetListOfPoints() + if (len(myList) != len(otherList)): + return None + else: + same = True + dx = otherList[0].x - myList[0].x + dy = otherList[0].y - myList[0].y + i = 1 + while ((i < len(myList)) and same): + same = not (((otherList[i].x - myList[i].x) == dx) and ((otherList[i].y - myList[i].y) == dy)) + i = i + 1 + if (same): + return Point.Point(dx, dy) + else: + return None + + def SwitchEndpoints(self): + """Switch the endpoints by reversing the entire list""" + self._listOfPoints.reverse() + self.SetListOfPoints(self._listOfPoints, self._listOfSlopes) + diff --git a/RankPanda/Song.py b/RankPanda/Song.py index b7c3eb8..e162ccd 100755 --- a/RankPanda/Song.py +++ b/RankPanda/Song.py @@ -347,7 +347,7 @@ def GetRankLocationsAtCount(self, count): return None - + #If the given count is outside of range of waypoints. Or less than 2. def CanAnimate(self, count): if (len(self._wayPointList) < 2): return False @@ -369,8 +369,8 @@ def AnimationBegin(self, count): def AnimationStep(self): if (not self.animating): - print("not self.animating") #(Brady) debug - return (None, 0, 0) #(Brady) added the last 0 to fix error unpacking tuple (try other values to see if it has any effect) + return (None, 0, 0) # TODO(Brady): added the last 0 to fix error + # unpacking tuple (try other values to see if it has any effect) if (self.songLoaded): time = self.timeOffset + pygame.mixer.music.get_pos() count = self.ConvertTimeToCount(time) diff --git a/RankPanda/Spline_test.py b/RankPanda/Spline_test.py new file mode 100644 index 0000000..1639d15 --- /dev/null +++ b/RankPanda/Spline_test.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python +import unittest +import CubicHermiteSpline as CHS + +"""Tests for the CubicHermiteSpline module """ + +class TestSpline(unittest.TestCase): + + def testGetLength(self): + fnlist1 = [[20, 2, -30, (39-2*31/3)], [10, -1, 20, 7]] + fnlist2 = [[8, -3, 20, 1], [80, 4, 2, 1]] + self.assertEquals(CHS.GetLengthHelper(fnlist1, 0, 1, 0.001), CHS._GetLengthHelper(fnlist1, 0, 1, 0.001)) + self.assertEquals(CHS.GetLengthHelper(fnlist2, 0, 1, 0.001), CHS._GetLengthHelper(fnlist2, 0, 1, 0.001)) + + def testGetTVal(self): + pass +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/RankPanda/Tester.py b/RankPanda/Tester.py index de034d7..c164b4b 100755 --- a/RankPanda/Tester.py +++ b/RankPanda/Tester.py @@ -1,211 +1,211 @@ -''' - -Created on Oct 13, 2009 - - - -@author: Adam - -''' - - - -import Song -import Move -import Rank -import Commands -import Point -import RankLocation -import RankIDGen -import CubicHermiteSpline -import CoreWrapper -import copy -import pprint -import pygame -from pygame.locals import * - - -class Tester(object): - def TestPoint1(self): - p1 = Point.Point(0,0) - p2 = Point.Point(10,10) - print(p1.x) - print(p2.y) - - def TestCubicHermiteSpline1(self): - p1 = Point.Point(0,0) - p2 = Point.Point(8, 4) - p3 = Point.Point(16, 0) - splineList = CubicHermiteSpline.SplineGenerator.GetSplines([p1, p2, p3], [None, None, None]) - pointList = CubicHermiteSpline.SplineGenerator.GetPoints(splineList) - print(pointList) - - - def TestSong1(self): - song = Song.Song("DefaultSong", 50, 4) - song.AddMove(1,8) - song.AddMove(9,16) - song.AddMove(4, 12) - Move1 = song.GetCurrentMove() - song.SetCurrentMove(0) - Move0 = song.GetCurrentMove() - print(str(Move0.GetNumber())) - print(str(Move1.GetNumber())) - print(str(Move0.GetFollowing().GetNumber())) - print(str(Move1.GetPrior().GetNumber())) - - location1 = RankLocation.RankLocation([Point.Point(100,100), Point.Point(116,100)]) - location3 = RankLocation.RankLocation([Point.Point(200,200), Point.Point(216, 200)]) - r1 = song.currentMove.CreateRank(location1) - r3 = song.currentMove.CreateRank(location3) - r3ID = r3.GetID() - song.GetCurrentMove().NameRank(r1.GetID(), 'A') - print('ID = ' + str(r1.GetID())) - - song.SetCurrentMove(1) - location2 = RankLocation.RankLocation([Point.Point(100,100), Point.Point(100,116)]) - print("location2's midpoint = " + str(location2.GetMidPoint().x) + ', ' + str(location2.GetMidPoint().y)) - r2 = song.currentMove.CreateRank(location2) - song.GetCurrentMove().NameRank(r1.GetID(), 'A') - - print('ID = ' + str(r2.GetID())) - - cmdLst = r2.GetCommandList() - - i = 0 - while (i < len(cmdLst)): - print(cmdLst[i].GetName() + ' ' + str(cmdLst[i].GetLength())) - i = i + 1 - song.SetCurrentMove(0) - song.SetCurrentMove(1) - newcmdlist = song.GetCurrentMove().LookUpName('A').GetCommandList() - i = 0 - while (i < len(newcmdlist)): - print(newcmdlist[i].GetName() + ' ' + str(newcmdlist[i].GetLength())) - i = i + 1 - song.SetCurrentMove(0) - newr3 = song.GetCurrentMove().LookUpID(r3ID) - listOfPoints = newr3.GetEndLocation().GetListOfPoints() - i = 0 - while (i < len(listOfPoints)): - print(repr(listOfPoints[i])) - i = i + 1 - - - - def TestCoreWrapper(self): - core = CoreWrapper.CoreWrapper("Test", 50, [(1,4)], []) - core.MoveAdded(1, 4, None) - core.MoveAdded(5, 8, None) - core.RankDrawn([Point.Point(100, 100), Point.Point(116, 100)]) - rID = core.GetRanks()[0][0] - core.RankAddSpline(rID, 0) - rLoc = core.GetRanks()[0][2] - rLoc2 = copy.deepcopy(rLoc) - print(repr(rLoc.GetListOfPoints())) - print(repr(rLoc.GetListOfDrawingPoints())) - print(repr(rLoc2.GetListOfPoints())) - print(repr(rLoc2.GetListOfDrawingPoints())) - - - def TestCHS(self): - splines = CubicHermiteSpline.SplineGenerator.GetSplines([Point.Point(0, 0), Point.Point(4, 4), Point.Point(8, 0)], [None, None, None]) - print(repr(splines)) - lengths = CubicHermiteSpline.SplineGenerator.GetLengths(splines) - print(repr(lengths)) - points = CubicHermiteSpline.SplineGenerator.GetPoints(splines) - i = 0 - while (i < len(points)): - j = 0 - while (j < len(points[i])): - print('x = ' + str(points[i][j].x) + ', y = ' + str(points[i][j].y)) - j = j + 1 - i = i + 1 - - - def TestSaving(self): - cw = CoreWrapper.CoreWrapper('Seven', 50, [(1,4)], [(1,1)]) - cw.MoveAdded(1, 9, None) - cw.Save('Heyla') - print('Done!') - - def TestLoading(self): - cw = CoreWrapper.CoreWrapper('Things', 50, [(1,4)], [(1,1)]) - cw.Load('Heyla') - print(cw.GetSong()) - print(cw.GetMoves()[0][1]) - print('Done!') - - - def TestGetLocationAtCount(self): - song = Song.Song('title', 100, 4) - song.AddMove(1, 8) #1 - 32 - song.AddMove(9, 16) #33 - 64 - move0 = song.GetMoveList()[0] - move1 = song.GetMoveList()[1] - loc0 = RankLocation.RankLocation([Point.Point(100, 100), Point.Point(116, 100)]) - loc1 = RankLocation.RankLocation([Point.Point(100, 116), Point.Point(116, 116)]) - r0 = move0.CreateRank(loc0, name='A') - r1 = move1.CreateRank(loc1, name='A') - - rls = song.GetRankLocationsAtCount(8) -# print(str(len(rls))) -# pprint.pprint(r1.GetCommandList()) -# pprint.pprint(rls) -# pointList = rls[0][2].GetListOfPoints() -# print('x0 = ' + str(pointList[0].x) + ', y0 = ' + str(pointList[0].y) + ', x1 = ' + str(pointList[1].x) + ', y1 = ' + str(pointList[1].y)) - - - - def TestGetTimeDifferenceAtCount(self): - song = Song.Song('title', 100, 4) - song.AddMove(1, 8) #Counts 1 - 32 - song.AddMove(9, 16) #Counts 33 - 64 - move0 = song.GetMoveList()[0] - move1 = song.GetMoveList()[1] - loc0 = RankLocation.RankLocation([Point.Point(100, 100), Point.Point(116, 100)]) - loc1 = RankLocation.RankLocation([Point.Point(100, 116), Point.Point(116, 116)]) - r0 = move0.CreateRank(loc0, name='A') - r1 = move1.CreateRank(loc1, name='A') - song.AddWayPoint(1, 0) #Count 1 - song.AddWayPoint(15, 10000) #Count 57 - print('song.GetTimeDifferenceAtCount(30) = ' + str(song.GetTimeDifferenceAtCount(30))) - - def TestMusic(self): - try: - print('line one:') - pygame.mixer.init() - print('line two:') - pygame.mixer.music.load('21 - Guys and Dolls (Reprise).mp3') - print('line three:') - pygame.mixer.music.play(1, 10.0) - print('Loop:') - i = 0 - stop = True - while (stop and (i < 100000000)): - i = i + 1 -# if (pygame.key.get_pressed()[K_s]): -# stop = False -# if ((i % 100000) == 0): -# print(str(pygame.key.get_focused())) - - print('Done!') - except: - j = 0 - while (j < 10000): - print('j = ' + str(j)) - j = j + 1 - finally: - print('finally!') - return 0 - - def TestStuff(self, a=3, b=4): - print('a = ' + str(a)) - print('b = ' + str(b)) - - -#Tester().TestStuff(b = 8, a = 15) -Tester().TestMusic() - - - +''' + +Created on Oct 13, 2009 + + + +@author: Adam + +''' + + + +import Song +import Move +import Rank +import Commands +import Point +import RankLocation +import RankIDGen +import CubicHermiteSpline +import CoreWrapper +import copy +import pprint +import pygame +from pygame.locals import * + + +class Tester(object): + def TestPoint1(self): + p1 = Point.Point(0,0) + p2 = Point.Point(10,10) + print(p1.x) + print(p2.y) + + def TestCubicHermiteSpline1(self): + p1 = Point.Point(0,0) + p2 = Point.Point(8, 4) + p3 = Point.Point(16, 0) + splineList = CubicHermiteSpline.SplineGenerator.GetSplines([p1, p2, p3], [None, None, None]) + pointList = CubicHermiteSpline.SplineGenerator.GetPoints(splineList) + print(pointList) + + + def TestSong1(self): + song = Song.Song("DefaultSong", 50, 4) + song.AddMove(1,8) + song.AddMove(9,16) + song.AddMove(4, 12) + Move1 = song.GetCurrentMove() + song.SetCurrentMove(0) + Move0 = song.GetCurrentMove() + print(str(Move0.GetNumber())) + print(str(Move1.GetNumber())) + print(str(Move0.GetFollowing().GetNumber())) + print(str(Move1.GetPrior().GetNumber())) + + location1 = RankLocation.RankLocation([Point.Point(100,100), Point.Point(116,100)]) + location3 = RankLocation.RankLocation([Point.Point(200,200), Point.Point(216, 200)]) + r1 = song.currentMove.CreateRank(location1) + r3 = song.currentMove.CreateRank(location3) + r3ID = r3.GetID() + song.GetCurrentMove().NameRank(r1.GetID(), 'A') + print('ID = ' + str(r1.GetID())) + + song.SetCurrentMove(1) + location2 = RankLocation.RankLocation([Point.Point(100,100), Point.Point(100,116)]) + print("location2's midpoint = " + str(location2.GetMidPoint().x) + ', ' + str(location2.GetMidPoint().y)) + r2 = song.currentMove.CreateRank(location2) + song.GetCurrentMove().NameRank(r1.GetID(), 'A') + + print('ID = ' + str(r2.GetID())) + + cmdLst = r2.GetCommandList() + + i = 0 + while (i < len(cmdLst)): + print(cmdLst[i].GetName() + ' ' + str(cmdLst[i].GetLength())) + i = i + 1 + song.SetCurrentMove(0) + song.SetCurrentMove(1) + newcmdlist = song.GetCurrentMove().LookUpName('A').GetCommandList() + i = 0 + while (i < len(newcmdlist)): + print(newcmdlist[i].GetName() + ' ' + str(newcmdlist[i].GetLength())) + i = i + 1 + song.SetCurrentMove(0) + newr3 = song.GetCurrentMove().LookUpID(r3ID) + listOfPoints = newr3.GetEndLocation().GetListOfPoints() + i = 0 + while (i < len(listOfPoints)): + print(repr(listOfPoints[i])) + i = i + 1 + + + + def TestCoreWrapper(self): + core = CoreWrapper.CoreWrapper("Test", 50, [(1,4)], []) + core.MoveAdded(1, 4, None) + core.MoveAdded(5, 8, None) + core.RankDrawn([Point.Point(100, 100), Point.Point(116, 100)]) + rID = core.GetRanks()[0][0] + core.RankAddSpline(rID, 0) + rLoc = core.GetRanks()[0][2] + rLoc2 = copy.deepcopy(rLoc) + print(repr(rLoc.GetListOfPoints())) + print(repr(rLoc.GetListOfDrawingPoints())) + print(repr(rLoc2.GetListOfPoints())) + print(repr(rLoc2.GetListOfDrawingPoints())) + + + def TestCHS(self): + splines = CubicHermiteSpline.SplineGenerator.GetSplines([Point.Point(0, 0), Point.Point(4, 4), Point.Point(8, 0)], [None, None, None]) + print(repr(splines)) + lengths = CubicHermiteSpline.SplineGenerator.GetLengths(splines) + print(repr(lengths)) + points = CubicHermiteSpline.SplineGenerator.GetPoints(splines) + i = 0 + while (i < len(points)): + j = 0 + while (j < len(points[i])): + print('x = ' + str(points[i][j].x) + ', y = ' + str(points[i][j].y)) + j = j + 1 + i = i + 1 + + + def TestSaving(self): + cw = CoreWrapper.CoreWrapper('Seven', 50, [(1,4)], [(1,1)]) + cw.MoveAdded(1, 9, None) + cw.Save('Heyla') + print('Done!') + + def TestLoading(self): + cw = CoreWrapper.CoreWrapper('Things', 50, [(1,4)], [(1,1)]) + cw.Load('Heyla') + print(cw.GetSong()) + print(cw.GetMoves()[0][1]) + print('Done!') + + + def TestGetLocationAtCount(self): + song = Song.Song('title', 100, 4) + song.AddMove(1, 8) #1 - 32 + song.AddMove(9, 16) #33 - 64 + move0 = song.GetMoveList()[0] + move1 = song.GetMoveList()[1] + loc0 = RankLocation.RankLocation([Point.Point(100, 100), Point.Point(116, 100)]) + loc1 = RankLocation.RankLocation([Point.Point(100, 116), Point.Point(116, 116)]) + r0 = move0.CreateRank(loc0, name='A') + r1 = move1.CreateRank(loc1, name='A') + + rls = song.GetRankLocationsAtCount(8) +# print(str(len(rls))) +# pprint.pprint(r1.GetCommandList()) +# pprint.pprint(rls) +# pointList = rls[0][2].GetListOfPoints() +# print('x0 = ' + str(pointList[0].x) + ', y0 = ' + str(pointList[0].y) + ', x1 = ' + str(pointList[1].x) + ', y1 = ' + str(pointList[1].y)) + + + + def TestGetTimeDifferenceAtCount(self): + song = Song.Song('title', 100, 4) + song.AddMove(1, 8) #Counts 1 - 32 + song.AddMove(9, 16) #Counts 33 - 64 + move0 = song.GetMoveList()[0] + move1 = song.GetMoveList()[1] + loc0 = RankLocation.RankLocation([Point.Point(100, 100), Point.Point(116, 100)]) + loc1 = RankLocation.RankLocation([Point.Point(100, 116), Point.Point(116, 116)]) + r0 = move0.CreateRank(loc0, name='A') + r1 = move1.CreateRank(loc1, name='A') + song.AddWayPoint(1, 0) #Count 1 + song.AddWayPoint(15, 10000) #Count 57 + print('song.GetTimeDifferenceAtCount(30) = ' + str(song.GetTimeDifferenceAtCount(30))) + + def TestMusic(self): + try: + print('line one:') + pygame.mixer.init() + print('line two:') + pygame.mixer.music.load('21 - Guys and Dolls (Reprise).mp3') + print('line three:') + pygame.mixer.music.play(1, 10.0) + print('Loop:') + i = 0 + stop = True + while (stop and (i < 100000000)): + i = i + 1 +# if (pygame.key.get_pressed()[K_s]): +# stop = False +# if ((i % 100000) == 0): +# print(str(pygame.key.get_focused())) + + print('Done!') + except: + j = 0 + while (j < 10000): + print('j = ' + str(j)) + j = j + 1 + finally: + print('finally!') + return 0 + + def TestStuff(self, a=3, b=4): + print('a = ' + str(a)) + print('b = ' + str(b)) + + +#Tester().TestStuff(b = 8, a = 15) +Tester().TestMusic() + + + diff --git a/ReadMe.txt b/ReadMe.txt index afd8a29..c28afeb 100644 --- a/ReadMe.txt +++ b/ReadMe.txt @@ -25,4 +25,5 @@ UPDATED INSTRUCTIONS: (as of 6/19/13 - Brady) http://www.wxpython.org/download.php Install or build them and put them in the site-packages folder of PythonXX\Lib folder (where XX is the version of python you installed) Python 2.7 may be downloaded from http://www.python.org/getit/ - NOTE: RankPanda will not work with Python 3. \ No newline at end of file + NOTE: RankPanda will not work with Python 3. + From 9b4070973f9d650037634b500db322fb8b821225 Mon Sep 17 00:00:00 2001 From: brajac709 Date: Mon, 24 Jun 2013 01:06:22 -0400 Subject: [PATCH 7/8] mend --- RankPanda/Rank.py | 1 + RankPanda/RankLocation.py | 4 ---- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/RankPanda/Rank.py b/RankPanda/Rank.py index ff0451e..081bedf 100644 --- a/RankPanda/Rank.py +++ b/RankPanda/Rank.py @@ -35,6 +35,7 @@ def __init__(self, endLocation, move): # TODO(astory): why isn't this called 'locked'? self.hold = False self.grabbed = False + self.grabbedPoint = None self.instrument = None self.listOfSelectedCommandNumbers = [] diff --git a/RankPanda/RankLocation.py b/RankPanda/RankLocation.py index 65f5476..c1467fb 100755 --- a/RankPanda/RankLocation.py +++ b/RankPanda/RankLocation.py @@ -229,11 +229,7 @@ def GetLengths(self): lengths.append( math.sqrt( (self._listOfPoints[i - 1].x - self._listOfPoints[i].x) * -<<<<<<< HEAD (self._listOfPoints[i - 1].x - self._listOfPoints[i].x) + -======= - (self._listOfPoints[i - 1].x - self._listOfPoints[i].x) ->>>>>>> 59531fcf6938069a3c63a9b4bbfcf65eb3010dbf (self._listOfPoints[i - 1].y - self._listOfPoints[i].y) * (self._listOfPoints[i - 1].y - self._listOfPoints[i].y))) i += 1 From d103bc951192c80e9a2047e064cbed7f618f896c Mon Sep 17 00:00:00 2001 From: brajac709 Date: Fri, 21 Jun 2013 17:21:17 -0400 Subject: [PATCH 8/8] Style fixes + more iterative - fix Made iterative versions of _GetLengthHelper and _GetTValueAtLengthHelper. Baseline tests show that they do the same thing as recursive versions but more tests may be needed. --- RankPanda/CubicHermiteSpline.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RankPanda/CubicHermiteSpline.py b/RankPanda/CubicHermiteSpline.py index 9beb8bf..107cb51 100755 --- a/RankPanda/CubicHermiteSpline.py +++ b/RankPanda/CubicHermiteSpline.py @@ -272,8 +272,8 @@ def GetInformationAtLengthFraction(splineList, lengthFraction): if lengthNeeded <= 0.1: t = 0 else: - #t = _GetTValueAtLengthHelper(splineList[i], lengthNeeded, 0, 1, 0.001, 0.001) - #TODO MAKE SURE THIS WORKS!!!! + # t = _GetTValueAtLengthHelper(splineList[i], lengthNeeded, 0, 1, 0.001, 0.001) + # TODO(Brady): MAKE SURE THIS WORKS 100%!!!! t = GetTValueAtLengthHelper(splineList[i], lengthNeeded, 0, 1, 0.001, 0.001) x = EvalCubic(t, splineList[i][0]) y = EvalCubic(t, splineList[i][1])