diff --git a/RankPanda/Commands.py b/RankPanda/Commands.py index eb914e3..871c816 100755 --- a/RankPanda/Commands.py +++ b/RankPanda/Commands.py @@ -12,56 +12,69 @@ 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..f63c1be 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,13 @@ 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 + return [(curmove.GetNumber(), curmove.GetName()) for curmove in listOfMoves] + + + # 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,31 +71,16 @@ def GetCurrentMove(self): # ? # List of all ranks in current move # [(id0, name0, location0), (id1, name1, location1)] - def GetRanks(self): - 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 - - - # Same as above but now you input the move number. - def GetRanksGivenMove(self, moveNumber): + def GetRanks(self, moveNumber = None): 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 - - + if (moveNumber is None): + allRanks = self._song.currentMove.GetAllRanks() + else: + allRanks = self._song.GetMoveList()[moveNumber].GetAllRanks() + 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 @@ -113,12 +93,8 @@ 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 + return [(r.GetID(), r.GetName(), r.GetEndLocation(),\ + r.GetLabelLocation()) for r in allSelectedRanks] def GetAdditionalRanks(self): curList = [] @@ -775,6 +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. # 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 8b6ed17..107cb51 100755 --- a/RankPanda/CubicHermiteSpline.py +++ b/RankPanda/CubicHermiteSpline.py @@ -1,230 +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. - -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]])) - 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)) - 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)) - 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 - 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)) - 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 - else: - if (curLength > length): - return SplineGenerator._GetTValueAtLengthHelper(fnList, length, tinit, tmid, lengthCalcTol, lengthCompareTol) - else: - return SplineGenerator._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(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]) + 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 2ccffbf..9dc5b70 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) @@ -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) @@ -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. """ @@ -833,25 +846,23 @@ def __init__(self, parent): # begin layout code self.directionPanel = wx.BoxSizer(wx.HORIZONTAL) self.directionChoices = ['Clockwise', 'Counter-clockwise'] - self.directionChoicesValues = [0, 1] 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 +887,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) @@ -896,13 +907,14 @@ 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.EndModal(0) def OnCancel(self, event): - self.EndModal(-1) - - + self.EndModal(-1) + + @@ -920,14 +932,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) @@ -969,7 +981,7 @@ def OnOK(self, event): self.EndModal(0) def OnCancel(self, event): - self.EndModal(-1) + self.EndModal(-1) @@ -987,15 +999,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) @@ -1037,11 +1049,11 @@ def OnOK(self, event): self.EndModal(0) def OnCancel(self, event): - self.EndModal(-1) + self.EndModal(-1) - + class ExpandDialog(wx.Dialog): @@ -1056,15 +1068,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) @@ -1106,10 +1118,10 @@ def OnOK(self, event): self.EndModal(0) def OnCancel(self, event): - self.EndModal(-1) + self.EndModal(-1) - - + + class CondenseDialog(wx.Dialog): @@ -1124,15 +1136,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) @@ -1174,7 +1186,7 @@ def OnOK(self, event): self.EndModal(0) def OnCancel(self, event): - self.EndModal(-1) + self.EndModal(-1) @@ -1187,14 +1199,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) @@ -1234,10 +1246,10 @@ def OnOK(self, event): self.EndModal(0) def OnCancel(self, event): - self.EndModal(-1) + self.EndModal(-1) - + class CurveDialog(wx.Dialog): @@ -1247,14 +1259,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) @@ -1294,8 +1306,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 """ @@ -1425,13 +1437,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) @@ -1485,7 +1497,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/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 05b0f7d..d89c08a 100755 --- a/RankPanda/GUIMain.py +++ b/RankPanda/GUIMain.py @@ -1,1424 +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: - self.dirname = '' - e = wx.FileDialog(self, "Open File", self.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) - 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) - - 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.GetRanksGivenMove(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.GetRanksGivenMove(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: - isSelected = False - - for s in selected: - 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): - 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 - 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 """ - self.dirname = '' - - while True: - d = wx.FileDialog(self, "Open File", self.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 """ - self.dirname = '' - d = wx.FileDialog(self, "Save File", self.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 """ - self.dirname = '' - d = wx.FileDialog(self, "Save File", self.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 7e214bb..55d2ae7 100755 --- a/RankPanda/Move.py +++ b/RankPanda/Move.py @@ -1,322 +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() - self._nameRankIndex = dict() - self._RankIDGen = RankIDGen.RankIDGen() - self._name = None - self._number = None - self.SetNumber(number) - self._prior = prior - self._following = following - self._listOfActiveRanks = [] - self._listOfActiveCommands = [] - 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)) - 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() - - # 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 - - def GetAllNamedRanks(self): - allNamedRanks = [] - i = 0 - items = self._nameRankIndex.items() - while (i < len(items)): - allNamedRanks.append(items[i][1]) - i = i + 1 - return allNamedRanks - - # 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): - 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/Move_test.py b/RankPanda/Move_test.py index 5f6348a..148b5c6 100644 --- a/RankPanda/Move_test.py +++ b/RankPanda/Move_test.py @@ -11,6 +11,16 @@ 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) + def testSetNumberName(self): + m = Move(0, 4, None, None, None) + m.SetName('Lala') + self.assertEquals(m._name, 'Lala') + m.SetNumber(3) + self.assertEquals(m._number, 3) + self.assertEquals(m._name, 'Lala') + if __name__ == '__main__': unittest.main() 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 a 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() - # 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) - - 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] - begin1 = beginLocation.GetListOfPoints()[-1] - 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)))) - #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)))) - 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 447debd..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.SplineGenerator.GetSplines(self._listOfPoints, - self._listOfSlopes) - self._drawingPoints = \ - CHS.SplineGenerator.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.SplineGenerator.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.SplineGenerator.EvalCubic(t, self._splineFunctions[number][0]) - y = CHS.SplineGenerator.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.SplineGenerator.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.SplineGenerator.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 da38c1f..e162ccd 100755 --- a/RankPanda/Song.py +++ b/RankPanda/Song.py @@ -87,16 +87,16 @@ def AddMoveByCount(self, startCount, endCount): i = 0 stop = False while ((i < len(self._moveList)) and (not stop)): - if (self._moveList[i].GetStartCount() > 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 @@ -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,7 +369,8 @@ def AnimationBegin(self, count): def AnimationStep(self): if (not self.animating): - return (None, 0) + 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 28e0aa6..c28afeb 100644 --- a/ReadMe.txt +++ b/ReadMe.txt @@ -1,18 +1,29 @@ -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. +