| 71 | | private var eventsMap:HashArray = new HashArray(); |
| 72 | | public function createEvents():void{ |
| 73 | | |
| 74 | | // prepare additional data about events |
| 75 | | |
| 76 | | |
| 77 | | // create events |
| | 82 | private var dataProviderAvailable:Boolean = false; |
| | 83 | private var recreateEvents:Boolean = false; |
| | 84 | private var eventFieldsUpToDate:Boolean = false; |
| | 85 | public function repaint():void{ |
| | 86 | if(dataProviderAvailable){ |
| | 87 | createEvents(); |
| | 88 | repaintEvents(); |
| | 89 | } |
| | 90 | } |
| | 91 | |
| | 92 | /** |
| | 93 | * Create display object for each TimeFrameEntity in dataProvider |
| | 94 | */ |
| | 95 | protected function createEvents():void{ |
| | 96 | |
| | 97 | // in order to save time we recreate EventFields |
| | 98 | // only if it is necessary |
| | 99 | if(recreateEvents){ |
| | 100 | |
| | 101 | // create events |
| | 102 | var ev:EventField; |
| | 103 | var i:int = 0, timeBegin:int, timeEnd:int; |
| | 104 | var from:DateUtil, to:DateUtil, current:DateUtil = (new DateUtil(currentDay)).dayBegin(); |
| | 105 | for each(var json:TimeFrameEntity in dataProvider_){ |
| | 106 | ev = new EventField(); |
| | 107 | eventsMap.put(json.UUID, ev); |
| | 108 | |
| | 109 | ev.eventInfo = new SimpleEventDisplay(); |
| | 110 | ev.data = json; |
| | 111 | ev.name = json.UUID; |
| | 112 | |
| | 113 | // event boundaries, index |
| | 114 | timeBegin = (json.from.getHours() * 4) + ((json.from.getMinutes() as int)/15); |
| | 115 | timeEnd = (json.to.getHours() * 4) + ((json.to.getMinutes() as int)/15); |
| | 116 | // check if event exceed current day |
| | 117 | from = (new DateUtil(json.from)).dayBegin(); |
| | 118 | to = (new DateUtil(json.to)).dayBegin(); |
| | 119 | if( current.compare(from) > 0 ) timeBegin = 0; |
| | 120 | if( current.compare(to) < 0 ) timeEnd = rowsPosition[1].length - 1; |
| | 121 | // store for future use |
| | 122 | ev.beginIndex = timeBegin; |
| | 123 | ev.endIndex = timeEnd; |
| | 124 | |
| | 125 | ev.setStyle("borderThickness", 5); |
| | 126 | ev.setStyle("cornerRadius", 20); |
| | 127 | |
| | 128 | addChild( ev ); |
| | 129 | } |
| | 130 | |
| | 131 | recreateEvents = false; |
| | 132 | } |
| | 133 | } |
| | 134 | |
| | 135 | // used by repaint |
| | 136 | private var clusterOfEventsMap:HashArray = new HashArray(); |
| | 137 | /** |
| | 138 | * Calculates rank and coordinates for each TimeFrame display object |
| | 139 | */ |
| | 140 | protected function repaintEvents():void{ |
| 79 | | var i:int = 0; |
| 80 | | for each(var json:TimeFrameEntity in dataProvider_){ |
| 81 | | ev = new EventField(); |
| 82 | | eventsMap.put(json.UUID, ev); |
| | 142 | var countArray:Array; |
| | 143 | var i:int, j:int = 0; |
| | 144 | |
| | 145 | // in order to save time we recalculate ranks, clusters etc. |
| | 146 | // only if it is necessary |
| | 147 | if(!eventFieldsUpToDate){ |
| | 148 | //------------------------------------------------ |
| | 149 | // "rank" events |
| | 150 | // count number of events for each quarter |
| | 151 | countArray = new Array(96); |
| | 152 | for each(ev in eventsMap){ |
| | 153 | for(i=ev.beginIndex; i<ev.endIndex; i++){ |
| | 154 | if(countArray[i]<0 || isNaN(countArray[i])) countArray[i]=0; |
| | 155 | countArray[i]++; |
| | 156 | } |
| | 157 | } |
| | 158 | |
| | 159 | // calculate rank and number of neighbours for each event |
| | 160 | var neighbours:int; |
| | 161 | for each(ev in eventsMap){ |
| | 162 | neighbours = 0; |
| | 163 | for(i=ev.beginIndex; i<ev.endIndex; i++) neighbours = Math.max( neighbours, countArray[i] ); |
| | 164 | ev.neighbours = neighbours - 1; |
| | 165 | ev.rank = neighbours*10000 + (ev.endIndex - ev.beginIndex)*100 + ev.beginIndex; |
| | 166 | } |
| | 167 | //------------------------------------------------ |
| | 168 | |
| | 169 | |
| | 170 | //------------------------------------------------ |
| | 171 | //clustering events into separated groups which don't intersect each other |
| | 172 | var clusterIndex:Array = new Array(96); |
| | 173 | var helpMap:HashArray, lastCreatedClusterID:int = 1; |
| | 174 | var trace:String = ""; |
| | 175 | |
| | 176 | // clear old clusters |
| | 177 | clusterOfEventsMap.clear(); |
| 84 | | ev.eventInfo = new SimpleEventDisplay(); |
| 85 | | ev.data = json; |
| 86 | | ev.name = json.UUID; |
| 87 | | ev.setStyle("borderThickness", 5); |
| 88 | | ev.setStyle("cornerRadius", 20); |
| 89 | | trace( "ADD : "+ev.data+" - ["+ev.data.UUID+"] " ); |
| 90 | | |
| 91 | | addChild( ev ); |
| 92 | | sizeEvent( ev, i ); |
| 93 | | i += 100; |
| 94 | | |
| 95 | | } |
| 96 | | |
| 97 | | } |
| 98 | | |
| 99 | | private function sizeEvent(ev:EventField, i:int):void{ |
| | 179 | // for each of the events |
| | 180 | for each(ev in eventsMap){ |
| | 181 | helpMap = new HashArray(); |
| | 182 | |
| | 183 | // check the time span of current event |
| | 184 | // if there were already clusters defined for this span then collect their ids |
| | 185 | for(i=ev.beginIndex; i<ev.endIndex; i++){ |
| | 186 | if(clusterIndex[i]<0 || isNaN(clusterIndex[i])) clusterIndex[i]=0; |
| | 187 | if(!helpMap.containsKey(clusterIndex[i]) && clusterIndex[i]>0){ |
| | 188 | helpMap.put(clusterIndex[i], clusterIndex[i]); |
| | 189 | } |
| | 190 | } |
| | 191 | |
| | 192 | if(helpMap.size == 0){ // helpMap.size == #clusters |
| | 193 | // there were no clusters so far for this span, |
| | 194 | // so we 'book' the span and create new cluster on the base of current event |
| | 195 | for(i=ev.beginIndex; i<ev.endIndex; i++) clusterIndex[i] = lastCreatedClusterID; |
| | 196 | clusterOfEventsMap.put(lastCreatedClusterID, [ ev ]); |
| | 197 | lastCreatedClusterID++; |
| | 198 | } |
| | 199 | if(helpMap.size == 1){ |
| | 200 | // there was one cluster for this span |
| | 201 | // so we just join current event to cluster we found |
| | 202 | var currentCluster:int = helpMap.getKeyAt(0); |
| | 203 | for(i=ev.beginIndex; i<ev.endIndex; i++) clusterIndex[i] = currentCluster; |
| | 204 | clusterOfEventsMap[currentCluster].push( ev ); |
| | 205 | } |
| | 206 | if(helpMap.size > 1){ |
| | 207 | // there was more than one cluster for this span |
| | 208 | // we join cluster we found into new single one |
| | 209 | // so we just join current event to cluster we found |
| | 210 | var temp:Array = [ ev ]; |
| | 211 | var minIndex:int = ev.beginIndex, maxIndex:int = ev.endIndex; |
| | 212 | for each(var value:* in helpMap){ |
| | 213 | for each(var clusterElement:EventField in clusterOfEventsMap[value]){ |
| | 214 | temp.push( clusterElement ); // add to new cluster |
| | 215 | minIndex = Math.min(minIndex, clusterElement.beginIndex ); |
| | 216 | maxIndex = Math.max(maxIndex, clusterElement.beginIndex ); |
| | 217 | } |
| | 218 | clusterOfEventsMap.deleteWithKey(value); |
| | 219 | } |
| | 220 | for(i=minIndex; i<maxIndex; i++) clusterIndex[i] = lastCreatedClusterID; |
| | 221 | clusterOfEventsMap.put(lastCreatedClusterID, temp); |
| | 222 | lastCreatedClusterID++; |
| | 223 | } |
| | 224 | |
| | 225 | } |
| | 226 | |
| | 227 | for (var key:String in clusterOfEventsMap){ |
| | 228 | trace = ""; |
| | 229 | var el:EventField; |
| | 230 | for each(el in clusterOfEventsMap[key]) trace+= (el.data as TimeFrameEntity).UUID.substr(0, 3)+"|"; |
| | 231 | Logger.debug( "~("+key+") -> ["+trace+"]" ); |
| | 232 | } |
| | 233 | //------------------------------------------------ |
| | 234 | |
| | 235 | eventFieldsUpToDate = true; |
| | 236 | } |
| | 237 | |
| | 238 | |
| | 239 | //------------------------------------------------ |
| | 240 | // for each cluster size it's events |
| | 241 | j = 0; |
| | 242 | var placeArray:Array = new Array(96); |
| | 243 | var minPosition:int = 0, numberOfPositions:int = 0; |
| | 244 | for each(var eventsArray:Array in clusterOfEventsMap){ |
| | 245 | // sort events by rank |
| | 246 | eventsArray.sort( sortEventsByRank ); |
| | 247 | |
| | 248 | // calculate sub-optimal position within cluster |
| | 249 | // surely there's a better algorithm :) |
| | 250 | numberOfPositions = 0; |
| | 251 | for each(ev in eventsArray){ |
| | 252 | minPosition = 0; |
| | 253 | // find minimal possible position (maximum of available position in span) |
| | 254 | for(i=ev.beginIndex; i<ev.endIndex; i++){ |
| | 255 | if(placeArray[i]<0 || isNaN(placeArray[i])) placeArray[i]=0; |
| | 256 | minPosition = Math.max( minPosition, placeArray[i] ); |
| | 257 | } |
| | 258 | // store information in event |
| | 259 | ev.leftPosition = minPosition; |
| | 260 | //update position availability array for current span |
| | 261 | for(i=ev.beginIndex; i<ev.endIndex; i++){ |
| | 262 | placeArray[i] = minPosition + 1; |
| | 263 | } |
| | 264 | numberOfPositions = Math.max( numberOfPositions, minPosition ); |
| | 265 | } |
| | 266 | |
| | 267 | // size events |
| | 268 | for each(ev in eventsArray){ |
| | 269 | sizeEvent( ev, eventsArray.length, numberOfPositions, j ); |
| | 270 | j += 100; |
| | 271 | } |
| | 272 | } |
| | 273 | //------------------------------------------------ |
| | 274 | } |
| | 275 | private function sortEventsByRank(a:EventField, b:EventField):int { |
| | 276 | return ( a.rank < b.rank ) ? -1 : (( a.rank > b.rank ) ? 1 : 0 ); |
| | 277 | } |
| | 278 | |
| | 279 | private function sizeEvent(ev:EventField, clusterSize:int, numberOfPositions:int, i:int):void{ |
| 102 | | var timeBegin:int = (data.from.getHours() * 4) + ((data.from.getMinutes() as int)/15); |
| 103 | | var timeEnd:int = (data.to.getHours() * 4) + ((data.to.getMinutes() as int)/15); |
| 104 | | |
| 105 | | // if event exceed current day |
| 106 | | var from:DateUtil = (new DateUtil(data.from)).dayBegin(); |
| 107 | | var current:DateUtil = (new DateUtil(currentDay)).dayBegin(); |
| 108 | | var to:DateUtil = (new DateUtil(data.to)).dayBegin(); |
| 109 | | |
| 110 | | if( current.compare(from) > 0 ){ |
| 111 | | timeBegin = 0; |
| 112 | | } |
| 113 | | if( current.compare(to) < 0 ){ |
| 114 | | timeEnd = rowsPosition[1].length - 1; |
| 115 | | } |
| 116 | | |
| 117 | | ev.width = 200;//XXX |
| 118 | | ev.height = rowsPosition[1][timeEnd] - rowsPosition[1][timeBegin]; |
| 119 | | ev.x = 50+i;//XXX |
| 120 | | ev.y = rowsPosition[1][timeBegin]; |
| | 282 | var availableWidth:Number = this.width - this.leftHorizontalMargin_ - this.rightHorizontalMargin_; |
| | 283 | ev.width = availableWidth * (2 / (numberOfPositions+2)); |
| | 284 | ev.height = rowsPosition[1][ev.endIndex] - rowsPosition[1][ev.beginIndex]; |
| | 285 | ev.x = this.leftHorizontalMargin_ + ((availableWidth / (numberOfPositions+2)) * ev.leftPosition); |
| | 286 | ev.y = rowsPosition[1][ev.beginIndex]; |
| | 287 | this.setChildIndex( ev, 0 ); |