aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThéo de la Hogue2024-09-03 14:44:45 +0200
committerThéo de la Hogue2024-09-03 14:44:45 +0200
commit3aa00e234e122f3cedffdc21c00a430fee7984a8 (patch)
tree489c94c885b7f52e7abd7a24eb7760d7b3a0ac3c
parenta3eb35c5a3725ba589663c6a8992b48b6743eb11 (diff)
downloadargaze-3aa00e234e122f3cedffdc21c00a430fee7984a8.zip
argaze-3aa00e234e122f3cedffdc21c00a430fee7984a8.tar.gz
argaze-3aa00e234e122f3cedffdc21c00a430fee7984a8.tar.bz2
argaze-3aa00e234e122f3cedffdc21c00a430fee7984a8.tar.xz
Adding new CSV file context.
-rw-r--r--docs/user_guide/utils/demonstrations_scripts.md8
-rw-r--r--src/argaze/utils/contexts/File.py213
-rw-r--r--src/argaze/utils/demo/csv_file_context.json10
-rw-r--r--src/argaze/utils/demo/gaze_positions.csv1052
4 files changed, 1283 insertions, 0 deletions
diff --git a/docs/user_guide/utils/demonstrations_scripts.md b/docs/user_guide/utils/demonstrations_scripts.md
index 277f380..e55e547 100644
--- a/docs/user_guide/utils/demonstrations_scripts.md
+++ b/docs/user_guide/utils/demonstrations_scripts.md
@@ -20,6 +20,14 @@ Load **random_context.json** file to generate random gaze positions:
python -m argaze load ./src/argaze/utils/demo/random_context.json
```
+## CSV file context
+
+Load **csv_file_context.json** file to analyze gaze positions from a CSV file:
+
+```shell
+python -m argaze load ./src/argaze/utils/demo/csv_file_context.json
+```
+
## OpenCV
### Cursor context
diff --git a/src/argaze/utils/contexts/File.py b/src/argaze/utils/contexts/File.py
new file mode 100644
index 0000000..91c64e2
--- /dev/null
+++ b/src/argaze/utils/contexts/File.py
@@ -0,0 +1,213 @@
+"""Define eye tracking data file context"""
+
+"""
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+You should have received a copy of the GNU General Public License along with
+this program. If not, see <https://www.gnu.org/licenses/>.
+"""
+
+__author__ = "Théo de la Hogue"
+__credits__ = []
+__copyright__ = "Copyright 2023, Ecole Nationale de l'Aviation Civile (ENAC)"
+__license__ = "GPLv3"
+
+import logging
+import time
+import math
+import threading
+
+import pandas
+import numpy
+
+from argaze import ArFeatures, DataFeatures, GazeFeatures
+
+
+class CSV(ArFeatures.DataPlaybackContext):
+
+ @DataFeatures.PipelineStepInit
+ def __init__(self, **kwargs):
+
+ # Init ArContext class
+ super().__init__()
+
+ # Init private attributes
+ self.__path = None
+ self.__separator = ','
+ self.__timestamp_column = None
+ self.__x_column = None
+ self.__y_column = None
+ self.__start = math.nan
+ self.__end = math.nan
+ self.__duration = 0.
+ self.__progression = 0.
+
+ @property
+ def path(self) -> str:
+ """Path to data file."""
+ return self.__path
+
+ @path.setter
+ def path(self, path: str):
+
+ self.__path = path
+
+ @property
+ def separator(self) -> str:
+ """Value delimiter character"""
+ return self.__separator
+
+ @separator.setter
+ def separator(self, separator: str):
+
+ self.__separator = separator
+
+ @property
+ def timestamp_column(self) -> str:
+ """Select timestamp column."""
+ return self.__timestamp_column
+
+ @timestamp_column.setter
+ def timestamp_column(self, timestamp_column: str):
+
+ self.__timestamp_column = timestamp_column
+
+ @property
+ def x_column(self) -> str:
+ """Select x column."""
+ return self.__x_column
+
+ @x_column.setter
+ def x_column(self, x_column: str):
+
+ self.__x_column = x_column
+
+ @property
+ def y_column(self) -> str:
+ """Select y column."""
+ return self.__y_column
+
+ @y_column.setter
+ def y_column(self, y_column: str):
+
+ self.__y_column = y_column
+ @property
+ def start(self) -> int|float:
+ """Start reading timestamp."""
+ return self.__start
+
+ @start.setter
+ def start(self, start: int|float):
+
+ self.__start = start
+
+ @property
+ def end(self) -> int|float:
+ """End reading timestamp."""
+ return self.__end
+
+ @end.setter
+ def end(self, end: int|float):
+
+ self.__end = end
+
+ @property
+ def duration(self) -> int|float:
+ """Get data duration."""
+
+ return self.__duration
+
+ @property
+ def progression(self) -> float:
+ """Get data processing progression between 0 and 1."""
+
+ return self.__progression
+
+ @DataFeatures.PipelineStepEnter
+ def __enter__(self):
+
+ logging.info('CSV file context starts...')
+
+ # Load gaze positions from a CSV file into Panda Dataframe
+ data_selector = {
+ self.__timestamp_column: numpy.float64,
+ self.__x_column: numpy.float64,
+ self.__y_column: numpy.float64
+ }
+
+ dataframe = pandas.read_csv(self.__path, delimiter = self.__separator, low_memory = False, usecols=data_selector.keys(), dtype=data_selector)
+
+ # Rename columns with generic names
+ dataframe.rename(columns={
+ self.__timestamp_column: 'timestamp',
+ self.__x_column: 'x',
+ self.__y_column: 'y'
+ }, inplace=True)
+
+ # Optionnaly select a time range
+ if not math.isnan(self.start):
+
+ dataframe = dataframe.loc[(dataframe['timestamp'] >= self.start)]
+
+ if not math.isnan(self.end):
+
+ dataframe = dataframe.loc[(dataframe['timestamp'] <= self.end)]
+
+ # Filter unvalid gaze positions
+ len_before = len(dataframe)
+ dataframe = dataframe[(dataframe['x'].notna() & dataframe['y']).notna()]
+
+ if len(dataframe) != len_before:
+ logging.info('%i unvalid gaze positions have been removed', len_before - len(dataframe))
+
+ # Open reading thread
+ self.__reading_thread = threading.Thread(target=self.__read, kwargs={'dataframe': dataframe})
+
+ logging.debug('> starting reading thread...')
+ self.__reading_thread.start()
+
+ @DataFeatures.PipelineStepExit
+ def __exit__(self, exception_type, exception_value, exception_traceback):
+
+ logging.info('CSV file context stops...')
+
+ # Close data stream
+ self.stop()
+
+ # Stop reading thread
+ threading.Thread.join(self.__reading_thread)
+
+ def __read(self, dataframe: pandas.DataFrame):
+ """Read and process gaze positions from dataframe."""
+
+ start_ts = dataframe.iloc[0].timestamp
+ end_ts = dataframe.iloc[-1].timestamp
+
+ self.__duration = end_ts - start_ts
+ self.__progression = 0.
+
+ logging.info('Reading %i gaze positions from %s to %s...', len(dataframe), f'{start_ts}', f'{end_ts}')
+
+ for index, row in dataframe.iterrows():
+
+ # Stop reading
+ if not self.is_running():
+
+ break
+
+ # Pause reading
+ while self.is_paused() and self.is_running():
+
+ time.sleep(0.1)
+
+ # Process gaze position
+ self._process_gaze_position(x = row['x'], y = row['y'], timestamp = row['timestamp'])
+
+ # Update progression
+ self.__progression = row['timestamp'] / self.__duration
+
diff --git a/src/argaze/utils/demo/csv_file_context.json b/src/argaze/utils/demo/csv_file_context.json
new file mode 100644
index 0000000..a09914f
--- /dev/null
+++ b/src/argaze/utils/demo/csv_file_context.json
@@ -0,0 +1,10 @@
+{
+ "argaze.utils.contexts.File.CSV" : {
+ "name": "CSV file data playback",
+ "path": "./src/argaze/utils/demo/gaze_positions.csv",
+ "timestamp_column": "Timestamp (ms)",
+ "x_column": "Gaze Position X (px)",
+ "y_column": "Gaze Position Y (px)",
+ "pipeline": "gaze_analysis_pipeline.json"
+ }
+} \ No newline at end of file
diff --git a/src/argaze/utils/demo/gaze_positions.csv b/src/argaze/utils/demo/gaze_positions.csv
new file mode 100644
index 0000000..558c995
--- /dev/null
+++ b/src/argaze/utils/demo/gaze_positions.csv
@@ -0,0 +1,1052 @@
+"Timestamp (ms)","Gaze Position X (px)","Gaze Position Y (px)"
+6016,400,443
+6056,400,443
+6106,401,443
+6128,401,443
+6168,401,442
+6367,401,442
+6409,401,442
+6486,401,442
+7310,408,433
+7334,415,427
+7375,435,412
+7397,441,409
+7438,445,405
+7472,446,405
+7509,446,404
+7532,446,404
+7570,449,392
+7593,450,384
+7631,450,378
+7653,450,377
+7705,432,371
+7730,415,365
+7766,390,353
+7788,376,345
+7828,334,318
+7847,318,306
+7890,296,288
+7912,286,277
+7950,278,263
+7978,275,256
+8018,268,246
+8039,266,245
+8074,265,244
+8092,264,243
+8128,262,243
+8185,263,242
+8205,264,241
+8241,265,231
+8261,265,228
+8305,267,227
+8337,267,226
+8392,267,228
+8427,269,232
+8445,270,232
+8481,272,231
+8501,273,228
+8537,273,219
+8558,272,217
+8592,272,217
+8613,271,218
+8650,271,222
+8671,271,225
+8703,272,227
+8724,272,227
+8755,274,227
+8776,274,226
+8813,275,224
+8834,275,223
+8866,275,223
+8886,279,225
+8922,292,228
+8944,312,227
+8981,351,222
+9000,382,218
+9033,424,214
+9053,447,213
+9087,510,216
+9104,538,216
+9137,591,218
+9156,620,220
+9192,691,219
+9212,719,216
+9246,788,214
+9267,812,215
+9300,855,216
+9319,878,215
+9352,900,215
+9371,907,216
+9403,915,217
+9420,915,217
+9464,915,217
+9484,915,218
+9519,913,219
+9538,912,219
+9573,904,222
+9592,893,224
+9626,875,225
+9645,871,226
+9677,869,226
+9738,869,227
+9760,870,229
+9798,872,231
+9818,872,231
+9851,875,232
+9871,879,232
+9904,883,230
+9925,884,229
+9963,886,228
+9984,886,227
+10073,887,229
+10125,892,234
+10146,894,234
+10181,895,233
+10202,896,231
+10232,896,229
+10252,896,229
+10341,896,229
+10363,896,232
+10399,894,247
+10420,894,254
+10454,890,274
+10472,887,284
+10519,866,316
+10540,858,328
+10573,842,352
+10590,830,370
+10621,816,386
+10641,801,399
+10671,780,415
+10690,770,422
+10723,749,435
+10742,734,442
+10772,717,449
+10790,699,458
+10823,673,468
+10839,659,471
+10868,639,477
+10885,625,481
+10921,597,492
+10941,587,496
+10975,566,508
+10991,558,513
+11025,545,523
+11044,542,527
+11078,537,536
+11095,536,540
+11126,535,548
+11142,535,551
+11177,538,556
+11196,538,557
+11230,541,559
+11247,542,559
+11288,542,559
+11331,542,559
+11349,541,559
+11383,540,558
+11441,542,558
+11462,544,559
+11497,552,562
+11518,558,566
+11549,561,569
+11571,562,569
+11605,562,570
+11657,565,564
+11676,565,561
+11711,564,557
+11766,564,558
+11787,565,560
+11822,569,569
+11841,569,570
+11877,571,570
+11898,572,568
+11933,573,564
+11954,574,561
+11988,575,559
+12009,575,559
+12078,575,560
+12115,578,564
+12137,583,565
+12171,599,560
+12192,607,549
+12229,612,537
+12254,613,535
+12288,613,536
+12305,613,537
+12334,613,539
+12354,613,539
+12388,617,541
+12406,624,540
+12438,638,534
+12458,647,530
+12495,685,512
+12515,706,502
+12547,752,474
+12567,779,449
+12602,811,405
+12622,830,380
+12653,848,359
+12671,864,341
+12706,881,316
+12726,887,308
+12760,890,305
+12776,892,305
+12812,896,301
+12831,898,300
+12868,901,299
+12888,911,296
+12926,928,284
+12947,936,280
+12983,957,266
+13000,963,262
+13034,969,257
+13053,969,257
+13087,969,258
+13108,969,260
+13142,970,266
+13159,971,267
+13189,971,267
+13216,972,266
+13249,972,261
+13271,970,256
+13310,964,252
+13331,963,252
+13367,962,253
+13388,962,255
+13422,962,257
+13443,963,257
+13480,964,256
+13500,964,255
+13536,964,253
+13556,964,253
+13590,964,254
+13611,964,255
+13647,965,257
+13668,965,257
+13705,966,256
+13725,967,253
+13762,959,243
+13782,944,238
+13816,914,234
+13837,893,233
+13870,830,223
+13890,806,216
+13922,751,204
+13941,718,201
+13974,655,192
+13993,629,188
+14030,565,184
+14051,539,184
+14082,491,186
+14100,467,186
+14132,420,189
+14150,393,189
+14182,363,190
+14198,349,189
+14232,324,188
+14252,315,188
+14286,306,190
+14305,303,190
+14338,295,191
+14358,295,191
+14394,295,190
+14414,296,190
+14448,299,188
+14468,302,186
+14501,311,178
+14521,317,174
+14556,319,174
+14614,318,177
+14634,318,181
+14666,318,182
+14686,319,182
+14721,320,181
+14741,320,180
+14776,319,179
+14795,318,179
+14835,313,181
+14856,312,185
+14896,312,186
+14917,312,186
+14950,315,185
+14971,316,184
+15007,316,182
+15027,315,182
+15069,309,182
+15094,302,187
+15127,285,199
+15144,276,205
+15178,261,219
+15204,256,223
+15238,250,229
+15256,243,239
+15290,235,263
+15309,229,274
+15344,217,294
+15363,212,302
+15400,205,314
+15419,204,314
+15453,204,314
+15482,203,313
+15517,204,307
+15537,204,305
+15569,203,304
+15586,203,303
+15621,202,303
+15641,203,302
+15673,208,296
+15694,209,294
+15732,210,291
+15753,210,291
+15790,210,290
+15808,210,291
+15842,211,292
+15861,211,292
+15899,212,292
+15924,212,291
+15955,209,290
+15973,209,290
+16010,208,291
+16032,208,291
+16070,208,287
+16091,210,279
+16122,210,258
+16140,208,244
+16174,205,213
+16193,204,203
+16223,203,195
+16239,203,192
+16273,203,190
+16291,203,190
+16325,203,192
+16345,205,202
+16377,210,210
+16397,211,210
+16433,213,208
+16454,213,205
+16488,211,202
+16507,211,202
+16543,211,202
+16565,211,203
+16600,212,205
+16621,213,206
+16654,214,206
+16674,214,206
+16713,214,203
+16734,213,203
+16768,212,203
+16788,213,206
+16823,223,224
+16843,227,229
+16876,241,236
+16894,252,242
+16930,263,252
+16950,269,258
+16987,273,262
+17006,278,267
+17037,284,272
+17054,288,275
+17089,290,275
+17106,291,276
+17139,299,282
+17162,303,285
+17197,305,286
+17217,306,287
+17256,307,287
+17277,307,285
+17315,302,279
+17337,302,279
+17373,304,281
+17394,306,282
+17427,308,281
+17448,308,281
+17484,308,280
+17505,307,280
+17544,308,281
+17567,309,282
+17603,316,285
+17624,326,291
+17662,350,319
+17683,361,336
+17719,389,378
+17738,403,396
+17768,435,430
+17788,451,444
+17822,486,473
+17842,499,482
+17875,523,500
+17894,538,510
+17928,552,519
+17947,557,522
+17981,565,528
+17998,566,529
+18033,568,527
+18051,569,527
+18085,572,527
+18102,573,526
+18136,574,522
+18153,575,520
+18185,578,518
+18206,579,517
+18242,580,517
+18300,581,517
+18321,582,517
+18359,585,513
+18379,586,509
+18411,590,498
+18432,592,491
+18467,593,487
+18489,594,487
+18527,594,486
+18582,595,490
+18601,596,490
+18638,597,491
+18660,598,490
+18696,599,483
+18717,599,481
+18750,599,480
+18771,600,483
+18810,603,496
+18830,604,500
+18866,609,508
+18888,613,513
+18924,614,513
+18945,615,514
+18983,621,530
+19001,622,538
+19035,624,548
+19060,625,554
+19093,626,567
+19110,626,570
+19145,626,571
+19164,626,572
+19199,628,576
+19218,628,576
+19249,631,576
+19266,632,575
+19304,633,573
+19325,633,573
+19405,633,574
+19428,637,576
+19467,642,578
+19488,642,578
+19546,643,578
+19583,644,577
+19604,645,576
+19636,649,572
+19654,651,569
+19686,654,567
+19707,657,565
+19745,666,559
+19764,672,555
+19801,691,535
+19822,704,514
+19853,720,489
+19872,740,462
+19907,771,419
+19926,791,382
+19960,814,335
+19977,825,315
+20010,846,289
+20030,859,275
+20063,877,258
+20080,885,249
+20115,897,240
+20134,901,238
+20170,902,239
+20190,903,246
+20224,905,252
+20242,906,253
+20277,908,258
+20296,910,266
+20330,911,275
+20350,910,276
+20384,910,279
+20404,909,282
+20443,908,289
+20464,908,290
+20560,907,290
+20597,907,290
+20630,907,289
+20665,907,286
+20686,907,285
+20745,907,286
+20777,908,288
+20798,911,288
+20835,923,283
+20854,937,272
+20891,955,247
+20913,964,231
+20947,973,211
+20968,980,196
+21004,986,175
+21021,989,166
+21054,990,142
+21071,989,135
+21100,988,129
+21120,987,129
+21156,983,131
+21177,980,134
+21213,978,137
+21232,977,142
+21270,974,158
+21292,974,162
+21326,969,169
+21348,969,172
+21381,967,177
+21402,965,180
+21436,965,181
+21494,965,179
+21516,964,179
+21554,959,178
+21572,957,178
+21607,949,181
+21629,948,182
+21666,947,183
+21686,947,184
+21723,942,185
+21745,936,185
+21778,920,183
+21797,910,181
+21833,892,180
+21854,890,181
+21889,889,181
+21918,889,181
+21954,895,186
+21973,903,189
+22008,922,198
+22029,928,200
+22069,930,200
+22091,928,200
+22128,909,192
+22149,902,191
+22182,895,191
+22205,894,191
+22251,893,191
+22278,894,193
+22317,894,194
+22338,894,194
+22375,895,194
+22396,896,193
+22433,900,188
+22454,902,187
+22492,915,187
+22513,920,188
+22550,924,190
+22571,930,195
+22609,949,212
+22631,962,219
+22667,979,225
+22686,991,226
+22717,1000,227
+22734,1004,228
+22770,1005,228
+22790,1005,230
+22821,1005,231
+22850,1005,231
+22886,1003,231
+22906,1001,231
+22943,985,232
+22964,975,231
+23003,959,230
+23025,956,231
+23063,954,233
+23084,954,233
+23116,954,235
+23135,956,236
+23174,956,239
+23195,956,241
+23231,954,244
+23250,953,244
+23288,948,242
+23309,941,239
+23347,918,228
+23366,906,226
+23404,898,224
+23424,894,223
+23460,892,221
+23480,890,221
+23513,890,222
+23530,891,224
+23568,893,229
+23589,894,232
+23625,905,245
+23646,912,248
+23682,930,248
+23704,932,248
+23742,931,247
+23760,930,247
+23793,928,248
+23814,927,249
+23854,926,251
+23876,926,252
+23908,926,252
+23927,926,252
+23967,926,254
+23989,926,255
+24027,928,258
+24049,930,259
+24081,932,258
+24102,933,252
+24141,929,240
+24163,929,239
+24213,929,239
+24235,930,239
+24269,940,247
+24290,951,258
+24327,958,264
+24345,958,264
+24385,958,265
+24404,959,266
+24440,959,268
+24463,959,272
+24504,962,283
+24525,963,285
+24559,961,285
+24579,961,285
+24616,955,282
+24638,953,282
+24673,948,279
+24691,944,276
+24724,939,272
+24744,938,272
+24781,937,272
+24802,937,272
+24837,940,272
+24858,943,270
+24898,944,260
+24920,941,257
+24956,933,256
+24978,931,257
+25011,929,262
+25030,929,263
+25072,931,266
+25093,931,266
+25129,924,267
+25150,909,266
+25188,879,261
+25210,847,260
+25245,797,265
+25263,759,274
+25298,682,286
+25318,629,288
+25353,472,278
+25373,412,270
+25407,273,250
+25427,239,243
+25461,187,227
+25481,158,210
+25518,147,198
+25538,147,198
+25568,147,198
+25589,149,198
+25629,174,201
+25649,194,206
+25686,235,222
+25704,252,229
+25738,281,236
+25758,291,237
+25794,296,238
+25824,296,238
+25860,296,242
+25880,296,243
+25934,296,243
+25972,297,243
+25991,297,243
+26026,297,244
+26046,298,247
+26078,301,249
+26098,302,249
+26137,303,246
+26158,303,244
+26195,302,243
+26213,302,243
+26251,300,250
+26271,300,251
+26302,300,251
+26321,303,248
+26358,305,243
+26376,303,240
+26409,302,239
+26431,301,239
+26470,295,240
+26492,293,241
+26529,291,242
+26550,291,242
+26588,287,238
+26609,284,237
+26642,279,235
+26663,277,237
+26702,272,246
+26723,267,254
+26761,262,268
+26782,259,272
+26821,258,273
+26840,257,272
+26877,246,268
+26902,242,268
+26937,242,268
+26955,245,266
+26993,267,263
+27013,274,262
+27046,278,258
+27066,280,251
+27102,279,246
+27149,279,247
+27171,280,250
+27205,289,252
+27224,294,249
+27259,298,239
+27280,298,223
+27315,296,208
+27334,297,202
+27370,303,192
+27391,304,190
+27428,304,189
+27459,304,190
+27494,303,197
+27513,303,197
+27550,302,198
+27567,301,198
+27603,294,200
+27624,284,207
+27662,272,224
+27684,268,233
+27719,270,241
+27740,273,244
+27774,275,248
+27795,274,250
+27831,273,251
+27853,273,251
+27888,284,255
+27907,292,255
+27945,304,256
+27966,305,257
+28003,308,265
+28022,311,272
+28056,327,294
+28077,337,304
+28117,374,334
+28138,400,351
+28173,432,372
+28193,456,389
+28224,480,408
+28241,488,416
+28276,505,439
+28295,514,455
+28329,519,469
+28349,521,475
+28385,528,491
+28405,529,495
+28438,530,497
+28455,533,501
+28492,548,516
+28510,554,522
+28543,566,534
+28563,577,545
+28599,604,575
+28619,610,581
+28654,612,585
+28674,613,588
+28707,615,593
+28727,615,594
+28774,613,589
+28795,608,584
+28828,602,578
+28845,602,578
+28880,599,578
+28900,598,579
+28931,595,579
+28951,593,579
+28982,593,580
+29040,592,579
+29059,587,577
+29094,575,577
+29115,567,578
+29147,558,579
+29167,555,579
+29207,555,579
+29251,555,578
+29271,555,577
+29309,554,575
+29330,550,573
+29369,549,573
+29420,552,574
+29446,571,577
+29494,615,569
+29517,627,561
+29554,635,547
+29578,635,545
+29634,635,545
+29672,629,540
+29692,623,535
+29728,608,523
+29746,606,522
+29778,600,518
+29799,597,514
+29840,597,514
+29859,598,516
+29896,600,524
+29917,600,527
+29953,600,529
+29974,600,529
+30011,605,517
+30033,606,509
+30068,606,508
+30089,606,509
+30121,605,517
+30142,604,522
+30181,603,527
+30200,604,529
+30244,606,530
+30269,608,529
+30310,609,528
+30331,609,528
+30368,605,533
+30389,602,535
+30428,591,536
+30447,580,532
+30484,558,515
+30505,540,498
+30540,513,471
+30559,491,451
+30591,460,424
+30609,442,408
+30647,402,372
+30667,387,359
+30702,348,332
+30722,333,321
+30758,306,303
+30779,289,292
+30812,273,282
+30829,267,278
+30864,254,275
+30888,243,274
+30933,224,272
+30953,217,270
+30984,214,269
+31002,214,269
+31047,215,269
+31078,216,267
+31096,216,267
+31143,215,267
+31162,214,268
+31196,213,271
+31213,213,273
+31250,216,274
+31272,217,272
+31311,219,261
+31332,218,257
+31366,217,256
+31398,218,257
+31436,220,263
+31457,221,264
+31492,224,262
+31513,229,258
+31552,230,247
+31574,229,242
+31612,228,237
+31633,227,232
+31670,230,229
+31689,237,224
+31729,254,212
+31756,267,207
+31791,289,208
+31812,292,209
+31845,291,211
+31865,286,221
+31902,280,236
+31924,279,244
+31962,280,248
+31984,289,248
+32021,305,242
+32043,312,237
+32083,313,236
+32139,313,238
+32158,312,238
+32192,312,239
+32224,312,239
+32264,316,235
+32285,317,232
+32323,317,224
+32344,317,223
+32392,317,225
+32430,318,235
+32451,318,238
+32586,317,239
+32609,313,244
+32644,301,254
+32665,295,257
+32707,290,257
+32728,288,256
+32764,285,255
+32784,284,255
+32823,287,261
+32845,290,264
+32883,314,264
+32904,338,257
+32940,372,245
+32961,398,238
+32999,454,238
+33020,478,240
+33054,524,239
+33074,558,236
+33112,620,231
+33132,645,229
+33168,707,224
+33189,733,225
+33223,784,222
+33243,800,221
+33279,840,223
+33296,851,225
+33332,867,227
+33353,873,226
+33394,891,226
+33416,898,227
+33452,899,227
+33480,899,228
+33512,900,233
+33530,901,236
+33569,901,237
+33629,900,237
+33647,898,237
+33680,896,238
+33697,896,239
+33737,894,240
+33758,893,240
+33798,890,243
+33819,887,247
+33856,882,254
+33877,882,256
+33911,882,257
+33929,882,257
+33969,883,257
+33990,883,258
+34027,884,261
+34049,884,263
+34089,884,265
+34111,886,265
+34147,893,263
+34169,902,258
+34207,923,233
+34229,929,223
+34265,944,203
+34290,951,189
+34325,955,170
+34346,955,159
+34382,955,155
+34413,954,155
+34449,952,165
+34471,951,175
+34506,949,189
+34527,950,195
+34564,951,197
+34586,952,198
+34627,954,199
+34649,955,202
+34685,955,214
+34704,955,221
+34737,956,223
+34758,958,223
+34798,959,223
+34817,959,222
+34854,957,222
+34874,955,223
+34914,953,228
+34938,954,231
+34973,959,238
+34994,964,244
+35030,968,249
+35049,967,250
+35091,965,252
+35113,962,258
+35149,958,267
+35171,956,270
+35209,956,271
+35265,956,271
+35299,949,266
+35319,943,264
+35353,940,263
+35373,937,263
+35414,932,263
+35438,928,263
+35469,921,260
+35490,914,248
+35530,905,223
+35552,904,207
+35585,908,198
+35604,910,196
+35638,915,192
+35658,918,191
+35696,920,190
+35750,920,193
+35768,921,195
+35802,925,201
+35821,928,202
+35864,941,208
+35886,945,211
+35924,946,219
+35945,944,228
+35983,942,244
+36005,942,248
+36044,942,250
+36062,942,250
+36100,939,248
+36122,926,243
+36163,914,243
+36185,907,243
+36223,893,243
+36243,891,243
+36282,890,243
+36304,889,244
+36341,887,246
+36360,886,248
+36403,885,253
+36425,882,259
+36459,878,268
+36481,874,272
+36515,854,275
+36535,818,275
+36569,756,275
+36589,728,279
+36625,660,296
+36643,635,302
+36673,589,309
+36690,573,311
+36731,555,313
+36757,554,313
+36843,555,313
+36863,559,316
+36897,566,322
+36917,573,325
+36957,597,329
+36981,615,326
+37030,633,316
+37054,633,314
+37095,634,314
+37117,634,315
+37153,633,316
+37174,633,317
+37212,635,319
+37233,637,319
+37274,639,318
+37295,639,315
+37334,633,311
+37355,628,310
+37389,623,311
+37407,621,313
+37443,620,315
+37461,620,316
+37497,621,317
+37515,623,318
+37555,626,317
+37576,626,317
+37610,627,320
+37630,628,324
+37667,629,325
+37687,630,326
+37722,633,326
+37742,634,325
+37779,634,324
+37801,634,323