Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1#!/usr/bin/env python 

2 

3""" 

4camcops_server/cc_modules/cc_trackerhelpers.py 

5 

6=============================================================================== 

7 

8 Copyright (C) 2012-2020 Rudolf Cardinal (rudolf@pobox.com). 

9 

10 This file is part of CamCOPS. 

11 

12 CamCOPS is free software: you can redistribute it and/or modify 

13 it under the terms of the GNU General Public License as published by 

14 the Free Software Foundation, either version 3 of the License, or 

15 (at your option) any later version. 

16 

17 CamCOPS is distributed in the hope that it will be useful, 

18 but WITHOUT ANY WARRANTY; without even the implied warranty of 

19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

20 GNU General Public License for more details. 

21 

22 You should have received a copy of the GNU General Public License 

23 along with CamCOPS. If not, see <https://www.gnu.org/licenses/>. 

24 

25=============================================================================== 

26 

27**Helper representations for trackers.** 

28 

29""" 

30 

31from enum import Enum 

32from typing import List, Optional 

33 

34import numpy as np 

35 

36 

37DEFAULT_TRACKER_ASPECT_RATIO = 2.0 # width / height 

38 

39 

40class LabelAlignment(Enum): 

41 """ 

42 Enum representing figure label alignment. 

43 """ 

44 center = "center" 

45 top = "top" 

46 bottom = "bottom" 

47 baseline = "baseline" 

48 

49 

50class TrackerLabel(object): 

51 """ 

52 Representation of a label on a 

53 :class:`camcops_server.cc_modules.cc_tracker.Tracker` figure. 

54 """ 

55 def __init__(self, 

56 y: float, 

57 label: str, 

58 vertical_alignment: LabelAlignment = LabelAlignment.center): 

59 """ 

60 Args: 

61 y: Y axis (vertical) position 

62 label: text for label 

63 vertical_alignment: :class:`LabelAlignment` enum 

64 """ 

65 self.y = y 

66 self.label = label 

67 self.vertical_alignment = vertical_alignment 

68 

69 

70class TrackerAxisTick(object): 

71 """ 

72 Representation of a Y-axis tick mark and associated label on a 

73 :class:`camcops_server.cc_modules.cc_tracker.Tracker` figure. 

74 """ 

75 def __init__(self, y: float, label: str): 

76 self.y = y 

77 self.label = label 

78 

79 

80class TrackerInfo(object): 

81 """ 

82 Tasks return one or more of these (one for each tracker to be shown), from 

83 which :class:`camcops_server.cc_modules.cc_tracker.Tracker` displays are 

84 created. 

85 """ 

86 def __init__(self, 

87 value: float, 

88 plot_label: str = None, 

89 axis_label: str = None, 

90 axis_min: float = None, 

91 axis_max: float = None, 

92 axis_ticks: Optional[List[TrackerAxisTick]] = None, 

93 horizontal_lines: Optional[List[float]] = None, 

94 horizontal_labels: Optional[List[TrackerLabel]] = None, 

95 aspect_ratio: Optional[float] = DEFAULT_TRACKER_ASPECT_RATIO): 

96 """ 

97 Args: 

98 value: numerical value 

99 plot_label: label for the whole plot 

100 axis_label: label for the Y axis 

101 axis_min: minimum value for the Y axis 

102 axis_max: maximum value for the Y axis 

103 axis_ticks: optional list of :class:`TrackerAxisTick` objects 

104 describing where to put tick marks/labels on the Y axis 

105 horizontal_lines: optional list of y values at which to draw 

106 horizontal (dotted) lines 

107 horizontal_labels: optional list of :class:`TrackerLabel` objects 

108 indicating which additional labels to place on the main plot 

109 (such as: to describe the meaning of the horizontal lines) 

110 aspect_ratio: optional aspect ratio (width / height) 

111 """ 

112 self.value = value 

113 self.plot_label = plot_label 

114 self.axis_label = axis_label 

115 self.axis_min = axis_min 

116 self.axis_max = axis_max 

117 self.axis_ticks = axis_ticks or [] 

118 self.horizontal_lines = horizontal_lines or [] 

119 self.horizontal_labels = horizontal_labels or [] 

120 self.aspect_ratio = aspect_ratio 

121 

122 

123def equally_spaced_ndarray(start: float, stop: float, num: int, 

124 endpoint: bool = True) -> np.ndarray: 

125 """ 

126 Produces equally spaced numbers. See 

127 https://stackoverflow.com/questions/477486/how-to-use-a-decimal-range-step-value. 

128 

129 Args: 

130 start: starting value 

131 stop: stopping value 

132 num: number of values to return 

133 endpoint: include the endpoint? 

134 

135 Returns: 

136 list of floats 

137 

138 """ # noqa 

139 return np.linspace(start, stop, num, endpoint=endpoint, dtype=float) 

140 

141 

142def equally_spaced_float(start: float, stop: float, num: int, 

143 endpoint: bool = True) -> List[float]: 

144 """ 

145 Returns a float equivalent of :func:`equally_spaced_float` (q.v.). 

146 """ 

147 return list(equally_spaced_ndarray(start, stop, num, endpoint=endpoint)) 

148 

149 

150def equally_spaced_int(start: int, stop: int, step: int, 

151 endpoint: bool = True) -> List[int]: 

152 """ 

153 Almost a synonym for :func:`range`! 

154 

155 Args: 

156 start: starting value 

157 stop: stopping value (INCLUSIVE if endpoint is True) 

158 step: step size 

159 endpoint: bool 

160 

161 Returns: 

162 list of integers 

163 """ 

164 if endpoint: 

165 if start <= stop: # normal 

166 range_stop = stop + 1 

167 else: # counting backwards: start > stop 

168 range_stop = stop - 1 

169 else: 

170 range_stop = stop 

171 return list(range(start, range_stop, step)) 

172 

173 

174def regular_tracker_axis_ticks_float( 

175 start: float, stop: float, num: int, 

176 endpoint: bool = True) -> List[TrackerAxisTick]: 

177 """ 

178 Args: 

179 start: starting value 

180 stop: stopping value 

181 num: number of values to return 

182 endpoint: include the endpoint? 

183 

184 Returns: 

185 a list of simple numerical TrackerAxisTick objects 

186 

187 """ 

188 ticks = [] # type: List[TrackerAxisTick] 

189 for val in equally_spaced_ndarray(start, stop, num, endpoint=endpoint): 

190 ticks.append(TrackerAxisTick(val, str(val))) 

191 return ticks 

192 

193 

194def regular_tracker_axis_ticks_int( 

195 start: int, stop: int, step: int, 

196 endpoint: bool = True) -> List[TrackerAxisTick]: 

197 """ 

198 Args: 

199 start: starting value 

200 stop: stopping value 

201 step: step size 

202 endpoint: include the endpoint? 

203 

204 Returns: 

205 a list of simple numerical TrackerAxisTick objects 

206 

207 """ 

208 ticks = [] # type: List[TrackerAxisTick] 

209 for val in equally_spaced_int(start, stop, step, endpoint=endpoint): 

210 ticks.append(TrackerAxisTick(val, str(val))) 

211 return ticks