├── .gitignore ├── .openworm.yml ├── .travis.yml ├── README.md ├── WCON_format.md ├── converters └── MultiWormTracker │ ├── OldMwtToWcon.scala │ ├── README.md │ ├── build.sbt │ └── project │ └── assembly.sbt ├── discuss └── Formats.md ├── src ├── LICENSE.md ├── Matlab │ ├── +wcon │ │ ├── +meta │ │ │ ├── lab.m │ │ │ └── software.m │ │ ├── +sl │ │ │ ├── +cellstr │ │ │ │ └── join.m │ │ │ ├── +datetime │ │ │ │ ├── datenum8601.m │ │ │ │ └── license_datenum8601.txt │ │ │ ├── +in │ │ │ │ ├── NULL.m │ │ │ │ ├── processVarargin.m │ │ │ │ ├── process_varargin_result.m │ │ │ │ └── propValuePairsToStruct.m │ │ │ └── +struct │ │ │ │ ├── setField.c │ │ │ │ ├── setField.m │ │ │ │ └── setField.mexw64 │ │ ├── +utils │ │ │ ├── fex │ │ │ │ ├── TestfRMField.m │ │ │ │ └── fRMField_license.txt │ │ │ ├── jsonopt.m │ │ │ ├── mergestruct.m │ │ │ ├── readZipData.m │ │ │ ├── rmfield.c │ │ │ ├── rmfield.m │ │ │ ├── rmfield.mexw64 │ │ │ ├── savejson.m │ │ │ ├── varargin2struct.m │ │ │ └── zip_file.m │ │ ├── @data │ │ │ └── data.m │ │ ├── @dataset │ │ │ ├── dataset.m │ │ │ ├── fromFile.m │ │ │ └── save.m │ │ ├── @load_options │ │ │ └── load_options.m │ │ ├── @metadata │ │ │ └── metadata.m │ │ ├── @units │ │ │ └── units.m │ │ ├── NULL.m │ │ ├── compile_code.m │ │ └── load.m │ ├── ReadMe.md │ ├── docs │ │ ├── TODO_List.txt │ │ └── design_decisions.txt │ └── tests │ │ ├── +wcon_tests │ │ ├── +tracker_commons │ │ │ └── @base │ │ │ │ └── base.m │ │ └── @tracker_commons │ │ │ └── tracker_commons.m │ │ ├── MRC_test.m │ │ └── test_shared_files.m ├── Python │ ├── INSTALL.md │ ├── README.md │ ├── examples │ │ ├── truncate_wcon.py │ │ └── view_wcon.py │ ├── setup.py │ ├── temp_example.py │ ├── tests │ │ ├── .bbb.wcon.swp │ │ ├── diagnostic_test.py │ │ └── tests.py │ └── wcon │ │ ├── __init__.py │ │ ├── measurement_unit.py │ │ ├── version.py │ │ ├── wcon_data.py │ │ └── wcon_parser.py ├── R │ ├── README.md │ ├── build.sbt │ ├── project │ │ └── assembly.sbt │ └── wcon-ready.r ├── Rust │ └── README.md ├── java │ ├── README.md │ ├── Wcons.java │ ├── build.sbt │ └── project │ │ └── assembly.sbt ├── julia │ ├── README.md │ └── src │ │ ├── CommonWorm.jl │ │ ├── DataSet.jl │ │ ├── MetaData.jl │ │ ├── Minimal.jl │ │ ├── ReadBasic.jl │ │ ├── ReadWrite.jl │ │ ├── TrackerCommons.jl │ │ └── Units.jl ├── octave │ ├── INSTALL.md │ ├── README.md │ ├── README.md.OLD │ ├── examples │ │ └── main.m │ ├── externals │ │ └── README.md │ ├── library │ │ └── WCONWorms.m │ ├── tests │ │ └── README.md │ └── wrappers │ │ ├── IDEAS.md │ │ ├── Makefile │ │ ├── NOTES.md │ │ ├── driver.cpp │ │ ├── driver.m │ │ ├── driver.py │ │ ├── example-octave │ │ ├── Makefile │ │ ├── README.md │ │ ├── example.c │ │ ├── example.h │ │ └── swig-example.template │ │ ├── example-python │ │ ├── Makefile │ │ ├── README.md │ │ ├── driver.cpp │ │ ├── octaveWconPythonWrapper.cpp │ │ ├── octaveWconPythonWrapper.h │ │ ├── pythonLib │ │ │ ├── __init__.py │ │ │ └── octaveWconTest.py │ │ └── pythonTest.py │ │ ├── extra-test-data │ │ ├── README.md │ │ ├── minimax-conflict.wcon │ │ └── minimax-mergeable.wcon │ │ ├── octaveWconPythonWrapper.cpp │ │ ├── octaveWconPythonWrapper.h │ │ ├── swigWrapper.c │ │ ├── swigWrapper.h │ │ ├── test-octave-exceptions │ │ ├── Makefile │ │ ├── README.md │ │ ├── exception_order_runme.m │ │ ├── minimal_c.i │ │ ├── minimal_c.m │ │ ├── minimal_cpp.i │ │ ├── minimal_cpp.m │ │ └── test_exception.i │ │ ├── wcon-oct-swig.template │ │ ├── wconOct_wrapperMeasurementUnit.cpp │ │ ├── wconOct_wrapperWCONWorms.cpp │ │ ├── wrapperInternal.cpp │ │ ├── wrapperInternal.h │ │ └── wrapperTypes.h └── scala │ ├── README.md │ ├── build.sbt │ └── src │ ├── examples │ ├── CircleTravel.scala │ ├── CountAnimals.scala │ ├── ExampleTemplate.scala │ └── MovementSelect.scala │ ├── main │ └── scala │ │ ├── Compatibility.scala │ │ ├── Create.scala │ │ ├── Custom.scala │ │ ├── Data.scala │ │ ├── DataSet.scala │ │ ├── FileSet.scala │ │ ├── Metadata.scala │ │ ├── ReadWrite.scala │ │ ├── Reshape.scala │ │ ├── UnitMap.scala │ │ ├── Units.scala │ │ └── WconImplicits.scala │ ├── minimal │ ├── MinimalReader.scala │ ├── MinimalWorm.scala │ └── MinimalWriter.scala │ └── test │ └── scala │ └── TestWcon.scala ├── tests ├── data │ ├── centroid.wcon │ ├── centroids.wcon │ ├── length-one-arrayed-single-array-xy.wcon │ ├── length-one-arrayed.wcon │ ├── offset.wcon │ ├── offsets.wcon │ ├── perimeter.wcon │ ├── pixelwalk.wcon │ ├── singleton-xy.wcon │ ├── spine-head-left.wcon │ ├── spine-head-right.wcon │ ├── spine-head-unknown.wcon │ ├── spine-ventral-ccw.wcon │ ├── spine-ventral-cw.wcon │ ├── spine-ventral-unknown.wcon │ ├── spine.wcon │ ├── string-id.wcon │ ├── two-ids.wcon │ ├── two-times-arrayed-bare-xy.wcon │ ├── two-times-arrayed.wcon │ └── two-times-separate.wcon ├── examples │ ├── all_movements.wcon │ ├── all_times.wcon │ └── count_animals.wcon ├── intermediate.wcon ├── intermediatezipped.wcon.zip ├── maximal_0.wcon ├── maximal_1.wcon ├── maximal_2.wcon ├── maximalzipped.wcon.zip ├── metadata │ ├── all-metadata.wcon │ ├── alt-arena-two-dimensions.wcon │ ├── alt-one-protocol.wcon │ ├── alt-one-who.wcon │ ├── alt-two-labs.wcon │ ├── alt-two-softwares.wcon │ ├── just-age.wcon │ ├── just-arena.wcon │ ├── just-food.wcon │ ├── just-humidity.wcon │ ├── just-id.wcon │ ├── just-interpolate.wcon │ ├── just-lab.wcon │ ├── just-media.wcon │ ├── just-protocol.wcon │ ├── just-sex.wcon │ ├── just-software.wcon │ ├── just-stage.wcon │ ├── just-strain.wcon │ ├── just-temperature.wcon │ ├── just-timestamp.wcon │ ├── just-who.wcon │ └── no-metadata.wcon ├── minimal.wcon ├── minimalzipped.wcon.zip ├── minimax.wcon ├── minimaxzipped.wcon.zip ├── multiworm.wcon ├── offset_and_centroid.wcon ├── offset_no_centroid_yes.wcon ├── offset_none.wcon ├── offset_only.wcon ├── perimeter_points.wcon └── units │ ├── angle │ ├── degrees.wcon │ ├── degrees2.wcon │ ├── degrees3.wcon │ ├── milliradians.wcon │ ├── milliradians2.wcon │ ├── milliradians3.wcon │ ├── radians.wcon │ ├── radians2.wcon │ └── radians3.wcon │ ├── custom │ └── q-is-one.wcon │ ├── length │ ├── foot.wcon │ ├── foot2.wcon │ ├── inch.wcon │ ├── inch2.wcon │ ├── meter.wcon │ ├── meter2.wcon │ ├── meter3.wcon │ ├── micron.wcon │ ├── micron2.wcon │ ├── micron3.wcon │ ├── micron4.wcon │ ├── millimeter.wcon │ ├── millimeter2.wcon │ ├── millimeter3.wcon │ └── millimeter4.wcon │ ├── si │ ├── bare.wcon │ ├── centi.wcon │ ├── centi2.wcon │ ├── giga.wcon │ ├── giga2.wcon │ ├── kilo.wcon │ ├── kilo2.wcon │ ├── mega.wcon │ ├── mega2.wcon │ ├── micro.wcon │ ├── micro2.wcon │ ├── milli.wcon │ ├── milli2.wcon │ ├── nano.wcon │ └── nano2.wcon │ ├── temperature │ ├── celsius.wcon │ ├── celsius2.wcon │ ├── celsius3.wcon │ ├── centigrade.wcon │ ├── centigrade2.wcon │ ├── fahrenheit.wcon │ ├── fahrenheit2.wcon │ ├── fahrenheit3.wcon │ ├── kelvin.wcon │ ├── kelvin2.wcon │ └── kelvin3.wcon │ ├── time │ ├── centisecond.wcon │ ├── centisecond2.wcon │ ├── centisecond3.wcon │ ├── day.wcon │ ├── day2.wcon │ ├── day3.wcon │ ├── hour.wcon │ ├── hour2.wcon │ ├── hour3.wcon │ ├── hour4.wcon │ ├── minute.wcon │ ├── minute2.wcon │ ├── minute3.wcon │ ├── second.wcon │ ├── second2.wcon │ └── second3.wcon │ └── unitless │ ├── fraction.wcon │ └── percent.wcon └── wcon_schema.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Random editor stuff 2 | *~ 3 | 4 | # Python byte-compiled / optimized / DLL files 5 | *.pyc 6 | __pycache__/* 7 | src/Python/build/* 8 | src/Python/dist/* 9 | src/Python/tests/test_chunk* 10 | src/Python/wcon.egg* 11 | .pypirc 12 | 13 | #Matlab 14 | *.asv 15 | 16 | # Scala 17 | src/scala/target/* 18 | -------------------------------------------------------------------------------- /.openworm.yml: -------------------------------------------------------------------------------- 1 | --- 2 | repo: openworm/tracker-commons 3 | shortDescription: "Compilation of information and code bases related to open-source trackers for C. elegans" 4 | coordinator: mcurrie@openworm.org 5 | documentation: https://github.com/openworm/tracker-commons/blob/master/README.md 6 | gitter: https://gitter.im/openworm/open-worm-analysis-toolbox 7 | keywords: 8 | - data 9 | languages: 10 | - scala 11 | - python 12 | - java 13 | - r 14 | - julia 15 | - octave 16 | - matlab 17 | - c 18 | - c++ 19 | latest-release: 20 | - beta 1.1.0 21 | members: 22 | - cheelee@openworm.org 23 | outputs: 24 | - openworm/sibernetic 25 | - openworm/org.geppetto 26 | - openworm/open-worm-analysis-toolbox 27 | parent: 28 | - openworm/open-worm-analysis-toolbox 29 | tests: true 30 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | 3 | python: 4 | # conda doesn't seem to work with 2.6 so let's forget about testing it 5 | #- 2.6 6 | - 2.7 7 | # test_save_and_load fails on minimax.wcon with Python 3.3, haven't had change to look into it 8 | #- 3.3 9 | - 3.4 10 | - 3.5 11 | - 3.6 12 | 13 | notifications: 14 | email: false 15 | 16 | before_install: 17 | - pip install --upgrade pip 18 | - pip install six 19 | - pip install pep8 20 | 21 | - which python 22 | - python --version 23 | 24 | # Anaconda installation 25 | - | 26 | if [[ ($TRAVIS_PYTHON_VERSION == 2.6) || ($TRAVIS_PYTHON_VERSION == 2.7) ]]; then 27 | MINICONDA_DIR=/home/travis/miniconda2 28 | wget http://repo.continuum.io/miniconda/Miniconda2-latest-Linux-x86_64.sh -O miniconda.sh 29 | else 30 | MINICONDA_DIR=/home/travis/miniconda3 31 | wget http://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh 32 | fi 33 | - chmod +x miniconda.sh 34 | - ./miniconda.sh -b 35 | - export PATH=$MINICONDA_DIR/bin:$PATH 36 | 37 | # Need to install the actual Python version, does this work? 38 | # To get the correct version you have to do a conda install 39 | # Old versions are at: http://repo.continuum.io/miniconda/ but don't 40 | # match Python versions and it seems like continuum doesn't want you 41 | # to try and match 42 | - conda install --yes python=$TRAVIS_PYTHON_VERSION numpy scipy pandas jsonschema psutil 43 | 44 | # Diagnostic information 45 | - which python 46 | - python --version 47 | - pydoc modules 48 | 49 | before_script: 50 | - cd $TRAVIS_BUILD_DIR/tests 51 | # Supplement our folder of test files with some larger ones we don't store directly in the GitHub repo 52 | # Download files from Dropbox because it is impossible to download files directly from Google Drive 53 | # if they are larger than the size at which Google warns that it can't scan for viruses; this popup 54 | # means we don't get the file using wget. Dropbox works like a charm, however. 55 | # (See http://stackoverflow.com/questions/25010369/) 56 | - wget "https://www.dropbox.com/s/gda92wl2hdoiu8y/testfile_new.wcon?dl=1" -O from_schafer_lab.wcon 57 | - wget "https://www.dropbox.com/s/apjyc9lh1t7646q/XJ30_NaCl500mM4uL6h_10m45x10s40s_Ea.wcon?dl=1" -O from_kerr.wcon 58 | # Zip the downloaded files so zipped versions can also be tested 59 | - zip from_schafer_lab_zipped.wcon.zip from_schafer_lab.wcon 60 | - zip from_kerr_zipped.wcon.zip from_kerr.wcon 61 | - ls -l 62 | - cd $TRAVIS_BUILD_DIR 63 | 64 | script: 65 | # Run pep8 on all .py files in all subfolders 66 | # We must ignore E402 module level import not at top of file 67 | # because of use case sys.path.append('..'); import 68 | - find . -name \*.py -exec pep8 --ignore=E402 {} + 69 | # NOTE: To correct any errors in the above pep8 check, run the 70 | # following to clear out about 80% of the issues automatically: 71 | # # Get the latest version of the repo 72 | # git pull 73 | # # Run autopep8 on all .py files in all subfolders 74 | # find . -name \*.py -exec autopep8 --recursive --aggressive --aggressive --in-place {} + 75 | # # Push these changes to GitHub 76 | # git commit -a -m "autopep8" 77 | # git push 78 | 79 | - cd $TRAVIS_BUILD_DIR/src/Python/tests/ 80 | - pwd 81 | - python -m unittest discover 82 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tracker Commons 2 | 3 | The Tracker Commons is a repository of documentation, software, and discussions that enable labs to share their worm tracking data and analysis routines. 4 | 5 | ## The WCON format 6 | 7 | The Worm tracker Commons Object Notation (WCON) file format provides a simple way to share _C. elegans_ tracking data between labs and analysis packages. 8 | 9 | WCON uses the widely-supported [JSON](http://json.org) file format, so almost any language can easily import the data. 10 | WCON specifies a few entries that you should include in a JSON "Object" (a.k.a. a map or dictionary) that will let 11 | readers easily extract basic information about worms. 12 | 13 | Here's an example! 14 | 15 | ```JSON 16 | { 17 | "metadata":{ 18 | "who":"Open Worm", 19 | "timestamp":"2016-01-22T17:44:48", 20 | "protocol":"Numbers made up by hand for an example!" 21 | }, 22 | "units":{"t":"s", "x":"mm", "y":"mm"}, 23 | "data":[ 24 | {"id":"worm1", "t":[0.1], "x":[[0.33, 0.65, 0.8, 1.1, 1.2]], "y":[[2.31, 2.25, 2.0, 1.87, 1.66]]}, 25 | {"id":"worm1", "t":[0.3], "x":[[0.27, 0.6, 0.75, 1.0, 1.1]], "y":[[2.4, 2.3, 2.07, 1.78, 1.75]]} 26 | ] 27 | } 28 | ``` 29 | 30 | This file contains data for a single worm over two timepoints. The worm's body is represented as 31 | five points along the worm's spine. 32 | 33 | ## Tracker Commons software for reading and writing WCON files 34 | 35 | The Tracker Commons repository contains implementations for reading and writing WCON data for a variety of languages. Browse the `src` directory to learn more! 36 | 37 | | Language | Principal Author | Capabilities | Test Status | Packaged? | 38 | | ------------- | ------------- | ------------- | ----------------- |------| 39 | | [Scala](src/scala) | Rex Kerr | Feature-complete, beta | [![Build Status](https://semaphoreci.com/api/v1/ichoran/tracker-commons/branches/master/badge.svg)](https://semaphoreci.com/ichoran/tracker-commons) | no | 40 | | [Python](src/Python) | Michael Currie | Feature-complete, beta | [![Build Status](https://travis-ci.org/openworm/tracker-commons.svg?branch=master)](https://travis-ci.org/openworm/tracker-commons) | [![PyPI package](https://badge.fury.io/py/wcon.svg)](http://badge.fury.io/py/wcon) | 41 | | [Java](src/java) | Rex Kerr | Feature-complete, beta | none | no | 42 | | [Matlab](src/Matlab) | Jim Hokanson | Runnable implementation (alpha) | n/a | no | 43 | | [R](src/R) | Rex Kerr | Lightweight wrapper using rscala (alpha) | none | no | 44 | | [Julia](src/julia) | Rex Kerr | Runnable implementation (alpha) | none | no | 45 | | [Octave](src/octave) | Chee Wai Lee | Lightweight wrapper to Python version | none | no | 46 | | [Rust](src/Rust) | n/a | Preliminary notes only | n/a | no | 47 | -------------------------------------------------------------------------------- /converters/MultiWormTracker/README.md: -------------------------------------------------------------------------------- 1 | # Multi-Worm Tracker Converter 2 | 3 | The Multi-Worm Tracker is software by Rex Kerr and colleagues that tracks 4 | multiple animals in real time and outputs only animal contours. The 5 | original version of the software is described in Swierczek et al., Nature 6 | Methods v. 8, pp. 592-598 (2011). 7 | 8 | The original version of the software produces a custom text-based output 9 | format which is largely compatible with the WCON format. 10 | 11 | Here, a command-line converter is provided that is based upon the Scala 12 | version of WCON. 13 | 14 | ## Compiling 15 | 16 | Make sure you have `sbt` installed (see Scala WCON converter for more 17 | details). Then run `sbt assembly` to produce a single jar that contains the 18 | converter and all dependencies. 19 | 20 | ## Running 21 | 22 | The converter can be run with 23 | 24 | ``` 25 | java -jar target/scala-2.12/old-mwt-to-wcon-assembly-0.1.0.jar to/ 20150131_114112 26 | ``` 27 | 28 | where `to/` is a path for where to place the output and where the numeric 29 | code is a directory for the Multi-Worm Tracker using its old (v. 1.3 and 30 | before) format. It can also be a zip file. 31 | 32 | If multiple input files or directories are specified, they'll all be 33 | converted separately and the results will be placed in the `to/` directory. 34 | 35 | By default, the files will be chunked in time and will contain at most 36 | approximately 100,000 data points, and all output files will be placed in a 37 | single `.zip` file. 38 | 39 | A pixel size of 0.026 mm per pixel is assumed. If this is not the correct 40 | value, set it with the `--pixel-size` option. For instance, for 0.0372 mm 41 | per pixel, use `--pixel-size=0.0327`. 42 | 43 | If the output should have some other maximum number of points, use 44 | `--max-data=1234` where `1234` is the maximum number of data points to 45 | be included. `0` is equivalent to infinity (all data in one file). 46 | 47 | By default, the summary image will be copied. To avoid this, use 48 | `--no-image`. 49 | 50 | If you want to elide data from potential animals that are not tracked for 51 | very long, use `--tracked-for=0.2` to, for example, set a minimum time of 52 | 0.2 seconds. 53 | 54 | If you want to elide data from potential animals that do not move very far, 55 | use `--must-move=0.5` to insist that they move at least 0.5 mm. 56 | 57 | If you want the data to go somewhere other than next to the directory/.zip 58 | file it was from, use `--to=/path/to/new/directory` (use quotes or escapes 59 | to handle spaces in the path). All output files will go there. 60 | 61 | Note that the input data must all fit in memory simultaneously. You should 62 | use flags like `-Xmx3G` to the java command to increase the memory as needed 63 | (shown here, 3 gigabytes maximum allowed). 64 | 65 | Note: you can also run directly from `sbt` using `sbt -J-Xmx3G 'run to/ 66 | 20150131_1141'` or the like. 67 | 68 | ## What is preserved and what is lost 69 | 70 | Event times are preserved in a structure that is TBD (i.e. they are not yet 71 | but will be when this is complete). 72 | 73 | Centroids, skeletons, and outlines are preserved as written. 74 | 75 | Areas are preserved under the `"@XJ"` tag for every data point in the 76 | format, `"@XJ": { "area": [0.01715, 0.01694, ...] }`, i.e. one point per 77 | timepoint. 78 | 79 | Everything else is discarded. 80 | -------------------------------------------------------------------------------- /converters/MultiWormTracker/build.sbt: -------------------------------------------------------------------------------- 1 | resolvers += "Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots" 2 | 3 | lazy val wcon = RootProject(file("../../src/scala")) 4 | 5 | // The main project 6 | lazy val main = (project in file(".")). 7 | settings( 8 | scalaVersion := "2.12.1", 9 | scalacOptions ++= Seq("-unchecked", "-feature", "-deprecation"), 10 | libraryDependencies += "com.novocode" % "junit-interface" % "0.9" % "test", 11 | libraryDependencies += "com.github.ichoran" %% "kse" % "0.6-SNAPSHOT", 12 | name := "old-mwt-to-wcon", 13 | version := "0.1.0", 14 | mainClass in Compile := Some( 15 | "org.openworm.trackercommons.converters.mwt.OldMwtToWcon" 16 | ) 17 | ). 18 | dependsOn(wcon) 19 | -------------------------------------------------------------------------------- /converters/MultiWormTracker/project/assembly.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.5") 2 | -------------------------------------------------------------------------------- /src/LICENSE.md: -------------------------------------------------------------------------------- 1 | ### License 2 | 3 | Copyright © 2015 by 4 | Andre Brown, 5 | Rex Kerr, 6 | Calico Life Sciences, 7 | Michael Currie, 8 | Chee Wai Lee, 9 | OpenWorm Foundation (Delaware #5787527) 10 | 11 | The software component of **tracker_commons**, like all software that is 12 | part of the [OpenWorm](https://github.com/openworm) project, is free 13 | software under the [MIT license](http://opensource.org/licenses/MIT). 14 | 15 | ------------- 16 | 17 | ### The MIT License 18 | 19 | Permission is hereby granted, free of charge, to any person obtaining a 20 | copy of this software and associated documentation files (the 21 | "Software"), to deal in the Software without restriction, including 22 | without limitation the rights to use, copy, modify, merge, publish, 23 | distribute, sublicense, and/or sell copies of the Software, and to 24 | permit persons to whom the Software is furnished to do so, subject to 25 | the following conditions: 26 | 27 | The above copyright notice and this permission notice shall be included 28 | in all copies or substantial portions of the Software. 29 | 30 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 31 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 32 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 33 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 34 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 35 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 36 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 37 | -------------------------------------------------------------------------------- /src/Matlab/+wcon/+meta/lab.m: -------------------------------------------------------------------------------- 1 | classdef lab < json.objs.dict 2 | % 3 | % Class: 4 | % wcon.meta.lab 5 | % 6 | 7 | %{ 8 | properties 9 | location = wcon.NULL 10 | %to specify physical location or address 11 | 12 | name = wcon.NULL 13 | %to indicate the name of the laboratory as necessary 14 | 15 | PI = wcon.NULL 16 | %to indicate the principal investigator of that lab 17 | 18 | address = wcon.NULL 19 | %temp 20 | end 21 | %} 22 | 23 | methods 24 | function obj = lab() 25 | n = wcon.NULL; 26 | s.location = n; 27 | s.name = n; 28 | s.PI = n; 29 | s.address = n; 30 | obj.props = s; 31 | end 32 | end 33 | 34 | methods (Static) 35 | function objs = fromFile(m) 36 | % 37 | % Inputs 38 | % ------ 39 | % m : json 40 | % 41 | 42 | temp = m.getParsedData; 43 | n_objs = length(temp); 44 | objs(n_objs) = wcon.meta.lab; 45 | if iscell(temp) 46 | for iObj = 1:n_objs 47 | cur_obj = objs(iObj); 48 | cur_obj.props = temp{iObj}; 49 | end 50 | elseif length(temp) > 1 51 | for iObj = 1:n_objs 52 | cur_obj = objs(iObj); 53 | cur_obj.props = temp(iObj); 54 | end 55 | else 56 | objs.props = temp; 57 | end 58 | end 59 | end 60 | 61 | end 62 | 63 | -------------------------------------------------------------------------------- /src/Matlab/+wcon/+meta/software.m: -------------------------------------------------------------------------------- 1 | classdef software < json.objs.dict 2 | % 3 | % Class: 4 | % wcon.meta.software 5 | 6 | properties 7 | name = wcon.NULL 8 | version = wcon.NULL 9 | featureID = wcon.NULL 10 | commit_hash = wcon.NULL 11 | end 12 | 13 | methods 14 | function obj = software() 15 | n = wcon.NULL; 16 | s.version = n; 17 | s.name = n; 18 | s.featureID = n; 19 | s.commit_hash = n; 20 | obj.props = s; 21 | end 22 | end 23 | 24 | methods (Static) 25 | function objs = fromFile(m) 26 | % 27 | % obj = wcon.meta.software.fromFile(m) 28 | 29 | temp = m.getParsedData; 30 | n_objs = length(temp); 31 | objs(n_objs) = wcon.meta.software; 32 | if iscell(temp) 33 | for iObj = 1:n_objs 34 | cur_obj = objs(iObj); 35 | cur_obj.props = temp{iObj}; 36 | end 37 | elseif length(temp) > 1 38 | for iObj = 1:n_objs 39 | cur_obj = objs(iObj); 40 | cur_obj.props = temp(iObj); 41 | end 42 | else 43 | objs.props = temp; 44 | endd 45 | end 46 | end 47 | end 48 | 49 | end 50 | 51 | -------------------------------------------------------------------------------- /src/Matlab/+wcon/+sl/+cellstr/join.m: -------------------------------------------------------------------------------- 1 | function str = join(cellstr_input,varargin) 2 | %x Joins elements of a cellstr together into a single string 3 | % 4 | % str = sl.cellstr.join(cellstr_input,varargin) 5 | % 6 | % This function joins a cell array of strings together given a delimiter. 7 | % 8 | % Inputs: 9 | % ------- 10 | % cellstr: 11 | % A cell array of strings to combine. 12 | % 13 | % Optional Inputs: 14 | % ---------------- 15 | % d: (default ',') 16 | % Delimiter string to use in combining strings 17 | % 18 | % To treat a delimiter as a literal escape the backlash with a 19 | % backlash. For example, this '\\t' will join strings with '\t' 20 | % instead of a tab. Percents should be escaped with a percent. 21 | % 22 | % The final delimiter is computed as by => final_delimeter = sprintf(d) 23 | % keep_rows : (default false) 24 | % If true, rows are escaped via \n instead of with the delimeter 25 | % 26 | % remove_empty: (default false) 27 | % If true empty values are removed. 28 | % 29 | % Examples: 30 | % --------- 31 | % cellstr_input = {'this' 'is' 'a' 'test'}; 32 | % str = sl.cellstr.join(cellstr_input,'d',' ') %use space as delimiter 33 | % 34 | % str => 'this is a test' 35 | % 36 | % Notes: 37 | % ------ 38 | % In 2013a Matlab introduced strjoin() which does something similar 39 | % although the implementation was subpar. 40 | 41 | in.d = ','; 42 | in.keep_rows = false; 43 | in.remove_empty = false; 44 | in = wcon.sl.in.processVarargin(in,varargin); 45 | 46 | if isempty(cellstr_input) 47 | str = ''; 48 | elseif ~iscell(cellstr_input) 49 | error('Input to %s must be a cell array',mfilename) 50 | else 51 | n_columns = size(cellstr_input,2); 52 | 53 | if in.keep_rows 54 | %We need to order by row, not by column, but then we still want the 55 | %row vector for below 56 | P = cellstr_input'; 57 | P = P(:)'; 58 | else 59 | P = cellstr_input(:)'; 60 | end 61 | if in.remove_empty 62 | P(cellfun('isempty',P)) = []; 63 | if isempty(P) 64 | str = ''; 65 | return 66 | end 67 | end 68 | 69 | P(2,:) = {sprintf(in.d)} ; %Added on printing to handle things like \t and \n 70 | 71 | if in.keep_rows 72 | P(2,n_columns:n_columns:end) = {sprintf('\n')}; 73 | end 74 | 75 | P{2,end} = [] ; 76 | str = sprintf('%s',P{:}); 77 | end -------------------------------------------------------------------------------- /src/Matlab/+wcon/+sl/+datetime/license_datenum8601.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Stephen Cobeldick 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in 12 | the documentation and/or other materials provided with the distribution 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 18 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /src/Matlab/+wcon/+sl/+in/NULL.m: -------------------------------------------------------------------------------- 1 | classdef NULL 2 | % 3 | % Class: 4 | % sl.in.NULL 5 | % 6 | % This class can be set as a default option and it will be removed 7 | % when splitting in "sl.in.splitAndProcessVarargin" 8 | % 9 | % This is useful for allowing defaults in subfunctions. 10 | % 11 | % See Also: 12 | % sl.in.splitAndProcessVarargin 13 | % 14 | % Example: 15 | % -------- 16 | % in.test = sl.in.NULL 17 | % in.cheese = 2 18 | % 19 | % TODO: finish call to sl.in.splitAndProcessVarargin 20 | % 21 | % If 'test' is not passed in as an option, it will not exist 22 | % as a field in the 'in' struct following sl.in.splitAndProcessVarargin 23 | end 24 | 25 | -------------------------------------------------------------------------------- /src/Matlab/+wcon/+sl/+in/propValuePairsToStruct.m: -------------------------------------------------------------------------------- 1 | function struct_output = propValuePairsToStruct(cell_input,varargin) 2 | % 3 | % struct_output = sl.in.propValuePairsToStruct(cell_input,varargin) 4 | % 5 | % 6 | % s = struct; 7 | % s.allow_spaces = true; 8 | % struct_output = sl.in.propValuePairsToStruct({'test',1,'test2',2},s) 9 | % 10 | % TODO: Finish documentation 11 | 12 | %TODO: Fix this to allow options ... 13 | 14 | in.allow_spaces = false; 15 | in.force_lower = false; 16 | in.force_upper = false; 17 | 18 | %The checking on this could improve ... 19 | if isempty(varargin) 20 | %pass 21 | elseif isstruct(varargin{1}) 22 | fn = fieldnames(varargin); 23 | for iName = 1:length(fn) 24 | cur_name = fn{iName}; 25 | in.(cur_name) = varargin.(cur_name); 26 | end 27 | elseif iscell(varargin) 28 | for iName = 1:2:length(varargin) 29 | cur_name = varargin{iName}; 30 | cur_value = varargin{iName+1}; 31 | in.(cur_name) = cur_value; 32 | end 33 | end 34 | 35 | %in = sl.in.processVarargin(in,varargin); 36 | 37 | is_str_mask = cellfun('isclass',cell_input,'char'); 38 | 39 | %Improvement: 40 | %------------------------------------------------- 41 | %Analyze calling information ... 42 | %Provide stack trace for editing ... 43 | % 44 | % Functions needed: 45 | % 1) prototype of caller 46 | % 2) calling format of parent 47 | % 3) links to offending lines ... 48 | % 49 | % NOTE: is_parsing_options would allow us to have different 50 | % error messages ... 51 | if ~all(is_str_mask(1:2:end)) 52 | %TODO: See improvement above, provide a clickable link that does 53 | %dbup 3x (up to main, up to caller, up to caller's caller) 54 | error('Unexpected format for varargin, not all properties are strings') 55 | end 56 | if mod(length(cell_input),2) ~= 0 57 | error('Property/value pairs are not balanced, length of input: %d',length(cell_input)) 58 | end 59 | 60 | if in.allow_spaces 61 | %strrep would be faster if we could guarantee 62 | %only single spaces :/ 63 | cell_input(1:2:end) = regexprep(cell_input(1:2:end),'\s+','_'); 64 | end 65 | 66 | 67 | cell_input = cell_input(:)'; %Ensure row vector 68 | if in.force_lower 69 | struct_output = cell2struct(cell_input(2:2:end),lower(cell_input(1:2:end)),2); 70 | elseif in.force_upper 71 | struct_output = cell2struct(cell_input(2:2:end),upper(cell_input(1:2:end)),2); 72 | else 73 | struct_output = cell2struct(cell_input(2:2:end),cell_input(1:2:end),2); 74 | end 75 | 76 | 77 | 78 | end -------------------------------------------------------------------------------- /src/Matlab/+wcon/+sl/+struct/setField.c: -------------------------------------------------------------------------------- 1 | #include "mex.h" 2 | 3 | /* 4 | * 5 | * mex setField.c 6 | * s = struct; 7 | * wtf = setField(s,'wtf batman',5); 8 | * 9 | * %Override test 10 | * wtf = setField(wtf,'wtf batman',1); 11 | * 12 | * %More testing 13 | * wtf.test = 3; 14 | * wtf.nope = 4; 15 | * wtf = setField(wtf,'nope','cheese'); 16 | * wtf = setField(wtf,'! !','wow'); 17 | * wtf = setField(wtf,'test',struct()); 18 | * 19 | */ 20 | 21 | mxArray *mxCreateSharedDataCopy(const mxArray *mx); 22 | #define COPY_ARRAY mxCreateSharedDataCopy 23 | 24 | void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray*prhs[]) 25 | { 26 | 27 | // new_struct = setField(struct,name,value) 28 | 29 | //Use cases 30 | //--------- 31 | //1) Valid field name, just use dynamic - inside Matlab 32 | //2) field name already exists, need to override 33 | //3) Doesn't exist, 34 | 35 | 36 | // Input checking 37 | // -------------- 38 | if (nrhs != 3) { 39 | mexErrMsgIdAndTxt("mexSetField:bad_n_input","3 inputs required for mexSetField"); 40 | } 41 | if (nlhs > 1) { 42 | mexErrMsgIdAndTxt("mexSetField:bad_n_output","1 ouput allowed for mexSetField"); 43 | } 44 | 45 | //Check this before 1st input, because depending on type of 1st input 46 | //we might end early 47 | if (!mxIsChar(prhs[1])){ 48 | mexErrMsgIdAndTxt("mexSetField:bad_type_input","2nd input should be a string"); 49 | } 50 | 51 | // Type of input arguments: Struct or empty matrix 52 | const mxArray *S = prhs[0]; 53 | if (!mxIsStruct(S)) { 54 | //In Matlab we can do: 55 | //s = [] 56 | //s.a = 3; %converts s to a struct 57 | //This is really handy for non-initialized properties 58 | //that become structs on initialization 59 | if (mxIsDouble(S) && mxGetNumberOfElements(S) == 0) { 60 | plhs[0] = mxCreateStructMatrix(1,1,0,NULL); 61 | char *field_to_set = (char *) mxArrayToString(prhs[1]); 62 | mxAddField(plhs[0],field_to_set); 63 | mxSetField(plhs[0],0,field_to_set,COPY_ARRAY(prhs[2])); 64 | mxFree(field_to_set); 65 | return; 66 | } 67 | mexErrMsgIdAndTxt("mexSetField:bad_type_input","1st input should be a struct"); 68 | } 69 | 70 | 71 | 72 | 73 | //Retrieval of inputs 74 | 75 | char *field_to_set = (char *) mxArrayToString(prhs[1]); 76 | mxArray *value_to_set = COPY_ARRAY(prhs[2]); 77 | 78 | mwSize n_existing_fields = mxGetNumberOfFields(S); 79 | //mwSize n_elements = mxGetNumberOfElements(S); 80 | 81 | //2) Does this field exist 82 | 83 | int field_number_of_input = mxGetFieldNumber(S, field_to_set); 84 | 85 | bool field_exists = field_number_of_input >= 0; 86 | 87 | int n_fields_allocate; 88 | 89 | if (field_exists){ 90 | n_fields_allocate = n_existing_fields; 91 | }else{ 92 | field_number_of_input = n_existing_fields; 93 | n_fields_allocate = n_existing_fields + 1; 94 | } 95 | 96 | 97 | 98 | // Get list of field names: 99 | const char **field_list = (const char **) mxMalloc(n_fields_allocate * sizeof(char *)); 100 | if (field_list == NULL) { 101 | mexErrMsgIdAndTxt("mexSetField:fieldnames", "Cannot get memory for fieldnames."); 102 | } 103 | 104 | for (int iField = 0; iField < n_existing_fields; iField++) { 105 | field_list[iField] = mxGetFieldNameByNumber(S, iField); 106 | } 107 | 108 | if (field_exists){ 109 | mxFree(field_to_set); 110 | }else{ 111 | field_list[n_existing_fields] = field_to_set; 112 | } 113 | 114 | //TODO: This should be checked, we aren't supporting 115 | //anything greater than 1 116 | // Create the output struct: 117 | plhs[0] = mxCreateStructArray(mxGetNumberOfDimensions(S), 118 | mxGetDimensions(S), n_fields_allocate, field_list); 119 | 120 | // Copy fields for each element of the struct array S: 121 | //for (iElem = 0; iElem < n_elements; iElem++) { 122 | 123 | for (int iField = 0; iField < field_number_of_input; iField++) { 124 | mxSetFieldByNumber(plhs[0], 0, iField, 125 | COPY_ARRAY(mxGetFieldByNumber(S, 0, iField))); 126 | } 127 | 128 | for (int iField = field_number_of_input + 1; iField < n_fields_allocate; iField++) { 129 | mxSetFieldByNumber(plhs[0], 0, iField, 130 | COPY_ARRAY(mxGetFieldByNumber(S, 0, iField))); 131 | } 132 | 133 | mxSetFieldByNumber(plhs[0], 0, field_number_of_input, value_to_set); 134 | 135 | // Cleanup: 136 | mxFree(field_list); 137 | 138 | 139 | } -------------------------------------------------------------------------------- /src/Matlab/+wcon/+sl/+struct/setField.m: -------------------------------------------------------------------------------- 1 | function setField 2 | 3 | error('Mex function setField.c not compiled') -------------------------------------------------------------------------------- /src/Matlab/+wcon/+sl/+struct/setField.mexw64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openworm/tracker-commons/e5a12d3213839e9307f9df5e0973201fdcfeca6e/src/Matlab/+wcon/+sl/+struct/setField.mexw64 -------------------------------------------------------------------------------- /src/Matlab/+wcon/+utils/fex/fRMField_license.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010, Jan Simon 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in 12 | the documentation and/or other materials provided with the distribution 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 18 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /src/Matlab/+wcon/+utils/jsonopt.m: -------------------------------------------------------------------------------- 1 | function val=jsonopt(key,default,varargin) 2 | % 3 | % val=jsonopt(key,default,optstruct) 4 | % 5 | % setting options based on a struct. The struct can be produced 6 | % by varargin2struct from a list of 'param','value' pairs 7 | % 8 | % authors:Qianqian Fang (fangq nmr.mgh.harvard.edu) 9 | % 10 | % $Id: loadjson.m 371 2012-06-20 12:43:06Z fangq $ 11 | % 12 | % input: 13 | % key: a string with which one look up a value from a struct 14 | % default: if the key does not exist, return default 15 | % optstruct: a struct where each sub-field is a key 16 | % 17 | % output: 18 | % val: if key exists, val=optstruct.key; otherwise val=default 19 | % 20 | % license: 21 | % BSD or GPL version 3, see LICENSE_{BSD,GPLv3}.txt files for details 22 | % 23 | % -- this function is part of jsonlab toolbox (http://iso2mesh.sf.net/cgi-bin/index.cgi?jsonlab) 24 | % 25 | 26 | val=default; 27 | if(nargin<=2) 28 | return; 29 | end 30 | opt=varargin{1}; 31 | if(isstruct(opt)) 32 | if(isfield(opt,key)) 33 | val=getfield(opt,key); 34 | elseif(isfield(opt,lower(key))) 35 | val=getfield(opt,lower(key)); 36 | end 37 | end -------------------------------------------------------------------------------- /src/Matlab/+wcon/+utils/mergestruct.m: -------------------------------------------------------------------------------- 1 | function s=mergestruct(s1,s2) 2 | % 3 | % s=mergestruct(s1,s2) 4 | % 5 | % merge two struct objects into one 6 | % 7 | % authors:Qianqian Fang (fangq nmr.mgh.harvard.edu) 8 | % date: 2012/12/22 9 | % 10 | % input: 11 | % s1,s2: a struct object, s1 and s2 can not be arrays 12 | % 13 | % output: 14 | % s: the merged struct object. fields in s1 and s2 will be combined in s. 15 | % 16 | % license: 17 | % BSD or GPL version 3, see LICENSE_{BSD,GPLv3}.txt files for details 18 | % 19 | % -- this function is part of jsonlab toolbox (http://iso2mesh.sf.net/cgi-bin/index.cgi?jsonlab) 20 | % 21 | 22 | if(~isstruct(s1) || ~isstruct(s2)) 23 | error('input parameters contain non-struct'); 24 | end 25 | if(length(s1)>1 || length(s2)>1) 26 | error('can not merge struct arrays'); 27 | end 28 | fn=fieldnames(s2); 29 | s=s1; 30 | for i=1:length(fn) 31 | s=setfield(s,fn{i},getfield(s2,fn{i})); 32 | end 33 | -------------------------------------------------------------------------------- /src/Matlab/+wcon/+utils/readZipData.m: -------------------------------------------------------------------------------- 1 | function output = readZipData(zip_file_path) 2 | % 3 | % wcon.utils.readZipData(zip_file_path) 4 | % 5 | %There is a lot of room for improvement in this file 6 | % 7 | %1) Ideally we switch to c code that handles reading the zip file 8 | %2) Ideally we can pad the result directly without a copy 9 | 10 | 11 | z = wcon.utils.zip_file(); 12 | 13 | temp_zip_dir_path = tempname(); 14 | 15 | file_names = unzip(zip_file_path,temp_zip_dir_path); 16 | 17 | keyboard 18 | 19 | z.temp_directory = temp_zip_dir_path; 20 | z.n_files = length(file_names); 21 | 22 | output = z; 23 | 24 | %N_Files 25 | %function to read binary data of each file 26 | 27 | 28 | 29 | 30 | 31 | % import com.mathworks.mlwidgets.io.InterruptibleStreamCopier 32 | % a=java.io.ByteArrayInputStream(Z); 33 | % b=java.util.zip.InflaterInputStream(a); 34 | % isc = InterruptibleStreamCopier.getInterruptibleStreamCopier; 35 | % c = java.io.ByteArrayOutputStream; 36 | % isc.copyStream(b,c); 37 | % Q=typecast(c.toByteArray,'uint8'); -------------------------------------------------------------------------------- /src/Matlab/+wcon/+utils/rmfield.m: -------------------------------------------------------------------------------- 1 | function S = rmfield(S, ToDelete) 2 | % rmfield: Remove field(s) from a struct - fast [MEX] 3 | % This function is about 5 to 10 times faster than RMFIELD of Matlab 2009a. 4 | % 5 | % T = rmfield(S, Name) 6 | % INPUT: 7 | % S: Struct or struct array. 8 | % Name: String or cell string. Removing a name, which is not a field name 9 | % of S, is *not* an error here, in opposite to Matlab's RMFIELD. 10 | % OUTPUT: 11 | % T: Struct S without the removed fields. 12 | % 13 | % EXAMPLES: 14 | % S.A = 1; S.B = 2; 15 | % T = rmfield(S, {'B', 'C'}); % >> T.A = 1 16 | % T = rmfield(S, 'A'); % >> T.B = 2 17 | % 18 | % COMPILATION: See rmfield.c 19 | % 20 | % TEST: Run TestfRMField to check validity and speed of the Mex function. 21 | % 22 | % Tested: Matlab 6.5, 7.7, 7.8, WinXP 23 | % Author: Jan Simon, Heidelberg, (C) 2007-2010 matlab.THISYEAR(a)nMINUSsimon.de 24 | 25 | % $JRev: R0g V:026 Sum:xDhgUft6JcQP Date:19-Aug-2010 23:57:17 $ 26 | % $License: BSD $ 27 | % $UnitTest: TestfRMField $ 28 | % $File: Tools\GLStruct\fRMField.m $ 29 | % 018: 28-Dec-2007 00:02, CELL2STRUCT without DIM is faster. 30 | 31 | % ============================================================================== 32 | % This is an implementation as M-file. It is still faster than RMFIELD of 33 | % Matlab 2009a, but it needs the C-Mex function CStrAinBP (Jan Simon): 34 | % http://www.mathworks.com/matlabcentral/fileexchange/24380 35 | 36 | % Prefer the C-Mex implementation of fRMField! 37 | % Comment this out, if you want to use the M-version: 38 | error(['JSimon:', mfilename, ':NoMex'], 'Cannot find compiled Mex file!'); -------------------------------------------------------------------------------- /src/Matlab/+wcon/+utils/rmfield.mexw64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openworm/tracker-commons/e5a12d3213839e9307f9df5e0973201fdcfeca6e/src/Matlab/+wcon/+utils/rmfield.mexw64 -------------------------------------------------------------------------------- /src/Matlab/+wcon/+utils/varargin2struct.m: -------------------------------------------------------------------------------- 1 | function opt=varargin2struct(varargin) 2 | % 3 | % 4 | % This is a temporary file as I'm currently using the jsonlab code 5 | % to write json files, but it would be much better to remove this 6 | % as the performance is not good 7 | % 8 | % opt=varargin2struct('param1',value1,'param2',value2,...) 9 | % or 10 | % opt=varargin2struct(...,optstruct,...) 11 | % 12 | % convert a series of input parameters into a structure 13 | % 14 | % authors:Qianqian Fang (fangq nmr.mgh.harvard.edu) 15 | % date: 2012/12/22 16 | % 17 | % input: 18 | % 'param', value: the input parameters should be pairs of a string and a value 19 | % optstruct: if a parameter is a struct, the fields will be merged to the output struct 20 | % 21 | % output: 22 | % opt: a struct where opt.param1=value1, opt.param2=value2 ... 23 | % 24 | % license: 25 | % BSD or GPL version 3, see LICENSE_{BSD,GPLv3}.txt files for details 26 | % 27 | % -- this function is part of jsonlab toolbox (http://iso2mesh.sf.net/cgi-bin/index.cgi?jsonlab) 28 | % 29 | 30 | len=length(varargin); 31 | opt=struct; 32 | if(len==0) return; end 33 | i=1; 34 | while(i<=len) 35 | if(isstruct(varargin{i})) 36 | opt=wcon.utils.mergestruct(opt,varargin{i}); 37 | elseif(ischar(varargin{i}) && i 1 31 | error('Multiple files detected, this functionality is not yet supported') 32 | end 33 | %TODO: We should build in the string buffering into the zip reading ... 34 | %With the current zip implementation, we could pass in the file path 35 | %but eventually I want to avoid unzipping the files to disk 36 | data_in_zip_file = temp.readFile(1); 37 | 38 | %This is temporary, char conversion back and forth kills performance 39 | temp_char_data = char(data_in_zip_file); 40 | 41 | %tic; 42 | root = json.tokens.parse(temp_char_data); 43 | %toc; 44 | else 45 | root = json.tokens.load(file_path); 46 | end 47 | %-------------------------------------------------------------------------- 48 | 49 | props = root.parseExcept({'units','data','metadata'}); 50 | 51 | if ~isfield(props,'units') 52 | error('WCON file must contain units') 53 | end 54 | units_token = root.getToken('units'); 55 | props.units = wcon.units.fromFile(units_token,obj,options); 56 | 57 | if ~isfield(props,'data') 58 | error('WCON file must contain data') 59 | end 60 | 61 | %TODO: We could make this lazy again ... 62 | %This needs to go at the end ... 63 | %obj.addLazyField('data',@()wcon.data.fromFile(root.getToken('data'),options)); 64 | data_token = root.getToken('data'); 65 | props.data = wcon.data.fromFile(data_token,obj,options); 66 | 67 | %meta-data is optional 68 | if isfield(props,'metadata') 69 | props.metadata = wcon.metadata.fromFile(root.getToken('metadata')); 70 | end 71 | 72 | props.files = {file_path}; 73 | obj.props = props; 74 | 75 | 76 | end -------------------------------------------------------------------------------- /src/Matlab/+wcon/@dataset/save.m: -------------------------------------------------------------------------------- 1 | function save(obj,file_path) 2 | % 3 | % Save is currently implemented by converting all objects to structs 4 | % and then calling libjson. Eventually this should be changed for better 5 | % performance ... 6 | % 7 | 8 | %Round trip issues: 9 | %------------------ 10 | %1) Array of objects or a singular object 11 | % 12 | % "prop":{} or "prop":[{}] => both yield .prop = object 13 | 14 | 15 | temp = h__process_object(obj); 16 | turtle_json_write_v0(temp); 17 | keyboard 18 | 19 | wcon.utils.savejson('',temp,'FileName',file_path); 20 | 21 | end 22 | 23 | function output = h__process_object(objs) 24 | 25 | n_objs = length(objs); 26 | 27 | %TODO: Add check, not allowing multi-dimensional object array as an input 28 | output = cell(1,n_objs); 29 | for iObj = 1:n_objs 30 | input_object = objs(iObj); 31 | 32 | % if isa(input_object,'wcon.utils.lazy_dict') || isa(input_object,'wcon.sl.obj.dict') 33 | % s = input_object.getPropertiesStruct(); 34 | % else 35 | % orig_state = warning('off','MATLAB:structOnObject'); 36 | s = struct(input_object); 37 | %class(input_object) 38 | % warning(orig_state); 39 | % end 40 | 41 | output{iObj} = h__process_struct(s); 42 | 43 | end 44 | 45 | if n_objs == 1 46 | output = output{1}; 47 | end 48 | 49 | end 50 | 51 | function output = h__process_cell(input) 52 | 53 | 54 | output = cell(size(input)); 55 | for iValue = 1:numel(output) 56 | cur_input_value = input{iValue}; 57 | if isobject(cur_input_value) 58 | %Should never have nulls in a cell array 59 | % if isa(temp,'wcon.NULL') 60 | % continue 61 | % end 62 | value = h__process_object(cur_input_value); 63 | elseif isstruct(cur_input_value) 64 | value = h__process_struct(cur_input_value); 65 | elseif iscell(cur_input_value) 66 | value = h__process_cell(cur_input_value); 67 | elseif ischar(cur_input_value) 68 | value = cur_input_value; 69 | elseif isnumeric(cur_input_value) 70 | value = cur_input_value; 71 | else 72 | error('Unhandled data type') 73 | end 74 | 75 | output{iValue} = value; 76 | end 77 | end 78 | 79 | function s_out = h__process_struct(s_in) 80 | 81 | s_out = struct; 82 | fn = fieldnames(s_in); 83 | for iField = 1:length(fn) 84 | cur_field_name = fn{iField}; 85 | temp = s_in.(cur_field_name); 86 | if isobject(temp) 87 | if isa(temp,'wcon.NULL') 88 | continue 89 | end 90 | value = h__process_object(temp); 91 | elseif isstruct(temp) 92 | value = h__process_struct(temp); 93 | elseif iscell(temp) 94 | value = h__process_cell(temp); 95 | elseif ischar(temp) 96 | value = temp; 97 | elseif isnumeric(temp) 98 | value = temp; 99 | else 100 | error('Unhandled data type') 101 | end 102 | 103 | try 104 | s_out.(cur_field_name) = value; 105 | catch 106 | s_out = wcon.sl.struct.setField(s_out,cur_field_name,value); 107 | end 108 | %TODO 109 | end 110 | 111 | end 112 | -------------------------------------------------------------------------------- /src/Matlab/+wcon/@load_options/load_options.m: -------------------------------------------------------------------------------- 1 | classdef load_options 2 | % 3 | % Class: 4 | % wcon.load_options 5 | % 6 | % See Also: 7 | % wcon.load 8 | % 9 | % 10 | % TODO: It would be nice to have a nice display of the options 11 | % with links to their documentation. This could be made a generic 12 | % function. 13 | 14 | properties 15 | merge_data = false %merge_data : (default false) 16 | % If true, data.x and data.y are matrices when all frames 17 | % have the same number of samples. 18 | % If false, all frames will be returned as cells. 19 | % 20 | % This can save time on loading, but may causes differences 21 | % in data between worms that have consistent frames and worms 22 | % that do not. 23 | end 24 | 25 | methods 26 | end 27 | 28 | end 29 | 30 | -------------------------------------------------------------------------------- /src/Matlab/+wcon/@metadata/metadata.m: -------------------------------------------------------------------------------- 1 | classdef metadata < json.objs.dict 2 | % 3 | % Class: 4 | % wcon.metadata 5 | % 6 | % https://github.com/openworm/tracker-commons/blob/master/WCON_format.md#metadata-fields-in-detail 7 | 8 | %All entries in this object are optional 9 | 10 | %{ 11 | custom 12 | lab 13 | who 14 | timestamp 15 | temperature 16 | humidity 17 | arena 18 | food 19 | media 20 | sex 21 | stage 22 | age 23 | strain 24 | protocol 25 | software 26 | settings 27 | 28 | 29 | %} 30 | 31 | methods 32 | function obj = metadata() 33 | null = wcon.NULL; 34 | props = obj.props; 35 | props.custom = null; 36 | props.lab = null; 37 | props.who = null; 38 | props.timestamp = null; 39 | props.temperature = null; 40 | props.humidity = null; 41 | props.arena = null; 42 | props.food = null; 43 | props.media = null; 44 | props.sex = null; 45 | props.stage = null; 46 | props.age = null; 47 | props.strain = null; 48 | props.protocol = null; 49 | props.software = null; 50 | props.settings = null; 51 | obj.props = props; 52 | end 53 | end 54 | 55 | methods (Static) 56 | function obj = fromFile(m) 57 | % 58 | % Inputs 59 | % ------ 60 | % m : json.token_info.object_token_info 61 | 62 | obj = wcon.metadata; 63 | 64 | [props,key_locations] = m.parseExcept({'lab','software'}); 65 | if key_locations(1) 66 | props.lab = wcon.meta.lab.fromFile(m.getToken('lab')); 67 | end 68 | if key_locations(2) 69 | props.software = wcon.meta.software.fromFile(m.getToken('software')); 70 | end 71 | 72 | obj.props = props; 73 | 74 | end 75 | end 76 | 77 | end 78 | 79 | -------------------------------------------------------------------------------- /src/Matlab/+wcon/@units/units.m: -------------------------------------------------------------------------------- 1 | classdef units < json.objs.dict 2 | % 3 | % Class: 4 | % wcon.units 5 | % 6 | % https://github.com/openworm/tracker-commons/blob/master/WCON_format.md#units 7 | % 8 | % Canonical Units: 9 | % ---------------- 10 | % s 11 | % mm 12 | 13 | %{ 14 | Any numeric quantity followed on a per-animal or per-timepoint basis 15 | must have its units defined in an object. 16 | 17 | Standard units: 18 | --------------- 19 | - seconds 20 | - millimeters 21 | 22 | Data properties that must have units 23 | ------------------------------------ 24 | - t 25 | - x 26 | - y 27 | 28 | 29 | Standard Properties: t x y 30 | %} 31 | 32 | %{ 33 | Specification: 34 | Tracker Commons software will automatically convert units to the 35 | standard internal representations (e.g. inches to mm) for all fields 36 | specified in the units block. It will look inside anything with a 37 | custom tag. It will not look inside the settings block in metadata, 38 | nor will it look inside unknown fields. 39 | 40 | Jim note: this will not look inside fields inside custom data 41 | 42 | %} 43 | 44 | methods (Static) 45 | function obj = fromFile(t,parent,options) 46 | % 47 | % obj = wcon.units.fromFile(t) 48 | % 49 | % Input 50 | % ----- 51 | % t: json.objs.token.object 52 | % parent: wcon.dataset 53 | % options: wcon.load_options 54 | 55 | obj = wcon.units; 56 | obj.props = t.getParsedData(); 57 | obj.props.parent = parent; 58 | end 59 | end 60 | 61 | methods 62 | function s = struct(obj) 63 | %disp('I ran') 64 | s = obj.props; 65 | s = wcon.utils.rmfield(s,'parent'); 66 | end 67 | end 68 | end 69 | 70 | -------------------------------------------------------------------------------- /src/Matlab/+wcon/NULL.m: -------------------------------------------------------------------------------- 1 | classdef (Hidden) NULL 2 | % 3 | % Class: 4 | % wcon.NULL 5 | 6 | end 7 | 8 | -------------------------------------------------------------------------------- /src/Matlab/+wcon/compile_code.m: -------------------------------------------------------------------------------- 1 | function compile_code 2 | 3 | 4 | 5 | end -------------------------------------------------------------------------------- /src/Matlab/+wcon/load.m: -------------------------------------------------------------------------------- 1 | function obj = load(file_path,varargin) 2 | % 3 | % w = wcon.load(file_path,varargin) 4 | % 5 | % Inputs 6 | % ------ 7 | % file_path : string 8 | % Path to the file. 9 | % 10 | % Optional Inputs 11 | % ---------------- 12 | % Optional inputs are specified in wcon.load_options 13 | % 14 | % Examples 15 | % -------- 16 | % 1) Standard Usage 17 | % w = wcon.load('C:\wcon_files\test.wcon') 18 | % 19 | % 2) Options 20 | % options = wcon.load_options; 21 | 22 | 23 | %{ 24 | 25 | root_path = 'G:\wcon_files\' 26 | ff = @(x) fullfile(root_path,x); 27 | file_path = ff('XJ30_NaCl500mM4uL6h_10m45x10s40s_Ea.wcon'); 28 | file_path = ff('wcon_testfile_new.wcon'); 29 | w = wcon.load(file_path); 30 | 31 | %} 32 | 33 | in = wcon.load_options; 34 | in = wcon.sl.in.processVarargin(in,varargin); 35 | 36 | obj = wcon.dataset.fromFile(file_path,in); 37 | 38 | end -------------------------------------------------------------------------------- /src/Matlab/ReadMe.md: -------------------------------------------------------------------------------- 1 | Usage 2 | ===== 3 | 4 | ```matlab 5 | f = wcon.load(file_path); 6 | ``` 7 | 8 | Requirements 9 | ============ 10 | This library relies on the Turtle JSON library: 11 | https://github.com/JimHokanson/turtle_json 12 | 13 | 14 | Issues 15 | ====== 16 | 17 | Lots. Jim will hopefully document these on GitHub soon. 18 | 19 | https://github.com/openworm/tracker-commons/issues 20 | -------------------------------------------------------------------------------- /src/Matlab/docs/TODO_List.txt: -------------------------------------------------------------------------------- 1 | Outstanding Tasks: 2 | 3 | 1) Verify that the temporary writer supports utf-8 conversion 4 | 2) Multiple file support 5 | 6 | Issues: 7 | https://github.com/openworm/tracker-commons/issues/129 8 | https://github.com/openworm/tracker-commons/issues/128 9 | 10 | Lastly: 11 | https://github.com/openworm/tracker-commons/issues/129 12 | 13 | A function to create each structure (arena, software, etc.)--optionally with whatever language-specific features make it most convenient (named arguments, default values, whatever) 14 | A function to create a single data entry from numbers 15 | A function to merge data entries into array form -------------------------------------------------------------------------------- /src/Matlab/docs/design_decisions.txt: -------------------------------------------------------------------------------- 1 | Design Decisions 2 | ----------------- 3 | 4 | 1) For worm data, should time be across rows or columns? -------------------------------------------------------------------------------- /src/Matlab/tests/+wcon_tests/+tracker_commons/@base/base.m: -------------------------------------------------------------------------------- 1 | classdef base 2 | % 3 | % Class 4 | % wcon_tests.tracker_commons.base 5 | % 6 | % See Also 7 | % -------- 8 | % wcon_tests.tracker_commons 9 | 10 | properties 11 | root_path 12 | end 13 | 14 | methods 15 | function obj = base(parent) 16 | obj.root_path = parent.root_path; 17 | end 18 | function run(obj) 19 | 20 | obj.intermediate_wcon(); 21 | obj.intermediate_wcon_zip(); 22 | 23 | end 24 | function intermediate_wcon(obj) 25 | % 26 | 27 | output = h_loadFile(obj,'intermediate.wcon'); 28 | end 29 | function intermediate_wcon_zip(obj) 30 | output = h_loadFile(obj,'intermediate.wcon.zip'); 31 | keyboard 32 | end 33 | end 34 | 35 | end 36 | 37 | function output = h_loadFile(obj,file_name) 38 | 39 | %TODO: Verify filename exists 40 | file_path = fullfile(obj.root_path,file_name); 41 | output = wcon.load(file_path); 42 | end -------------------------------------------------------------------------------- /src/Matlab/tests/+wcon_tests/@tracker_commons/tracker_commons.m: -------------------------------------------------------------------------------- 1 | classdef tracker_commons 2 | % 3 | % Class: 4 | % wcon_tests.tracker_commons 5 | 6 | %{ 7 | tester = wcon_tests.tracker_commons; 8 | tester.run 9 | %} 10 | 11 | properties 12 | root_path 13 | end 14 | 15 | methods 16 | function obj = tracker_commons() 17 | % 18 | % obj = wcon_tests.tracker_commons 19 | % 20 | 21 | temp = which('wcon.load'); 22 | tc_root = fileparts(fileparts(fileparts(fileparts(temp)))); 23 | obj.root_path = fullfile(tc_root,'tests'); 24 | end 25 | function run(obj) 26 | base = wcon_tests.tracker_commons.base(obj); 27 | base.run(); 28 | end 29 | end 30 | 31 | end 32 | 33 | -------------------------------------------------------------------------------- /src/Matlab/tests/MRC_test.m: -------------------------------------------------------------------------------- 1 | file_path = 'D:\test.json\test.json'; 2 | file_path = 'G:\test.json.zip'; 3 | 4 | options = struct; 5 | f = wcon.load(file_path,options); -------------------------------------------------------------------------------- /src/Matlab/tests/test_shared_files.m: -------------------------------------------------------------------------------- 1 | function test_shared_files 2 | 3 | %{ 4 | %Testing code 5 | %----------------------------------------- 6 | FILE_USE = 1; 7 | file_root = 'C:\Users\RNEL\Google Drive\OpenWorm\OpenWorm Public\Movement Analysis\example_data\WCON' 8 | options = struct; 9 | if FILE_USE == 1 10 | file_name = 'testfile_new.wcon' 11 | 12 | %This is incorrect 13 | %options.merge_data = true; 14 | else 15 | file_name = 'XJ30_NaCl500mM4uL6h_10m45x10s40s_Ea.wcon' 16 | end 17 | 18 | file_path = fullfile(file_root,file_name); 19 | 20 | tic 21 | for i = 1:10 22 | f = wcon.load(file_path,options); 23 | 24 | end 25 | toc/10 26 | 27 | tic 28 | profile on 29 | for i = 1:10 30 | f = wcon.load(file_path,options); 31 | end 32 | profile off 33 | toc 34 | %} 35 | -------------------------------------------------------------------------------- /src/Python/INSTALL.md: -------------------------------------------------------------------------------- 1 | The `wcon` Python package works with both Python 2 and 3. 2 | 3 | If you already have the scipy stack installed, installing `wcon` should be as simple as: 4 | 5 | ``` 6 | pip install wcon 7 | ``` 8 | 9 | Here are the complete installation instructions to get from a freshly provisioned Ubuntu Amazon Web Services (AWS) Machine Instance (AMI) to a machine with WCON installed: 10 | 11 | ``` 12 | # PYTHON 3 STEPS FROM CLEAN UBUNTU AMI 13 | 14 | # UPDATE SYSTEM 15 | sudo apt-get -y update 16 | sudo apt-get -y upgrade 17 | sudo apt-get -y dist-upgrade 18 | sudo reboot 19 | 20 | # INSTALL PYTHON AND LIBRARIES NEEDED BY WCON 21 | PYTHON_VERSION=3.5 22 | MINICONDA_DIR=~/miniconda3 23 | wget http://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh 24 | chmod +x miniconda.sh 25 | ./miniconda.sh -b 26 | export PATH=$MINICONDA_DIR/bin:$PATH 27 | conda install --yes python=$PYTHON_VERSION numpy scipy pandas jsonschema psutil 28 | 29 | # INSTALL wcon. Two options: 30 | 31 | # 1. GET WCON FROM PYPI 32 | pip install wcon 33 | 34 | # 2. ALTERNATIVELY: GET WCON FROM SOURCE 35 | sudo apt-get install -y git 36 | cd ~ 37 | mkdir github 38 | cd github 39 | git clone https://github.com/openworm/tracker-commons.git 40 | cd tracker-commons/src/Python/tests 41 | python tests.py 42 | ``` 43 | 44 | For Python 2, these steps will differ slightly (e.g. change to `PYTHON_VERSION=2.7` and `MINICONDA_DIR=~/miniconda`) 45 | -------------------------------------------------------------------------------- /src/Python/examples/truncate_wcon.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import sys 3 | 4 | sys.path.append('..') 5 | from wcon import WCONWorms 6 | 7 | import tests 8 | 9 | tests.setUpModule() 10 | 11 | file_name = 'asic-1 (ok415) on food L_2010_07_08__11_46_40___7___5.wcon' 12 | w = WCONWorms.load_from_file(file_name) 13 | 14 | # Truncate to just the first 10 seconds 15 | w.data_as_odict['1'] = w.data_as_odict['1'].ix[0:10] 16 | 17 | # Grab the first 10 frames (doesn't work since the first 3885 frames are NaN) 18 | # w.data_as_odict['1'] = w.data_as_odict['1'].iloc[:10] 19 | 20 | # View the experiment data as a CSV (e.g. so it can be loaded in Excel) 21 | # w.data_as_odict['1'].to_csv('bbb.csv') 22 | 23 | # Save the smaller file 24 | w.save_to_file('smaller.wcon', pretty_print=True) 25 | 26 | tests.tearDownModule() 27 | -------------------------------------------------------------------------------- /src/Python/examples/view_wcon.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import sys 3 | 4 | sys.path.append('..') 5 | from wcon import WCONWorms, MeasurementUnit 6 | 7 | file_name = 'asic-1 (ok415) on food L_2010_07_08__11_46_40___7___5.wcon' 8 | w = WCONWorms.load_from_file(file_name) 9 | -------------------------------------------------------------------------------- /src/Python/setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | """ 4 | setup.py for the Python packager 5 | 6 | Based on https://github.com/pypa/sampleproject/blob/master/setup.py 7 | """ 8 | 9 | # Always prefer setuptools over distutils 10 | from setuptools import setup 11 | # To use a consistent encoding 12 | from codecs import open 13 | from os import path 14 | import os 15 | from wcon.version import __version__ 16 | 17 | here = path.abspath(path.dirname(__file__)) 18 | readme_path = path.join(here, 'README.md') 19 | 20 | long_description = 'See https://github.com/openworm/tracker-commons\n' 21 | 22 | # Get the long description from the README file, and add it. 23 | if path.exists(readme_path): 24 | with open(readme_path, encoding='utf-8') as f: 25 | long_description += f.read() 26 | 27 | print(os.listdir('.')) # DEBUG 28 | 29 | setup( 30 | name='wcon', 31 | # Versions should comply with PEP440. For a discussion on single-sourcing 32 | # the version across setup.py and the project code, see 33 | # https://packaging.python.org/en/latest/single_source_version.html 34 | version=__version__, 35 | description='Worm tracker Commons Object Notation', 36 | long_description=long_description, 37 | url='https://github.com/openworm/tracker-commons', 38 | author='Kerr, R; Brown, A; Currie, M; OpenWorm', 39 | author_email='ichoran@gmail.com', 40 | license='MIT', 41 | classifiers=[ 42 | 'Development Status :: 4 - Beta', 43 | 'Intended Audience :: Science/Research', 44 | 'Topic :: Scientific/Engineering :: Bio-Informatics', 45 | 'License :: OSI Approved :: MIT License', 46 | 'Programming Language :: Python :: 2', 47 | 'Programming Language :: Python :: 2.7', 48 | 'Programming Language :: Python :: 3', 49 | 'Programming Language :: Python :: 3.4', 50 | 'Programming Language :: Python :: 3.5', 51 | ], 52 | keywords='C. elegans worm tracking', 53 | packages=['wcon'], 54 | package_data={'': ['../../wcon_schema.json']}, 55 | install_requires=['jsonschema'] 56 | # Actually also requires numpy, scipy and numpy but I don't want to force 57 | # pip to install these since pip is bad at that for those packages. 58 | ) 59 | -------------------------------------------------------------------------------- /src/Python/temp_example.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Created on Sun Dec 13 00:47:21 2015 5 | 6 | @author: Michael 7 | """ 8 | import numpy as np 9 | import pandas as pd 10 | import warnings 11 | from six import StringIO 12 | import json 13 | 14 | from wcon import WCONWorms, MeasurementUnit 15 | 16 | pd.set_option('display.expand_frame_repr', False) 17 | 18 | # Suppress RuntimeWarning warnings in Spider because it's a known bug 19 | # http://stackoverflow.com/questions/30519487/ 20 | # warnings.simplefilter(action = "ignore", category = RuntimeWarning) 21 | 22 | 23 | if __name__ == '__main__': 24 | 25 | JSON_path = '../../tests/minimax.wcon' 26 | 27 | w2 = WCONWorms.load_from_file(JSON_path) 28 | 29 | w2.save_to_file('example_saved_file.WCON', pretty_print=True) 30 | w3 = WCONWorms.load_from_file('example_saved_file.WCON') 31 | 32 | u = MeasurementUnit.create('cm') 33 | 34 | # io=StringIO() 35 | # json.dump([None], io) 36 | # io.getvalue() 37 | 38 | if __name__ == '__main__2': 39 | worm_file_text2 = (('{"units":{"t":"s","x":"m","y":"m"},' 40 | '"data":[{"id":3, "t":1.3, ' 41 | '"x":[3,4], "y":[5.4,3]}]}')) 42 | 43 | w1 = WCONWorms.load(StringIO(worm_file_text2)) 44 | 45 | 46 | if __name__ == '__main__3': 47 | w1 = WCONWorms.load(StringIO('{"units":{},' 48 | '"data":[{"id":3, "t":1.3, ' 49 | '"x":[3,4,4,3.2], ' 50 | '"y":[5.4,3,1,-3]}]}')) 51 | -------------------------------------------------------------------------------- /src/Python/tests/.bbb.wcon.swp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openworm/tracker-commons/e5a12d3213839e9307f9df5e0973201fdcfeca6e/src/Python/tests/.bbb.wcon.swp -------------------------------------------------------------------------------- /src/Python/tests/diagnostic_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Unit tests for the Python WCON parser 5 | 6 | """ 7 | import os 8 | import sys 9 | from six import StringIO # StringIO.StringIO in 2.x, io.StringIO in 3.x 10 | import json 11 | import jsonschema 12 | import unittest 13 | import filecmp 14 | import glob 15 | import collections 16 | 17 | import pandas as pd 18 | idx = pd.IndexSlice 19 | import numpy as np 20 | import time 21 | 22 | sys.path.append('..') 23 | from wcon import WCONWorms, MeasurementUnit 24 | from wcon.measurement_unit import MeasurementUnitAtom 25 | 26 | 27 | def timing_function(): 28 | """ 29 | There's a better timing function available in Python 3.3+ 30 | Otherwise use the old one. 31 | 32 | """ 33 | if sys.version_info[0] >= 3 and sys.version_info[1] >= 3: 34 | return time.monotonic() 35 | else: 36 | return time.time() 37 | 38 | 39 | if __name__ == '__main__': 40 | # def test_big_file(): 41 | print("BIG TEST: Test load and save and load and save") 42 | files_to_test = [sys.argv[1]] 43 | 44 | for JSON_path in files_to_test: 45 | for pretty in [True]: # , False]: 46 | print("LOADING FOR TEST: " + JSON_path + 47 | " (PRETTY = " + str(pretty) + ")") 48 | 49 | start_time = timing_function() 50 | w1 = WCONWorms.load_from_file(JSON_path, 51 | validate_against_schema=False) 52 | print("Time to load w1: " + str(timing_function() - start_time)) 53 | 54 | # Save these worm tracks to a file, then load that file 55 | test_path = 'test.wcon' 56 | start_time = timing_function() 57 | w1.save_to_file(test_path, pretty_print=pretty) 58 | print("Time to save w1: " + str(timing_function() - start_time)) 59 | 60 | start_time = timing_function() 61 | w2 = WCONWorms.load_from_file(test_path, 62 | validate_against_schema=False) 63 | print("Time to load w2: " + str(timing_function() - start_time)) 64 | 65 | # x1 = w1.data.loc[:, idx[0, 'x', 0]].fillna(0) 66 | # x2 = w2.data.loc[:, idx[0, 'x', 0]].fillna(0) 67 | # cmm = np.flatnonzero(x1 != x2) 68 | # xx = pd.concat([x1, x2], axis=1) 69 | # xx = xx.loc[cmm] 70 | 71 | # Then load and save AGAIN and do a file comparison to make 72 | # sure it's the same 73 | # this will test that we order the keys (even though this 74 | # isn't in the WCON standard it's nice for human 75 | # readability, i.e. to have "units" before "data", 76 | # "id" first in a data segment, etc.) 77 | w3 = WCONWorms.load_from_file(test_path, 78 | validate_against_schema=False) 79 | assert(w2 == w3) 80 | assert(w1 == w2) 81 | assert(w1 == w3) 82 | 83 | test_path2 = 'test2.wcon' 84 | w3.save_to_file(test_path2, pretty_print=pretty) 85 | 86 | # As described in the above comment: check that running 87 | # load/save twice should generate an IDENTICAL file. 88 | assert(filecmp.cmp(test_path, test_path2)) 89 | 90 | os.remove(test_path) 91 | os.remove(test_path2) 92 | 93 | # test_big_file() 94 | -------------------------------------------------------------------------------- /src/Python/wcon/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | """ 4 | wcon: Worm tracker Commons Object Notation 5 | https://github.com/openworm/tracker-commons 6 | 7 | """ 8 | 9 | 10 | from .wcon_parser import WCONWorms 11 | from .measurement_unit import MeasurementUnit 12 | from .version import __version__ 13 | 14 | __all__ = ['WCONWorms', 'MeasurementUnit', '__version__'] 15 | -------------------------------------------------------------------------------- /src/Python/wcon/version.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # Store the version here so: 5 | # 1) we don't load dependencies by storing it in __init__.py 6 | # 2) we can import it in setup.py for the same reason 7 | # 3) we can import it into your module module 8 | # (from http://stackoverflow.com/questions/458550/) 9 | __version__ = '1.1.0' 10 | -------------------------------------------------------------------------------- /src/R/README.md: -------------------------------------------------------------------------------- 1 | ## R implementation of the Tracker Commons WCON File Format 2 | 3 | R can run a Scala interpreter via the `rscala` package. This gives R access to the Scala implementation of the WCON file format. 4 | 5 | This project just contains minimal utility routines needed to get up and running quickly. 6 | 7 | ### Requirements 8 | 9 | You need Java 8 and R installed. 10 | 11 | ### Installation 12 | 13 | First, make sure the Scala installation works properly (you can run `sbt package` successfully). 14 | 15 | Then, with R installed, type `install.packages("rscala")`. If a recent version of Scala is installed, you should be able to run `library(rscala)` and nothing will happen; otherwise it will throw errors. If you don't have a recent version of Scala installed, either install one or use `rscala::scalaInstall()` to get one automatically. 16 | 17 | Finally, enter 18 | 19 | ```bash 20 | sbt assembly 21 | ``` 22 | 23 | to build a `.jar` for use with R. (You'll want to rerun this any time the Scala version changes and you want the updates.) 24 | 25 | ### Example usage 26 | 27 | From within R, start with the `source('wcon-ready.r')`. This will load a Scala interpreter (called `si`) which you can call using the syntax `si %~% 'scala.code("here")'`. 28 | 29 | Here is an example of reading WCON data from R: 30 | 31 | ```R 32 | source('wcon-ready.r') 33 | si %~% 'val i = ReadWrite.read("../../tests/intermediate.wcon").right.get' 34 | si %~% 'val w = ct.Wcon from i' 35 | numrecords = si %~% 'w.datas.length' 36 | firstid = si %~% 'w.datas(0).id' 37 | first_time_of_first = si %~% 'w.datas(0).t(0)' 38 | first_y_points = si %~% 'w.datas(0).ys(0)' 39 | ``` 40 | 41 | ### Documentation 42 | 43 | You mostly want to use the Scala documentation since the R implementation just calls through to the Scala. You can use either the standard `org.openworm.trackercommons` structures, or the wrapped `org.openworm.trackercommons.compatibility` ones that use simpler data structures. Either way, the Scaladocs can be built with `sbt doc` in the Scala project directory. 44 | 45 | To learn more about how to use rscala to interface between R and Scala, see the [rscala documentation](https://cran.r-project.org/web/packages/rscala/rscala.pdf). 46 | -------------------------------------------------------------------------------- /src/R/build.sbt: -------------------------------------------------------------------------------- 1 | // Annoyingly, we need to know the resolvers of other projects 2 | // We depend on the Scala project: 3 | 4 | resolvers += "Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots" 5 | lazy val scalawcon = RootProject(file("../scala")) 6 | 7 | 8 | // This part handles the R project 9 | 10 | lazy val root = (project in file(".")).dependsOn(scalawcon).settings( 11 | name := "WconR", 12 | version := "0.1.0", 13 | scalaVersion := "2.11.8", 14 | assemblyJarName in assembly := "WconR-0.1.0.jar", 15 | target in assembly := file("target") 16 | ) 17 | -------------------------------------------------------------------------------- /src/R/project/assembly.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.3") 2 | -------------------------------------------------------------------------------- /src/R/wcon-ready.r: -------------------------------------------------------------------------------- 1 | library(rscala) 2 | si = scalaInterpreter("target/WconR-0.1.0.jar") 3 | si %~% "import kse.flow._, kse.coll._, kse.maths._, kse.eio._" 4 | si %~% "import kse.jsonal._, kse.jsonal.JsonConverters._" 5 | si %~% "import org.openworm.trackercommons.{compatibility => ct, _}" 6 | 7 | -------------------------------------------------------------------------------- /src/Rust/README.md: -------------------------------------------------------------------------------- 1 | # Rust implementation of Tracker Commons 2 | 3 | Rust is a hybrid high-level/low-level language with advanced compile-time checking to ensure an absence of the kinds of errors that usually plague traditional low-level languages such as C. 4 | 5 | ## Implementation notes 6 | 7 | Rust is reported to have an extremely fast parser combinator library, [`nom`](https://github.com/Geal/nom), which should mean that parsing can be extremely fast without having to push characters around by hand. 8 | 9 | It also has built-in JSON support in its [`rustc-serialize` library](https://crates.io/crates/rustc-serialize). May wish to test whether hand-rolled JSON support is faster or slower than a `nom` parser. Also, need to test whether `rustc-serialize` will do sane things with floating-point values (i.e. produce JSON, not something that looks like JSON but has junk in it). 10 | 11 | Rust also has a workable Foreign Function Interface (FFI) system to generate C bindings. Thus, the Rust implementation could be the back-end for other implementations (e.g. R) that may be slower. It could also be the back-end to a C or C++ implementation, but would provide stronger guarantees about safety than hand-written C. 12 | -------------------------------------------------------------------------------- /src/java/README.md: -------------------------------------------------------------------------------- 1 | ## Java implementation of Tracker Commons WCON data format 2 | 3 | This project contains a Java interface to the WCON data format from OpenWorm's Tracker Commons. 4 | 5 | This is not a stand-alone implementation. Rather, the Java implementation calls the Scala implementation, as that also runs on the Java Virtual Machine. This project supplies a few high-level routines that ease usage from Java. 6 | 7 | ### Requirements 8 | 9 | You need to have Java 8 running on your machine. Additionally, you need to be able to compile the Scala implementation: follow the instructions there. When you can type `sbt package` there and have it complete successfully, you're ready to begin here. 10 | 11 | Since you already need to have SBT running on your machine to build the Scala version, SBT is also used to build the Java version (instead of more traditional tools like Ant or Maven). Again, you already have it, so you shouldn't have to worry about it any more than you already did. 12 | 13 | ### Building the Java implementation 14 | 15 | In the `src/java` directory, enter 16 | 17 | ```bash 18 | sbt assembly 19 | ``` 20 | 21 | at the command-line. This will compile the Scala project if it needs to, then compile the files for this project. It produces a single `.jar` file that contains the Scala runtime, the class files from the Scala implementation, any additional class files needed, and the routines in this package. You can then include this single jar on your classpath. (It is in the `target` directory.) 22 | 23 | ### Example usage 24 | 25 | Here is an example of reading WCON data using the Java WCON reader. 26 | 27 | ```java 28 | import org.openworm.trackercommons.compatibility.*; 29 | import org.openworm.trackercommons.javawcon.*; 30 | 31 | public class ReadWconExample { 32 | public static void main(String args[]) throws java.io.IOException { 33 | java.io.File f = new java.io.File("../../tests/intermediate.wcon"); 34 | Wcon w = Wcons.read(f); 35 | System.out.println("There are " + w.datas().length + " records"); 36 | System.out.println("The first record has ID " + w.datas()[0].id()); 37 | System.out.println("The first timepoint for that ID is " + w.datas()[0].t(0)); 38 | System.out.println("The third x coordinate for that timepoint is " + w.datas()[0].x(0, 2)); 39 | } 40 | } 41 | ``` 42 | 43 | You can compile this with `javac -cp target/JavaWcon-0.1.jar ReadWconExample.java` and run it with `java -cp target/JavaWcon-0.1.jar ReadWconExample` (if you place this file in the Java Tracker Commons directory, which is probably not a great 44 | idea in general). 45 | 46 | ### Documentation 47 | 48 | You mostly want to view the Scaladocs for the `org.openworm.trackercommons.compatibility` library to see what you can do with the main classes here (`Wcon`, `Metadata`, `Data`, etc.). Run `sbt doc` inside the Scala project to generate documentation and point your browser there (at `target/scala-2.11/api`). 49 | -------------------------------------------------------------------------------- /src/java/Wcons.java: -------------------------------------------------------------------------------- 1 | package org.openworm.trackercommons.javawcon; 2 | 3 | import java.io.*; 4 | import org.openworm.trackercommons.compatibility.*; 5 | 6 | /** This class provides methods to read and write WCON files for consumption by Java programs. */ 7 | public class Wcons { 8 | /** Reads a Wcon file by using the Scala implementation, then converting the result. */ 9 | public static Wcon read(File f) throws IOException { 10 | scala.util.Either< String, org.openworm.trackercommons.DataSet > result = 11 | org.openworm.trackercommons.ReadWrite.read(f); 12 | if (result.isRight()) return Wcon.from(result.right().get()); 13 | else throw new IOException(result.left().get()); 14 | } 15 | 16 | /** Writes a Wcon file (possibly zipped) by converting the data to standard Scala data structures and using Scala's writing support. */ 17 | public static void write(File f, Wcon w, Boolean asZip) throws IOException { 18 | String fname = 19 | w.myFile().isEmpty() ? 20 | ( 21 | (asZip && f.getName().toLowerCase().endsWith(".zip")) ? 22 | f.getName().substring(0, f.getName().length() - 4) : 23 | f.getName() 24 | ) : 25 | w.myFile(); 26 | scala.util.Either< String, scala.runtime.BoxedUnit > result = 27 | (asZip) ? 28 | org.openworm.trackercommons.ReadWrite.writeZip(w.toUnderlying(), f, fname) : 29 | org.openworm.trackercommons.ReadWrite.write(w.toUnderlying(), f, fname); 30 | if (result.isLeft()) throw new IOException(result.left().get()); 31 | } 32 | 33 | private static File anotherFileOrNull(File wfile, String me, String list[]) { 34 | if (list.length == 0) return null; 35 | String p = wfile.getPath(); 36 | String ext = p.substring(Math.max(p.length() - 5, 0)).toLowerCase(); 37 | if ( 38 | !(p.endsWith(me)) && 39 | !(ext.equalsIgnoreCase(".wcon") && p.substring(0, p.length()-ext.length()).endsWith(me)) 40 | ) throw new IllegalArgumentException("Mismatch between actual and specified file name"); 41 | if (p.endsWith(me)) { 42 | return new File(p.substring(0, p.length() - me.length()) + list[0]); 43 | } 44 | else { 45 | return new File(p.substring(0, p.length() - me.length() - ext.length()) + list[0] + ext); 46 | } 47 | } 48 | 49 | /** If we read this file from `wfile`, this will get the next one pointed to by the Wcon file. If 50 | * there is no previous file, this method will return `null`. (Be careful!) Note: `zip` in 51 | * the file name is not supported, so strip it off if it was there (even if it was a zip file). 52 | */ 53 | public static File nextFileOrNull(File wfile, Wcon w) { return anotherFileOrNull(wfile, w.myFile(), w.nextFiles()); } 54 | 55 | /** If we read this file from `wfile`, this will get the previous one pointed to by the Wcon file. If 56 | * there is no previous file, this method will return `null`. (Be careful!) Note: `zip` in 57 | * the file name is not supported so strip it off if it was there (even if it was a zip file). 58 | */ 59 | public static File previousFileOrNull(File wfile, Wcon w) { return anotherFileOrNull(wfile, w.myFile(), w.previousFiles()); } 60 | } 61 | -------------------------------------------------------------------------------- /src/java/build.sbt: -------------------------------------------------------------------------------- 1 | // Annoyingly, we need to know the resolvers of other projects 2 | // We depend on the Scala project: 3 | 4 | resolvers += "Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots" 5 | lazy val scalawcon = RootProject(file("../scala")) 6 | 7 | 8 | // This part handles the Java project 9 | 10 | lazy val root = (project in file(".")).dependsOn(scalawcon).settings( 11 | name := "JavaWcon", 12 | version := "0.1.0", 13 | scalaVersion := "2.11.8", 14 | assemblyJarName in assembly := "JavaWcon-0.1.0.jar", 15 | target in assembly := file("target") 16 | ) 17 | -------------------------------------------------------------------------------- /src/java/project/assembly.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.3") 2 | -------------------------------------------------------------------------------- /src/julia/README.md: -------------------------------------------------------------------------------- 1 | # Julia implementation of Tracker Commons 2 | 3 | Julia is a high-level language targeted at scientific data analysis and statistics. 4 | It shares similarities with both Matlab and Python in syntax, but can compile very 5 | fast (C-like) code when it can figure out that variables always contain primitive 6 | types (doubles, etc.). 7 | 8 | Thus, a long-term goal is for the Julia implementation to be fast. It is presently 9 | a work in progress, however, and speed presently takes third priority behind existence 10 | and correctness of capabilities. 11 | 12 | ## Installation 13 | 14 | This code has been tested with Julia 0.4.2. It is no longer being tested with Julia 0.3. 15 | 16 | To run this project you need to have [JSON.jl](https://github.com/JuliaLang/JSON.jl) installed. 17 | 18 | Just type `Pkg.add("JSON")` at the Julia prompt. 19 | 20 | ## Minimal reader / writer 21 | 22 | The minimal WCON reader is in `Minimal.jl`. If you want to build a lightweight WCON reader/writer 23 | for your own data, this may be a good place to start. To use it, just `include("Minimal.jl")` and then 24 | enter 25 | 26 | ```julia 27 | TrackerCommonsMinimal.read_wcon("my/path/to/my_data.wcon") 28 | ``` 29 | 30 | to get a `WormDataSet` structure which roughly reflects the minimal WCON data features. 31 | 32 | If you have a `WormDataSet`, you can write it out with 33 | 34 | ```julia 35 | TrackerCommonsMinimal.write_wcon("my/path/to/new_file.wcon") 36 | ``` 37 | 38 | ## Full-featured reader / writer 39 | 40 | ### Data I/O 41 | 42 | TODO: write examples. 43 | 44 | ### Data Types 45 | 46 | Worm data, including a single ID, plus a time series and x- and y-coordinate values, is 47 | represented in the `CommonWorm` type. 48 | 49 | TODO: finish section 50 | 51 | #### A note about Data Frames 52 | 53 | Julia contains a _Data Frame_ type, motivated by a similar type in R, that consists of 54 | tabular data with named column headers. Data frames are commonly used in data analysis 55 | and statistics, so it would seem that they would be a natural fit for worm tracking data. 56 | 57 | However, tracking data is not necessarily a convenient rectangular shape: worms may be 58 | tracked for different periods of time, and within one worm, some data may be static (which side is ventral), 59 | some may be a scalar for each time point (centroid), and other data may be vector valued (x coordinates of 60 | spine). Forcing the data into tabular form could thus be inefficient and require the user to 61 | deal with frequent missing data. 62 | 63 | Thus, the Tracker Commons implementation imports the data in custom structures. Users are 64 | encouraged to convert this to Data Frames in those cases where it suits their analysis. 65 | 66 | ### Unit Conversions 67 | 68 | TODO: finish section 69 | 70 | -------------------------------------------------------------------------------- /src/julia/src/Minimal.jl: -------------------------------------------------------------------------------- 1 | ## This file is part of Open Worm's Tracker Commons project and is distributed under the MIT license. 2 | ## Contents copyright (c) 2016 by Rex Kerr, Calico Life Sciences, and Open Worm. 3 | 4 | ############################################# 5 | # Example of a minimal WCON reader in Julia # 6 | ############################################# 7 | 8 | module TrackerCommonsMinimal 9 | 10 | import JSON 11 | 12 | type CommonWorm 13 | id 14 | t::Array{Float64,1} 15 | x::Array{Array{Float64,1},1} 16 | y::Array{Array{Float64,1},1} 17 | end 18 | 19 | type WormDataSet 20 | data::Array{CommonWorm,1} 21 | units::Dict{AbstractString,Any} 22 | end 23 | 24 | function make_dbl(a) 25 | x::Float64 = isa(a, Number) ? convert(Float64, a) : NaN 26 | x 27 | end 28 | 29 | function make_dbl_array(q::Any) 30 | if (typeof(q) <: Array) 31 | # Do it this way because map of Any array might still be Any 32 | result = zeros(length(q)) 33 | for i in 1:length(q) 34 | result[i] = make_dbl(q[i]) 35 | end 36 | result 37 | else [make_dbl(q)] 38 | end 39 | end 40 | 41 | function make_dbl_array(q::Any, n::Int64) 42 | if (typeof(q) <: Array) 43 | # Do it this way because map of Any array might still be Any 44 | result = Array(Float64, n) 45 | for i in 1:length(q) 46 | result[i] = make_dbl(q[i]) 47 | end 48 | result 49 | else fill(make_dbl(q), n) 50 | end 51 | end 52 | 53 | function make_dbl_array_array(q::Any, n::Int64) 54 | if (n == 1) 55 | if (length(q)==1 && typeof(q[1]) <: Array) 56 | Array[make_dbl_array(q[1])] 57 | else 58 | Array[make_dbl_array(q)] 59 | end 60 | else 61 | result = Array(Array{Float64, 1}, n) 62 | for i in 1:length(q) 63 | result[i] = make_dbl_array(q[i]) 64 | end 65 | result 66 | end 67 | end 68 | 69 | function get_a_worm(data::Dict{AbstractString, Any}) 70 | id = data["id"] 71 | t = make_dbl_array(data["t"]) 72 | ox = haskey(data, "ox") ? make_dbl_array(data["ox"], length(t)) : (haskey(data, "cx") ? make_dbl_array(data["cx"], length(t)) : zeros(length(t))) 73 | oy = haskey(data, "oy") ? make_dbl_array(data["oy"], length(t)) : (haskey(data, "cy") ? make_dbl_array(data["cy"], length(t)) : zeros(length(t))) 74 | x = make_dbl_array_array(data["x"], length(t)) 75 | y = make_dbl_array_array(data["y"], length(t)) 76 | if (length(t) != length(ox)) 77 | error("Length of t and x-offset or centroid do not match for worm $(id) at $(t[1])") 78 | end 79 | if (length(t) != length(oy)) 80 | error("Length of t and y-offset or centroid do not match for worm $(id) at $(t[1])") 81 | end 82 | if (length(t) != length(x)) 83 | error("Length of t and x data do not match for worm $(id) at $(t[1]): $(length(t)) vs $(length(x))") 84 | end 85 | if (length(t) != length(y)) 86 | error("Length of t and y data do not match for worm $(id) at $(t[1]): $(length(t)) vs $(length(y))") 87 | end 88 | for i in 1:length(t) 89 | if (length(x[i]) != length(y[i])) 90 | error("Different number of x and y values for data point $(i) at time $(t[i]) for worm $(id)") 91 | end 92 | x[i] = x[i] - ox[i] 93 | y[i] = y[i] - oy[i] 94 | end 95 | CommonWorm(id, t, x, y) 96 | end 97 | 98 | function read_wcon(filename::AbstractString) 99 | j = JSON.parsefile(filename) 100 | j = convert(Dict{AbstractString, Any}, j) 101 | u = convert(Dict{AbstractString, Any}, j["units"]) 102 | for x in ["t" "x" "y"] 103 | if (!haskey(u,x)) 104 | error("WCON file does not specify units for $(t)") 105 | end 106 | end 107 | d = j["data"] 108 | if (isa(d, Dict{AbstractString, Any})) 109 | d = [d] 110 | end 111 | if (!(typeof(d) <: Array)) 112 | error("WCON data section is not an array") 113 | end 114 | dd = [convert(Dict{AbstractString, Any}, x) for x in d] 115 | WormDataSet([get_a_worm(x) for x in dd], u) 116 | end 117 | 118 | function write_wcon(worms::WormDataSet, filename::AbstractString) 119 | h = open(filename, "w") 120 | JSON.print(h, Dict{AbstractString, Any}("units" => worms.units, "data" => worms.data)) 121 | close(h) 122 | end 123 | 124 | end 125 | -------------------------------------------------------------------------------- /src/julia/src/ReadBasic.jl: -------------------------------------------------------------------------------- 1 | ## This file is part of Open Worm's Tracker Commons project and is distributed under the MIT license. 2 | ## Contents copyright (c) 2016 by Rex Kerr, Calico Life Sciences, and Open Worm. 3 | 4 | 5 | ########################################### 6 | ### Get numeric data out of parsed JSON ### 7 | ########################################### 8 | 9 | function make_dbl(a) 10 | x::Float64 = isa(a, Number) ? convert(Float64, a) : NaN 11 | x 12 | end 13 | 14 | function make_dbl_array(q::Any) 15 | if (typeof(q) <: Array) 16 | # Do it this way because map of Any array might still be Any 17 | result = zeros(length(q)) 18 | for i in 1:length(q) 19 | result[i] = make_dbl(q[i]) 20 | end 21 | result 22 | else [make_dbl(q)] 23 | end 24 | end 25 | 26 | function make_dbl_array(q::Any, n::Int64) 27 | if (typeof(q) <: Array) 28 | # Do it this way because map of Any array might still be Any 29 | result = Array(Float64, n) 30 | for i in 1:length(q) 31 | result[i] = make_dbl(q[i]) 32 | end 33 | result 34 | else fill(make_dbl(q), n) 35 | end 36 | end 37 | 38 | function make_dbl_array_array(q::Any, n::Int64) 39 | if (n == 1) 40 | if (length(q)==1 && typeof(q[1]) <: Array) 41 | Array[make_dbl_array(q[1])] 42 | else 43 | Array[make_dbl_array(q)] 44 | end 45 | else 46 | result = Array(Array{Float64, 1}, n) 47 | for i in 1:length(q) 48 | result[i] = make_dbl_array(q[i]) 49 | end 50 | result 51 | end 52 | end 53 | 54 | function extract_custom(d :: Dict{AbstractString, Any}) 55 | dd = Dict{AbstractString, Any}() 56 | for (k,v) in d 57 | if startswith(k, "@") 58 | dd[k] = v 59 | end 60 | end 61 | dd 62 | end 63 | -------------------------------------------------------------------------------- /src/julia/src/ReadWrite.jl: -------------------------------------------------------------------------------- 1 | ## This file is part of Open Worm's Tracker Commons project and is distributed under the MIT license. 2 | ## Contents copyright (c) 2016 by Rex Kerr, Calico Life Sciences, and Open Worm. 3 | 4 | ################################## 5 | # WCON file I/O via JSON parser # 6 | ################################# 7 | 8 | 9 | import JSON 10 | 11 | function read_file(fullname :: AbstractString) 12 | result :: Union{AbstractString, DataSet} = "" 13 | j = JSON.parsefile(fullname) 14 | result = parsed_json_to_dataset(j, fullname) 15 | return result 16 | end 17 | 18 | function read_all_files(fullname :: AbstractString) 19 | result :: Union{AbstractString, Array{DataSet, 1}} = "" 20 | fwd = Array{DataSet, 1}() 21 | bkw = Array{DataSet, 1}() 22 | first = read_file(fullname) 23 | if isa(first, AbstractString) 24 | result = convert(AbstractString, first) 25 | return result 26 | end 27 | working :: DataSet = first 28 | while !working.prev_filename.isnull 29 | more = read_file(working.prev_filename.value) 30 | if isa(more, AbstractString) 31 | result = string("Read ", 1 + length(bkw), " files successfully but failed on ", working.prev_filename.value, ". ", convert(AbstractString, more)) 32 | return result 33 | end 34 | push!(bkw, more) 35 | working = more 36 | end 37 | working = first 38 | while !working.next_filename.isnull 39 | more = read_file(working.next_filename.value) 40 | if isa(more, AbstractString) 41 | result = string("Read ", 1 + length(bkw) + length(fwd), " files successfully but failed on ", working.next_filename.value, ". ", convert(AbstractString, more)) 42 | return result 43 | end 44 | push!(fwd, more) 45 | working = more 46 | end 47 | result = [reverse(bkw); [first]; fwd] 48 | return result 49 | end 50 | 51 | function write_file(ds :: DataSet, fullname :: Nullable{AbstractString}) 52 | j = convert_for_json(ds) 53 | fn = (fullname.isnull) ? ds.this_name : fullname.value 54 | fh = open(fn, "w") 55 | JSON.print(fh, j) 56 | close(fh) 57 | end 58 | -------------------------------------------------------------------------------- /src/julia/src/TrackerCommons.jl: -------------------------------------------------------------------------------- 1 | ## This file is part of Open Worm's Tracker Commons project and is distributed under the MIT license. 2 | ## Contents copyright (c) 2016 by Rex Kerr, Calico Life Sciences, and Open Worm. 3 | 4 | ################################################## 5 | # Julia module for reading and writing WCON data # 6 | ################################################## 7 | 8 | module TrackerCommons 9 | 10 | include("ReadBasic.jl") 11 | include("Units.jl") 12 | include("MetaData.jl") 13 | include("CommonWorm.jl") 14 | include("DataSet.jl") 15 | include("ReadWrite.jl") 16 | 17 | end # module TrackerCommons 18 | -------------------------------------------------------------------------------- /src/octave/INSTALL.md: -------------------------------------------------------------------------------- 1 | ##Installation 2 | 3 | ###Tested Platforms 4 | Ubuntu LTS 14.04 - run as a VirtualBox OS image on Mac OS X. 5 | Ubuntu LTS 14.04 AWS image 6 | 7 | ###Pre-requisites: 8 | 9 | As this Octave implementation of WCON is developed as a wrapper 10 | library around the Python implementation, it is first dependent on the 11 | installation instructions for Python as described in the [Python 12 | implementation](../Python/INSTALL.md). 13 | 14 | As of the current Octave implementation please follow the instructions 15 | for Python version 3.5, and using the source code from git. 16 | 17 | ###Octave-specific setup: 18 | 19 | The following instructions are best run as a bash shell script. Paths 20 | are relative, so the script should be safe to run from any folder. 21 | 22 | ``` 23 | #!/bin/bash 24 | # Pre-requisite - run python-install.sh 25 | PYTHON_VERSION=3.5 26 | MINICONDA_DIR=~/miniconda3 27 | 28 | export LC_ALL=en_US.UTF-8 29 | export LANG=en_US.UTF-8 30 | 31 | sudo apt-get install -y octave 32 | # liboctave-dev is required for mkoctfile 33 | sudo apt-get install -y liboctave-dev 34 | sudo apt-get install -y swig 35 | sudo apt-get install -y g++ 36 | sudo apt-get install -y make 37 | export PATH=$MINICONDA_DIR/bin:$PATH 38 | # Library paths to /lib/x86_64-linux-gnu and /usr/lib/x86_64-linux-gnu 39 | # are because the libgfortran.so.3 and libreadline.so.6 files installed 40 | # by miniconda3 are incompatible with the ones expected by swig and 41 | # octave. 42 | export LD_LIBRARY_PATH=/lib/x86_64-linux-gnu:/usr/lib/x86_64-linux-gnu:$MINICONDA_DIR/lib:$LD_LIBRARY_PATH 43 | cd ~/github/tracker-commons/src/octave/wrappers 44 | make 45 | cat >> ~/.profile < wconoct 47 | octave:2> worm1 = wconoct.load_from_file('../../../tests/multiworm.wcon') 48 | ``` 49 | 50 | ### Octave API 51 | 52 | The Octave API is not object oriented as in the case of 53 | Python. Instead object instances are handled in the form of integer 54 | handles in Octave. 55 | 56 | In the following example, worm1 is a handle to a WCONWorms object 57 | instance acquired after successfully loading the data from the file 58 | multiworm.wcon. 59 | 60 | ```bash 61 | octave:2> worm1 = wconoct.load_from_file('../../../tests/multiworm.wcon') 62 | ``` 63 | 64 | In general, object instance methods in the Python API require a valid 65 | object reference handle in the corresponding Octave API. For example: 66 | 67 | ```bash 68 | octave:3> wconoct.save_to_file(worm1,'output.wcon') 69 | ``` 70 | 71 | writes the instance worm1 out to file. 72 | 73 | The prototype currently does not add a prefix to methods for the 74 | Python class WCONWorms. It adds the "MU" prefix to methods for the 75 | Python class MeasurementUnit so there is no ambiguity for method names 76 | common to both classes like to_canon. 77 | 78 | The current API is as follows: 79 | 80 | ####Support Methods 81 | * initWrapper() - initializes the wrapper library, instantiates Python interpreter. 82 | * isNullHandle(int handle) - given handle, is it NULL? 83 | * isNoneHandle(int handle) - given handle, is it a Python None object? 84 | 85 | ####WCONWorms Methods 86 | * int load_from_file(string path) 87 | * save_to_file(int self, string path) 88 | * int to_canon(int self) - returns object instance that is a canonical version of self 89 | * int add(int self, int handle2) - merges the contents of self and handle2 and returns new object instance 90 | * boolean eq(int self, int handle2) 91 | * int units(int self) - returns list instance of units used in self. Note: list instances are not currently implemented. The handle is valid, but unuseable. 92 | * int metadata(int self) - returns metadata instance used in self. Note: metadata instances are not currently implemented. The handle is valid, but unuseable. 93 | * long num_worms(int self) 94 | * int worm_ids(int self) - returns list instance of worm ids. Note: list instances ar not currently implemented. The handle is valid, but unuseable. 95 | * int data_as_odict(int self) - returns pandas DataFrame object instance. Note: The handle is valid, but unuseable. 96 | 97 | ####MeasurementUnit Methods 98 | * int MU_create(string unit_string) 99 | * double MU_to_canon(int self, double value) 100 | * double MU_from_canon(int self, double value) 101 | * string MU_unit_string(int self) 102 | * string MU_canonical_unit_string(int self) 103 | -------------------------------------------------------------------------------- /src/octave/README.md.OLD: -------------------------------------------------------------------------------- 1 | ## Matlab/Octave Prototype for WCON API 2 | 3 | This is primarily an Octave prototype, but it is expected to be portable/ported 4 | to Matlab without any major compatibility issues, very probably with no changes. 5 | 6 | The prototype is currently tested against the following setup: 7 | 8 | Mac OS X (El Capitan) 9 | Octave 4.0.0_5 10 | 11 | ### Setup 12 | 13 | Install Octave as required. On Mac OS X, we recommend using homebrew: 14 | 15 | ```bash 16 | brew install octave 17 | ``` 18 | 19 | This package relies on the jsonlab library for JSON parsing capabilities. The 20 | current prototype expects the git repository to be installed in the "externals" 21 | folder. When setting up this package for the first time: 22 | 23 | ```bash 24 | cd externals 25 | git clone https://github.com/fangq/jsonlab 26 | ``` 27 | 28 | ### Running the Prototype 29 | 30 | ```bash 31 | cd driver 32 | octave main.m 33 | ``` 34 | 35 | ### Current Status 36 | 37 | This prototype is mostly incomplete. Some very 38 | basic functionality works, but all assumed API functions exist and are 39 | exercised by main.m in the "driver" folder. 40 | 41 | ### Intended API 42 | 43 | WCONWorms class object 44 | * to_canon() # convert data to canonical units 45 | * load_from_file(file) # loads from json (wcon) file 46 | * save_to_file(file) # writes to json (wcon) file. save_to_file will also invoke to_canon() 47 | * load(string) # loads from a literal string 48 | * is_equal(object1, object2) # probably need to invoke to_canon() on both first 49 | * file_equal(file1, file2) # do we really need this? 50 | * merge(object1, object2) # Exception is raised on conflict. What is a conflict - time overlap with incompatible movement? 51 | -------------------------------------------------------------------------------- /src/octave/examples/main.m: -------------------------------------------------------------------------------- 1 | addpath('../library:../externals/jsonlab'); 2 | 3 | origworm = WCONWorms(); 4 | result = isobject(origworm); 5 | disp(result); 6 | result = isa(origworm,'WCONWorms'); 7 | disp(result); 8 | result = isa(origworm,'InvalidClass'); 9 | disp(result); 10 | 11 | displayData(origworm); 12 | origworm = to_canon(origworm); 13 | 14 | % Octave does not seem to like side-effects. If you do not assign the return object, 15 | % the original object would not see the change. 16 | worm = load_from_file(origworm,'../../../tests/minimal.wcon'); 17 | displayData(worm); 18 | 19 | save_to_file(worm,'testing.wcon'); 20 | 21 | worm2 = load(worm,'{"obj":{"string":"value","array":[1,2,3]}}'); 22 | displayData(worm); 23 | displayData(worm2); 24 | 25 | is_equal(worm,worm2); 26 | is_equal(worm2,origworm); 27 | 28 | worm3 = merge(worm,worm2); 29 | -------------------------------------------------------------------------------- /src/octave/externals/README.md: -------------------------------------------------------------------------------- 1 | Folder for hosting all 3rd Party tools (like jsonlab) 2 | -------------------------------------------------------------------------------- /src/octave/library/WCONWorms.m: -------------------------------------------------------------------------------- 1 | classdef WCONWorms 2 | properties 3 | wcondata; 4 | end 5 | 6 | methods 7 | function obj = WCONWorms() 8 | % Pretend initial data for every object 9 | obj.wcondata=loadjson('{"obj":{"string":"value","array":[1,2,3]}}'); 10 | end 11 | 12 | function obj = displayData(obj) 13 | disp(obj.wcondata); 14 | end 15 | 16 | function obj = to_canon(obj) 17 | disp('Conversion to canonical form - unimplemented.'); 18 | end 19 | 20 | function obj = load_from_file(obj, filename) 21 | % disp(filename); 22 | dat = loadjson([filename]); 23 | obj.wcondata = dat; 24 | % disp(obj.wcondata); 25 | end 26 | 27 | function obj = save_to_file(obj, filename) 28 | % disp(filename); 29 | disp(['Output to [' filename '] - unimplemented.']); 30 | end 31 | 32 | function obj = load(obj, json_string) 33 | dat = loadjson(json_string); 34 | obj.wcondata = dat; 35 | end 36 | 37 | function ret = is_equal(obj, obj2) 38 | canon_obj = to_canon(obj); 39 | canon_obj2 = to_canon(obj2); 40 | if (isequal(canon_obj.wcondata,canon_obj2.wcondata)) 41 | ret = 1 42 | else 43 | ret = 0 44 | endif 45 | end 46 | 47 | function obj = merge(obj, obj2) 48 | disp('Merging two objects - unimplemented.'); 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /src/octave/tests/README.md: -------------------------------------------------------------------------------- 1 | Stub folder for tests that can be implemented for testing the library functionality 2 | under Travis-CI. 3 | TODO: Figure how that's done for octave. And if it is possible to handle Matlab 4 | licensing issues if we do decide to run tests against Matlab too. 5 | -------------------------------------------------------------------------------- /src/octave/wrappers/IDEAS.md: -------------------------------------------------------------------------------- 1 | Random thoughts on implementation and design decisions. 2 | 3 | 1. Consider a C++ static object to encapsulate wrapper so Python embedding initialization 4 | and finalization are automatically handled by the class constructor and destructor. 5 | 6 | 2. Consider Octave operator overloading for 2 WCONWorms objects. (see 7 | http://www.gnu.org/software/octave/doc/v4.0.1/Operator-Overloading.html) 8 | Note this requires that WCONWorms be implemented as a shell class object on Octave, 9 | which isn't necessarily a bad thing. 10 | 11 | 3. Ideally we'd want to keep global copies of references to key API Python objects 12 | that were only initialized once at init time. For example, making the calls to import 13 | the wrapper module, or creating the Python references to static class objects like 14 | WCONWorms. 15 | 16 | 4. Keep an eye out for additional complexity issues for when threads are used with Python. 17 | 18 | 5. We will need developer documentation on the Python folders to inform people maintaining 19 | code there, that there is some coupling to their code from over here. 20 | 21 | 6. Consider defining a PythonObjHandle type for type-safety issues when 22 | returning actual integers. 23 | 24 | 7. PyLong_FromLong is a Python 3 construct. Pay attention to that when 25 | considering Python 3 and Python 2 portability issues later. -------------------------------------------------------------------------------- /src/octave/wrappers/Makefile: -------------------------------------------------------------------------------- 1 | CPP=g++ 2 | AR=ar 3 | SWIG=swig 4 | MKOCTFILE=mkoctfile 5 | 6 | CFLAGS=-std=gnu++11 7 | 8 | PYTHON_VER=3.5 9 | PYTHON_CONFIG=python${PYTHON_VER}-config 10 | 11 | # For some bizarre reasons, config --ldflags will work for 12 | # the Mac the first time, and then subsequently fail to 13 | # include the path to the libraries 14 | PYTHON_PREFIX=$(shell ${PYTHON_CONFIG} --prefix) 15 | PYTHON_CFLAGS=$(shell ${PYTHON_CONFIG} --cflags) 16 | PYTHON_LDFLAGS=$(shell ${PYTHON_CONFIG} --ldflags) 17 | 18 | WRAPPER_OBJS=octaveWconPythonWrapper.o wrapperInternal.o \ 19 | wconOct_wrapperWCONWorms.o \ 20 | wconOct_wrapperMeasurementUnit.o 21 | COMMON_HEADERS=octaveWconPythonWrapper.h wrapperTypes.h 22 | 23 | WRAPPER_LIB=libWconOct.a libWconOct.so 24 | WRAPPER_LIB_LDFLAGS=-L. -lWconOct 25 | 26 | SWIG_MODULENAME=wconoct 27 | 28 | all: driver ${WRAPPER_LIB} ${SWIG_MODULENAME}.oct 29 | 30 | ${SWIG_MODULENAME}.oct: ${SWIG_MODULENAME}.cpp swigWrapper.c swigWrapper.h \ 31 | ${WRAPPER_LIB} 32 | $(MKOCTFILE) -o ${SWIG_MODULENAME} ${SWIG_MODULENAME}.cpp \ 33 | swigWrapper.c ${WRAPPER_LIB_LDFLAGS} 34 | 35 | ${SWIG_MODULENAME}.cpp: ${SWIG_MODULENAME}.i 36 | $(SWIG) -octave -o ${SWIG_MODULENAME}.cpp ${SWIG_MODULENAME}.i 37 | 38 | ${SWIG_MODULENAME}.i: wcon-oct-swig.template 39 | sed -e "s/SWIG_MOD_NAME/${SWIG_MODULENAME}/g" wcon-oct-swig.template > ${SWIG_MODULENAME}.i 40 | 41 | driver: driver.o ${WRAPPER_LIB} 42 | $(CPP) -o driver driver.o ${WRAPPER_LIB_LDFLAGS} 43 | 44 | libWconOct.a: ${WRAPPER_OBJS} 45 | $(AR) rcs libWconOct.a ${WRAPPER_OBJS} 46 | 47 | libWconOct.so: ${WRAPPER_OBJS} 48 | $(CPP) -shared -o libWconOct.so ${WRAPPER_OBJS} ${PYTHON_LDFLAGS} 49 | 50 | driver.o: driver.cpp 51 | $(CPP) $(CFLAGS) -c driver.cpp 52 | 53 | %.o: %.cpp ${COMMON_HEADERS} 54 | $(CPP) $(CFLAGS) -fPIC -c $< ${PYTHON_CFLAGS} 55 | 56 | clean: 57 | rm -f *~ *.o *.a *.so driver *.oct *.i ${SWIG_MODULENAME}.cpp 58 | -------------------------------------------------------------------------------- /src/octave/wrappers/NOTES.md: -------------------------------------------------------------------------------- 1 | Initial attempt with Python 3.5 was foiled on the Mac because I had 2 | forgotten that miniconda's psutil somehow does not appear to work with 3 | Mac OS X, and I had to fall back to Python 2.7 the last time. Going 4 | to put the Mac aside for a while, and try this on Ubuntu VirtualBox. 5 | -------------------------------------------------------------------------------- /src/octave/wrappers/driver.m: -------------------------------------------------------------------------------- 1 | wconoct; 2 | loaded_worm = wconoct.load_from_file('../../../tests/minimax.wcon'); 3 | wconoct.save_to_file(loaded_worm,'wrappertest.wcon'); 4 | disp(loaded_worm); 5 | canonical_worm = wconoct.to_canon(loaded_worm); 6 | disp(canonical_worm); 7 | wconoct.save_to_file(canonical_worm,'wrapperCanonical.wcon'); 8 | conflict_worm = wconoct.load_from_file('extra-test-data/minimax-conflict.wcon'); 9 | mergeable_worm = wconoct.load_from_file('extra-test-data/minimax-mergeable.wcon'); 10 | if (wconoct.eq(loaded_worm,canonical_worm) == 1) 11 | disp('Equal'); 12 | else 13 | disp('Not Equal'); 14 | endif 15 | added_worm = wconoct.add(loaded_worm,canonical_worm); 16 | if (wconoct.eq(loaded_worm,added_worm) == 1) 17 | disp('Equal'); 18 | else 19 | disp('Not Equal'); 20 | endif 21 | disp('Trying to merge data with conflict'); 22 | added_worm = wconoct.add(loaded_worm,conflict_worm); 23 | disp('Trying to merge compatible data'); 24 | added_worm = wconoct.add(loaded_worm,mergeable_worm); 25 | if (wconoct.eq(loaded_worm,added_worm) == 1) 26 | disp('Equal'); 27 | else 28 | disp('Not Equal'); 29 | endif 30 | 31 | units_data = wconoct.units(loaded_worm); 32 | num_units = wconoct.unitsDict_numElements(units_data); 33 | t_mu = wconoct.unitsDict_valueFromKey(units_data,"'t'"); 34 | t_unitStr = wconoct.MU_unit_string(t_mu); 35 | disp("units for 't' is:"); 36 | disp(t_unitStr); 37 | width_mu = wconoct.unitsDict_valueFromKey(units_data,"'width'"); 38 | width_unitStr = wconoct.MU_unit_string(width_mu); 39 | disp("units for 'width' is:"); 40 | disp(width_unitStr); 41 | width_canonUnitStr = wconoct.MU_canonical_unit_string(width_mu); 42 | disp("Canonical units for 'width' is:"); 43 | disp(width_canonUnitStr); 44 | age_mu = wconoct.unitsDict_valueFromKey(units_data,"'age'"); 45 | age_unitStr = wconoct.MU_unit_string(age_mu); 46 | disp("units for 'age' is:"); 47 | disp(age_unitStr); 48 | 49 | metadata_data = wconoct.metadata(loaded_worm); 50 | disp(metadata_data); 51 | if (wconoct.isNoneHandle(metadata_data) == 1) 52 | disp('metadata is empty'); 53 | else 54 | disp('Non-empty metadata'); 55 | endif 56 | minimal_worm = wconoct.load_from_file('../../../tests/minimal.wcon'); 57 | empty_metadata_data = wconoct.metadata(minimal_worm); 58 | disp(empty_metadata_data); 59 | if (wconoct.isNoneHandle(empty_metadata_data) == 1) 60 | disp('metadata is empty'); 61 | else 62 | disp('Non-empty metadata'); 63 | endif 64 | num_worms = wconoct.num_worms(loaded_worm); 65 | disp(num_worms); 66 | worm_ids = wconoct.worm_ids(loaded_worm); 67 | data_odict = wconoct.data_as_odict(loaded_worm); 68 | 69 | hours_unit = MU_create('h'); 70 | canon_val = MU_to_canon(hours_unit, 1.0); 71 | disp(canon_val); 72 | from_canon_val = MU_from_canon(hours_unit, 3600.0); 73 | disp(from_canon_val); 74 | disp(MU_unit_string(hours_unit)); 75 | disp(MU_canonical_unit_string(hours_unit)); 76 | -------------------------------------------------------------------------------- /src/octave/wrappers/driver.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.append('../../Python') 3 | 4 | import wcon 5 | from wcon import WCONWorms 6 | from wcon import MeasurementUnit 7 | 8 | worm = WCONWorms.load_from_file('../../../tests/minimax.wcon') 9 | worm.save_to_file('pythonNonWrapperTest.wcon', pretty_print=True) 10 | 11 | mergeable = WCONWorms.load_from_file('extra-test-data/minimax-mergeable.wcon') 12 | 13 | merged = worm + mergeable 14 | merged.save_to_file('pythonMerged.wcon', pretty_print=True) 15 | 16 | merged2 = mergeable + worm 17 | merged2.save_to_file('pythonMerged2.wcon', pretty_print=True) 18 | 19 | nometa = WCONWorms.load_from_file('../../../tests/minimal.wcon') 20 | print(nometa.metadata) 21 | -------------------------------------------------------------------------------- /src/octave/wrappers/example-octave/Makefile: -------------------------------------------------------------------------------- 1 | SWIG_MODULENAME=swigexample 2 | 3 | all: ${SWIG_MODULENAME}.oct 4 | 5 | ${SWIG_MODULENAME}.oct: ${SWIG_MODULENAME}.cpp example.c example.h 6 | mkoctfile -o ${SWIG_MODULENAME} ${SWIG_MODULENAME}.cpp example.c 7 | 8 | ${SWIG_MODULENAME}.cpp: ${SWIG_MODULENAME}.i 9 | swig -octave -o ${SWIG_MODULENAME}.cpp ${SWIG_MODULENAME}.i 10 | 11 | ${SWIG_MODULENAME}.i: swig-example.template 12 | sed -e "s/SWIG_MOD_NAME/${SWIG_MODULENAME}/g" swig-example.template > ${SWIG_MODULENAME}.i 13 | 14 | clean: 15 | rm -f *~ *.o ${SWIG_MODULENAME}.oct ${SWIG_MODULENAME}.i ${SWIG_MODULENAME}.cpp 16 | -------------------------------------------------------------------------------- /src/octave/wrappers/example-octave/README.md: -------------------------------------------------------------------------------- 1 | This folder hosts experimental sandbox examples for interfacing 2 | Octave with C/C++ using the swig tool. 3 | 4 | It is extremely simple, but it serves to exercise important 5 | functionality for: 6 | 7 | 1. The ability to invoke a C/C++ function from Octave. 8 | 2. The ability to maintain and manipulate persistent global state. 9 | 3. The ability to invoke C/C++ functions that manipulate #2. 10 | 11 | The idea behind the above functionality for an Octave-WCON wrapper 12 | around the Python (or any other language with C/C++ bindings) is 13 | the ability of the wrapper to not only invoke code from the host 14 | library, but also to handle the necessary book-keeping for any 15 | translation issues. As an example, the current design decision on 16 | mapping Python objects to Octave is to make use of integer handles, 17 | so there has to be some book keeping done when crossing the 18 | language boundaries. -------------------------------------------------------------------------------- /src/octave/wrappers/example-octave/example.c: -------------------------------------------------------------------------------- 1 | int Foo = 2; 2 | 3 | /* obviously this is not actually gcd, but that's not important */ 4 | int gcd(int x, int y) { 5 | return x * y; 6 | } 7 | 8 | void incGlobal(int x) { 9 | Foo += x; 10 | } 11 | -------------------------------------------------------------------------------- /src/octave/wrappers/example-octave/example.h: -------------------------------------------------------------------------------- 1 | extern int Foo; 2 | int gcd(int x, int y); 3 | void incGlobal(int x); 4 | -------------------------------------------------------------------------------- /src/octave/wrappers/example-octave/swig-example.template: -------------------------------------------------------------------------------- 1 | %module SWIG_MOD_NAME 2 | %{ 3 | #include "example.h" 4 | %} 5 | int gcd(int x, int y); 6 | void incGlobal(int x); 7 | extern int Foo; -------------------------------------------------------------------------------- /src/octave/wrappers/example-python/Makefile: -------------------------------------------------------------------------------- 1 | CPP=g++ 2 | 3 | PYTHON_VER=3.5 4 | PYTHON_CONFIG=python${PYTHON_VER}-config 5 | 6 | # For some bizarre reasons, config --ldflags will work for 7 | # the Mac the first time, and then subsequently fail to 8 | # include the path to the libraries 9 | PYTHON_PREFIX=$(shell ${PYTHON_CONFIG} --prefix) 10 | PYTHON_CFLAGS=$(shell ${PYTHON_CONFIG} --cflags) 11 | #PYTHON_LDFLAGS=-L${PYTHON_PREFIX}/lib $(shell ${PYTHON_CONFIG} --ldflags) 12 | PYTHON_LDFLAGS=$(shell ${PYTHON_CONFIG} --ldflags) 13 | 14 | WRAPPER_OBJS=octaveWconPythonWrapper.o 15 | DRIVER_OBJS=driver.o 16 | 17 | all: test 18 | 19 | test: ${WRAPPER_OBJS} ${DRIVER_OBJS} 20 | $(CPP) -o test ${WRAPPER_OBJS} ${DRIVER_OBJS} ${PYTHON_LDFLAGS} 21 | # $(CPP) -o test -Wl,-rpath,${PYTHON_PREFIX}/lib ${WRAPPER_OBJS} ${DRIVER_OBJS} ${PYTHON_LDFLAGS} 22 | 23 | ${WRAPPER_OBJS}: octaveWconPythonWrapper.cpp octaveWconPythonWrapper.h 24 | $(CPP) -c octaveWconPythonWrapper.cpp ${PYTHON_CFLAGS} 25 | 26 | ${DRIVER_OBJS}: driver.cpp 27 | $(CPP) -c driver.cpp 28 | 29 | clean: 30 | rm -f *~ *.o test 31 | -------------------------------------------------------------------------------- /src/octave/wrappers/example-python/README.md: -------------------------------------------------------------------------------- 1 | This example folder will house code for the basics of python embedding in C/C++. 2 | The goal is to set this up as a concrete example for how an Octave can conceivably 3 | invoke Python functionality via the C/C++ bindings. 4 | -------------------------------------------------------------------------------- /src/octave/wrappers/example-python/driver.cpp: -------------------------------------------------------------------------------- 1 | #include "octaveWconPythonWrapper.h" 2 | 3 | #include 4 | using namespace std; 5 | 6 | void myfoo(int n); 7 | void mybar(int n); 8 | 9 | int main(int argc, char **argv) { 10 | myfoo(2); 11 | mybar(2); 12 | foo(2); 13 | bar(2); 14 | } 15 | 16 | void myfoo(int n) { 17 | cout << "In Local foo " << n << endl; 18 | } 19 | 20 | void mybar(int n) { 21 | cout << "In Local bar " << n*2 << endl; 22 | } 23 | -------------------------------------------------------------------------------- /src/octave/wrappers/example-python/octaveWconPythonWrapper.cpp: -------------------------------------------------------------------------------- 1 | #include "octaveWconPythonWrapper.h" 2 | 3 | #include 4 | #include 5 | using namespace std; 6 | 7 | extern "C" void initOctaveWconPython() { 8 | static bool isInitialized = false; 9 | if (!isInitialized) { 10 | cout << "Initializing Embedded Python Interpreter" << endl; 11 | Py_Initialize(); 12 | PyRun_SimpleString("import sys; sys.path.append('pythonLib')\n"); 13 | isInitialized = true; 14 | } 15 | } 16 | 17 | extern "C" void foo(int n) { 18 | PyObject *pName, *pModule, *pFunc; 19 | PyObject *pErr; 20 | 21 | initOctaveWconPython(); 22 | cout << "In C++ Wrapper foo with input " << n << endl; 23 | pName = PyUnicode_FromString("octaveWconTest"); 24 | pModule = PyImport_Import(pName); 25 | Py_DECREF(pName); 26 | if (pModule != NULL) { 27 | pFunc = PyObject_GetAttrString(pModule, "myfoo"); 28 | if ((pFunc != NULL) && (PyCallable_Check(pFunc) == 1)) { 29 | PyObject_CallFunctionObjArgs(pFunc, PyLong_FromLong(long(n)), NULL); 30 | // pErr does not need its reference count decremented as we do not own it 31 | pErr = PyErr_Occurred(); 32 | if (pErr != NULL) { 33 | PyErr_Print(); 34 | } 35 | } else { 36 | Py_XDECREF(pFunc); 37 | cout << "ERROR: Function foo failed to load" << endl; 38 | } 39 | } else { 40 | Py_XDECREF(pModule); 41 | cout << "ERROR: Module octaveWconTest not found" << endl; 42 | } 43 | } 44 | 45 | extern "C" void bar(int n) { 46 | PyObject *pName, *pModule, *pFunc; 47 | PyObject *pErr; 48 | 49 | initOctaveWconPython(); 50 | cout << "In C++ Wrapper foo with input " << n << endl; 51 | pName = PyUnicode_FromString("octaveWconTest"); 52 | pModule = PyImport_Import(pName); 53 | Py_DECREF(pName); 54 | if (pModule != NULL) { 55 | pFunc = PyObject_GetAttrString(pModule, "mybar"); 56 | if ((pFunc != NULL) && (PyCallable_Check(pFunc) == 1)) { 57 | PyObject_CallFunctionObjArgs(pFunc, PyLong_FromLong(long(n)), NULL); 58 | // pErr does not need its reference count decremented as we do not own it 59 | pErr = PyErr_Occurred(); 60 | if (pErr != NULL) { 61 | PyErr_Print(); 62 | } 63 | } else { 64 | Py_XDECREF(pFunc); 65 | cout << "ERROR: Function bar failed to load" << endl; 66 | } 67 | } else { 68 | Py_XDECREF(pModule); 69 | cout << "ERROR: Module octaveWconTest not found" << endl; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/octave/wrappers/example-python/octaveWconPythonWrapper.h: -------------------------------------------------------------------------------- 1 | extern "C" void initOctaveWconPython(); 2 | 3 | extern "C" void foo(int n); 4 | extern "C" void bar(int n); 5 | -------------------------------------------------------------------------------- /src/octave/wrappers/example-python/pythonLib/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openworm/tracker-commons/e5a12d3213839e9307f9df5e0973201fdcfeca6e/src/octave/wrappers/example-python/pythonLib/__init__.py -------------------------------------------------------------------------------- /src/octave/wrappers/example-python/pythonLib/octaveWconTest.py: -------------------------------------------------------------------------------- 1 | def myfoo(n): 2 | print("foo " + str(n)) 3 | return 4 | 5 | 6 | def mybar(n): 7 | print("bar " + str(n * 2)) 8 | return 9 | 10 | 11 | def mytest(): 12 | print("in mytest") 13 | return 5 14 | -------------------------------------------------------------------------------- /src/octave/wrappers/example-python/pythonTest.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.append('pythonLib') 3 | import octaveWconTest 4 | 5 | if __name__ == "__main__": 6 | octaveWconTest.myfoo(5) 7 | octaveWconTest.mybar(2) 8 | -------------------------------------------------------------------------------- /src/octave/wrappers/extra-test-data/README.md: -------------------------------------------------------------------------------- 1 | The only reason this folder exists is to have data items 2 | that replicate failure/successful merging conditions 3 | described in the Python tests for such conditions. 4 | 5 | It is just a pain in the butt in C/C++ to modify those 6 | pieces of data on-the-fly. 7 | -------------------------------------------------------------------------------- /src/octave/wrappers/extra-test-data/minimax-conflict.wcon: -------------------------------------------------------------------------------- 1 | { 2 | "metadata":{ 3 | "lab":{"location":"CRB, room 5020", "name":"Behavioural Genomics" }, 4 | "who":"Firstname Lastname", 5 | "timestamp":"2012-04-23T18:25:43.511Z", 6 | "temperature":22, 7 | "humidity":40.2, 8 | "arena":{ "type":"petri", "size":35, "units":"mm" }, 9 | "food":"none", 10 | "media":"agarose", 11 | "sex":"hermaphrodite", 12 | "stage":"adult", 13 | "age":18.511, 14 | "strain":"CB4856", 15 | "image_orientation":"imaged onto agar", 16 | "protocol":"text description of protocol", 17 | "software":{ 18 | "tracker":{ "name":"Software Name", "version":"1.3.0"}, 19 | "featureID":"@OMG" 20 | }, 21 | "settings":"Any valid JSON entry with hardware and software configuration can go here" 22 | }, 23 | "units":{ 24 | "t":"s", 25 | "x":"mm", 26 | "y":"m", 27 | "ox":"mm", 28 | "oy":"mm", 29 | "speed":"mm/s", 30 | "curvature":"1/mm", 31 | "width":"mm", 32 | "humidity":"%", 33 | "temperature":"C", 34 | "age":"h" 35 | }, 36 | "data":[ 37 | { "id":"4", "t":1.4, "x":[1215.11, 1216.14, 1217.12], "y":[234.89, 265.23, 235.08] }, 38 | { "id":"1", "t":[1.3,1.5], "x":[[1,1,1],[null, 1216.14, 1217.12]], 39 | "y":[[2,2,2],[234.89, 265.23, 235.08]], "ox":[5000,5001], "oy":0, "head":"R" }, 40 | { "id":"2", "t":1.4, "x":[125.11, 126.14, 117.12], "y":[23.3, 22.23, 21135.08] }, 41 | { "id":"1", "t":1.4, "x":[4000.00, 1216.14, 1217.12], "y":[234.89, 265.23, 235.08]}, 42 | { "id":"1", "t":1.3, "x":[1, 1, 1], "y":[2, 2, 2], "head":"R", "@OMG":{"speed":[55,55,55]}}, 43 | { "id":"2", "t":1.5, "x":[1215.11, 1216.14, 1217.12], "y":[234.89, 265.23, 235.08], "head":"L", "ventral":"CW" }, 44 | { "id":"1", "t":2.5, "x":[1215.11, 1216.14, 1217.12, 1234.3, 3423.2, 3423.1], "y":[234.89, 265.23, 235.08,342.1,-323.2,23] } 45 | ], 46 | "@OMG": 5 47 | } -------------------------------------------------------------------------------- /src/octave/wrappers/extra-test-data/minimax-mergeable.wcon: -------------------------------------------------------------------------------- 1 | { 2 | "metadata":{ 3 | "lab":{"location":"CRB, room 5020", "name":"Behavioural Genomics" }, 4 | "who":"Firstname Lastname", 5 | "timestamp":"2012-04-23T18:25:43.511Z", 6 | "temperature":22, 7 | "humidity":40.2, 8 | "arena":{ "type":"petri", "size":35, "units":"mm" }, 9 | "food":"none", 10 | "media":"agarose", 11 | "sex":"hermaphrodite", 12 | "stage":"adult", 13 | "age":18.511, 14 | "strain":"CB4856", 15 | "image_orientation":"imaged onto agar", 16 | "protocol":"text description of protocol", 17 | "software":{ 18 | "tracker":{ "name":"Software Name", "version":"1.3.0"}, 19 | "featureID":"@OMG" 20 | }, 21 | "settings":"Any valid JSON entry with hardware and software configuration can go here" 22 | }, 23 | "units":{ 24 | "t":"s", 25 | "x":"mm", 26 | "y":"m", 27 | "ox":"mm", 28 | "oy":"mm", 29 | "speed":"mm/s", 30 | "curvature":"1/mm", 31 | "width":"mm", 32 | "humidity":"%", 33 | "temperature":"C", 34 | "age":"h" 35 | }, 36 | "data":[ 37 | { "id":"5", "t":3.5, "x":[1215.11, 1216.14, 1217.12, 1234.3, 3423.2, 3423.1], "y":[234.89, 265.23, 235.08,342.1,-323.2,23] } 38 | ], 39 | "@OMG": 5 40 | } 41 | -------------------------------------------------------------------------------- /src/octave/wrappers/octaveWconPythonWrapper.cpp: -------------------------------------------------------------------------------- 1 | #include "octaveWconPythonWrapper.h" 2 | 3 | #include 4 | 5 | #include 6 | #include // for srand and rand 7 | using namespace std; 8 | 9 | // Included here because we need declarations from Python.h 10 | #include "wrapperInternal.h" 11 | 12 | PyObject *wrapperGlobalModule=NULL; 13 | PyObject *wrapperGlobalWCONWormsClassObj=NULL; 14 | PyObject *wrapperGlobalMeasurementUnitClassObj=NULL; 15 | 16 | // Am exposing this as a wrapper interface method 17 | // because it is conceivable a user or some 18 | // middleware tool might want to explicitly 19 | // invoke the initializer. 20 | extern "C" void wconOct_initWrapper(WconOctError *err) { 21 | static bool isInitialized = false; 22 | 23 | PyObject *pErr; 24 | 25 | // Always check regardless of initialization. 26 | // NOTE: This works based on the requirement that every exposed API 27 | // method MUST invoke an initialization check. 28 | // The only exceptions are direct query methods that do not require 29 | // an initialized runtime, like isNullHandle. 30 | wrapInternalCheckErrorVariable(err); 31 | if (!isInitialized) { 32 | cout << "Initializing Embedded Python Interpreter" << endl; 33 | // initializing random number generator 34 | srand(1337); 35 | Py_Initialize(); 36 | PyRun_SimpleString("import sys; sys.path.append('../../Python')\n"); 37 | 38 | wrapperGlobalModule = PyImport_Import(PyUnicode_FromString("wcon")); 39 | pErr = PyErr_Occurred(); 40 | if (pErr != NULL) { 41 | PyErr_Print(); 42 | Py_XDECREF(wrapperGlobalModule); 43 | *err = FAILED; 44 | return; 45 | } 46 | 47 | wrapperGlobalWCONWormsClassObj = 48 | PyObject_GetAttrString(wrapperGlobalModule,"WCONWorms"); 49 | pErr = PyErr_Occurred(); 50 | if (pErr != NULL) { 51 | PyErr_Print(); 52 | Py_XDECREF(wrapperGlobalModule); 53 | Py_XDECREF(wrapperGlobalWCONWormsClassObj); 54 | *err = FAILED; 55 | return; 56 | } 57 | 58 | wrapperGlobalMeasurementUnitClassObj = 59 | PyObject_GetAttrString(wrapperGlobalModule,"MeasurementUnit"); 60 | pErr = PyErr_Occurred(); 61 | if (pErr != NULL) { 62 | PyErr_Print(); 63 | Py_XDECREF(wrapperGlobalModule); 64 | Py_XDECREF(wrapperGlobalWCONWormsClassObj); 65 | Py_XDECREF(wrapperGlobalMeasurementUnitClassObj); 66 | *err = FAILED; 67 | return; 68 | } 69 | isInitialized = true; 70 | } 71 | 72 | *err = SUCCESS; 73 | return; 74 | } 75 | 76 | extern "C" int wconOct_isNullHandle(WconOctHandle handle) { 77 | if (handle == WCONOCT_NULL_HANDLE) { 78 | return 1; 79 | } else { 80 | return 0; 81 | } 82 | } 83 | 84 | extern "C" WconOctHandle wconOct_makeNullHandle() { 85 | return WCONOCT_NULL_HANDLE; 86 | } 87 | 88 | extern "C" int wconOct_isNoneHandle(WconOctHandle handle) { 89 | if (handle == WCONOCT_NONE_HANDLE) { 90 | return 1; 91 | } else { 92 | return 0; 93 | } 94 | } 95 | 96 | -------------------------------------------------------------------------------- /src/octave/wrappers/octaveWconPythonWrapper.h: -------------------------------------------------------------------------------- 1 | #ifndef __OCTAVE_WCON_PYTHON_WRAPPER_H_ 2 | #define __OCTAVE_WCON_PYTHON_WRAPPER_H_ 3 | 4 | #include "wrapperTypes.h" 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif /* __cplusplus */ 9 | 10 | /* Support Methods */ 11 | void wconOct_initWrapper(WconOctError *err); 12 | int wconOct_isNullHandle(WconOctHandle handle); 13 | WconOctHandle wconOct_makeNullHandle(); 14 | int wconOct_isNoneHandle(WconOctHandle handle); 15 | 16 | /* WCONWorms */ 17 | WconOctHandle wconOct_static_WCONWorms_load_from_file(WconOctError *err, 18 | const char *wconpath); 19 | void wconOct_WCONWorms_save_to_file(WconOctError *err, 20 | const WconOctHandle selfHandle, 21 | const char *output_path, 22 | int pretty_print, 23 | int compressed); 24 | 25 | WconOctHandle wconOct_WCONWorms_to_canon(WconOctError *err, 26 | const WconOctHandle selfHandle); 27 | WconOctHandle wconOct_WCONWorms_add(WconOctError *err, 28 | const WconOctHandle selfHandle, 29 | const WconOctHandle handle); 30 | int wconOct_WCONWorms_eq(WconOctError *err, 31 | const WconOctHandle selfHandle, 32 | const WconOctHandle handle); 33 | WconOctUnitsDict *wconOct_WCONWorms_units(WconOctError *err, 34 | const WconOctHandle selfHandle); 35 | WconOctHandle wconOct_WCONWorms_metadata(WconOctError *err, 36 | const WconOctHandle selfHandle); 37 | WconOctHandle wconOct_WCONWorms_data(WconOctError *err, 38 | const WconOctHandle selfHandle); 39 | long wconOct_WCONWorms_num_worms(WconOctError *err, 40 | const WconOctHandle selfHandle); 41 | WconOctHandle wconOct_WCONWorms_worm_ids(WconOctError *err, 42 | const WconOctHandle selfHandle); 43 | WconOctHandle wconOct_WCONWorms_data_as_odict(WconOctError *err, 44 | const WconOctHandle selfHandle); 45 | 46 | /* MeasurementUnit */ 47 | WconOctHandle wconOct_static_MeasurementUnit_create(WconOctError *err, 48 | const char *unitStr); 49 | double wconOct_MeasurementUnit_to_canon(WconOctError *err, 50 | const WconOctHandle selfHandle, 51 | const double val); 52 | double wconOct_MeasurementUnit_from_canon(WconOctError *err, 53 | const WconOctHandle selfHandle, 54 | const double val); 55 | const char *wconOct_MeasurementUnit_unit_string(WconOctError *err, 56 | const WconOctHandle selfHandle); 57 | const char *wconOct_MeasurementUnit_canonical_unit_string(WconOctError *err, 58 | const WconOctHandle selfHandle); 59 | 60 | #ifdef __cplusplus 61 | } 62 | #endif /* __cplusplus */ 63 | 64 | #endif /* __OCTAVE_WCON_PYTHON_WRAPPER_H_ */ 65 | -------------------------------------------------------------------------------- /src/octave/wrappers/swigWrapper.h: -------------------------------------------------------------------------------- 1 | #ifndef __SWIG_WRAPPER_H_ 2 | #define __SWIG_WRAPPER_H_ 3 | 4 | #include "octaveWconPythonWrapper.h" 5 | 6 | void initWrapper(); 7 | int isNullHandle(int handle); 8 | int isNoneHandle(int handle); 9 | 10 | int load_from_file(const char *path); 11 | void save_to_file(int selfHandle, const char *path); 12 | int to_canon(int selfHandle); 13 | int add(int selfHandle, int handle2); 14 | int eq(int selfHandle, int handle2); 15 | WconOctUnitsDict *units(int selfHandle); 16 | int metadata(int selfHandle); 17 | long num_worms(int selfHandle); 18 | int worm_ids(int selfHandle); 19 | int data_as_odict(int selfHandle); 20 | 21 | int MU_create(const char *unitStr); 22 | double MU_to_canon(int selfHandle, double value); 23 | double MU_from_canon(int selfHandle, double value); 24 | const char *MU_unit_string(int selfHandle); 25 | const char *MU_canonical_unit_string(int selfHandle); 26 | 27 | 28 | int unitsDict_numElements(WconOctUnitsDict *dictionary); 29 | int unitsDict_valueFromKey(WconOctUnitsDict *dictionary, 30 | const char *key); 31 | 32 | #endif /* __SWIG_WRAPPER_H_ */ 33 | -------------------------------------------------------------------------------- /src/octave/wrappers/test-octave-exceptions/Makefile: -------------------------------------------------------------------------------- 1 | all: exception_order.oct minimal_exception.oct 2 | 3 | minimal: minimal_exception.oct 4 | 5 | c: c_exception.oct 6 | 7 | exception_order.oct: exception_order.cpp 8 | mkoctfile -o exception_order exception_order.cpp 9 | 10 | exception_order.cpp: test_exception.i 11 | swig -octave -c++ -o exception_order.cpp test_exception.i 12 | 13 | minimal_exception.oct: minimal_exception.cpp 14 | mkoctfile -o minimal_exception minimal_exception.cpp 15 | 16 | minimal_exception.cpp: minimal_cpp.i 17 | swig -octave -c++ -o minimal_exception.cpp minimal_cpp.i 18 | 19 | c_exception.oct: c_exception.cpp 20 | mkoctfile -o c_exception c_exception.cpp 21 | 22 | c_exception.cpp: minimal_c.i 23 | swig -octave -o c_exception.cpp minimal_c.i 24 | 25 | clean: 26 | rm -f *~ *.o *.oct *.cpp 27 | 28 | -------------------------------------------------------------------------------- /src/octave/wrappers/test-octave-exceptions/README.md: -------------------------------------------------------------------------------- 1 | Test code from the SWIG folks for propagating exceptions from C++ to 2 | Octave. The code base is entirely from https://github.com/swig/swig. 3 | 4 | Examples/test-suite/octave/exception_order_runme.m 5 | Examples/test-suite/exception_order.i 6 | -------------------------------------------------------------------------------- /src/octave/wrappers/test-octave-exceptions/exception_order_runme.m: -------------------------------------------------------------------------------- 1 | exception_order 2 | 3 | function check_lasterror(expected) 4 | if (!strcmp(lasterror.message, expected)) 5 | # Take account of older versions prefixing with "error: " and adding a newline at the end 6 | if (!strcmp(regexprep(lasterror.message, 'error: (.*)\n$', '$1'), expected)) 7 | error(["Bad exception order. Expected: \"", expected, "\" Got: \"", lasterror.message, "\""]) 8 | endif 9 | endif 10 | endfunction 11 | 12 | a = A() 13 | 14 | try 15 | a.foo() 16 | catch 17 | check_lasterror("C++ side threw an exception of type E1") 18 | end_try_catch 19 | 20 | try 21 | a.bar() 22 | catch 23 | check_lasterror("C++ side threw an exception of type E2") 24 | end_try_catch 25 | 26 | try 27 | a.foobar() 28 | catch 29 | check_lasterror("postcatch unknown (SWIG_RuntimeError)") 30 | end_try_catch 31 | 32 | try 33 | a.barfoo(1) 34 | catch 35 | check_lasterror("C++ side threw an exception of type E1") 36 | end_try_catch 37 | 38 | try 39 | a.barfoo(2) 40 | catch 41 | check_lasterror("C++ side threw an exception of type E2 *") 42 | end_try_catch 43 | -------------------------------------------------------------------------------- /src/octave/wrappers/test-octave-exceptions/minimal_c.i: -------------------------------------------------------------------------------- 1 | %module c_exception 2 | 3 | %include "exception.i" 4 | 5 | /* 6 | last resource, catch everything but don't override 7 | user's throw declarations. 8 | */ 9 | 10 | #if defined(SWIGUTL) 11 | %exception { 12 | try { 13 | $action 14 | } catch(...) { 15 | SWIG_exception_fail(SWIG_RuntimeError,"postcatch unknown"); 16 | } 17 | } 18 | #elif defined(SWIGGO) && defined(SWIGGO_GCCGO) 19 | %exception %{ 20 | try { 21 | $action 22 | #ifdef __GNUC__ 23 | } catch (__cxxabiv1::__foreign_exception&) { 24 | throw; 25 | #endif 26 | } catch(...) { 27 | SWIG_exception(SWIG_RuntimeError,"postcatch unknown"); 28 | } 29 | %} 30 | #else 31 | %exception { 32 | try { 33 | $action 34 | } catch(...) { 35 | SWIG_exception(SWIG_RuntimeError,"postcatch unknown"); 36 | } 37 | } 38 | #endif 39 | 40 | %inline %{ 41 | struct E1 42 | { 43 | }; 44 | 45 | struct E2 46 | { 47 | }; 48 | 49 | /* caught by %postexception */ 50 | int foo() 51 | { 52 | throw E1(); 53 | return 0; 54 | } 55 | 56 | /* caught by %postexception */ 57 | int bar() 58 | { 59 | throw E2(); 60 | return 0; 61 | } 62 | %} 63 | -------------------------------------------------------------------------------- /src/octave/wrappers/test-octave-exceptions/minimal_c.m: -------------------------------------------------------------------------------- 1 | c_exception 2 | 3 | function print_and_continue() 4 | disp("Caught Exception: "),disp(lasterror.message) 5 | endfunction 6 | 7 | try 8 | c_exception.foo() 9 | catch 10 | print_and_continue() 11 | end_try_catch 12 | 13 | try 14 | c_exception.bar() 15 | catch 16 | print_and_continue() 17 | end_try_catch 18 | 19 | c_exception.foo() 20 | -------------------------------------------------------------------------------- /src/octave/wrappers/test-octave-exceptions/minimal_cpp.i: -------------------------------------------------------------------------------- 1 | %module minimal_exception 2 | 3 | %include "exception.i" 4 | 5 | %inline %{ 6 | struct E1 7 | { 8 | }; 9 | 10 | struct E2 11 | { 12 | }; 13 | 14 | struct E3 15 | { 16 | }; 17 | 18 | struct A 19 | { 20 | /* caught by the user's throw definition */ 21 | int foo() throw(E1) 22 | { 23 | throw E1(); 24 | return 0; 25 | } 26 | 27 | int bar() throw(E2) 28 | { 29 | throw E2(); 30 | return 0; 31 | } 32 | 33 | int foobar() 34 | { 35 | return 1337; 36 | } 37 | 38 | int barfoo() 39 | { 40 | return 42; 41 | } 42 | }; 43 | %} 44 | 45 | -------------------------------------------------------------------------------- /src/octave/wrappers/test-octave-exceptions/minimal_cpp.m: -------------------------------------------------------------------------------- 1 | minimal_exception 2 | 3 | function check_lasterror(expected) 4 | if (!strcmp(lasterror.message, expected)) 5 | # Take account of older versions prefixing with "error: " and adding a newline at the end 6 | if (!strcmp(regexprep(lasterror.message, 'error: (.*)\n$', '$1'), expected)) 7 | error(["Bad exception order. Expected: \"", expected, "\" Got: \"", lasterror.message, "\""]) 8 | endif 9 | endif 10 | endfunction 11 | 12 | a = A() 13 | 14 | try 15 | a.foo() 16 | catch 17 | check_lasterror("C++ side threw an exception of type E1") 18 | end_try_catch 19 | 20 | try 21 | a.bar() 22 | catch 23 | check_lasterror("C++ side threw an exception of type E2") 24 | end_try_catch 25 | 26 | elite = a.foobar() 27 | a.bar() 28 | hitchhiker = a.barfoo() 29 | -------------------------------------------------------------------------------- /src/octave/wrappers/test-octave-exceptions/test_exception.i: -------------------------------------------------------------------------------- 1 | %module exception_order 2 | 3 | %warnfilter(SWIGWARN_RUBY_WRONG_NAME); 4 | 5 | #if defined(SWIGGO) && defined(SWIGGO_GCCGO) 6 | %{ 7 | #ifdef __GNUC__ 8 | #include 9 | #endif 10 | %} 11 | #endif 12 | 13 | %include "exception.i" 14 | 15 | %{ 16 | #if defined(_MSC_VER) 17 | #pragma warning(disable: 4290) // C++ exception specification ignored except to indicate a function is not __declspec(nothrow) 18 | #endif 19 | %} 20 | 21 | /* 22 | last resource, catch everything but don't override 23 | user's throw declarations. 24 | */ 25 | 26 | #if defined(SWIGUTL) 27 | %exception { 28 | try { 29 | $action 30 | } catch(...) { 31 | SWIG_exception_fail(SWIG_RuntimeError,"postcatch unknown"); 32 | } 33 | } 34 | #elif defined(SWIGGO) && defined(SWIGGO_GCCGO) 35 | %exception %{ 36 | try { 37 | $action 38 | #ifdef __GNUC__ 39 | } catch (__cxxabiv1::__foreign_exception&) { 40 | throw; 41 | #endif 42 | } catch(...) { 43 | SWIG_exception(SWIG_RuntimeError,"postcatch unknown"); 44 | } 45 | %} 46 | #else 47 | %exception { 48 | try { 49 | $action 50 | } catch(...) { 51 | SWIG_exception(SWIG_RuntimeError,"postcatch unknown"); 52 | } 53 | } 54 | #endif 55 | 56 | %catches(E1,E2*,ET,ET,...) A::barfoo(int i); 57 | 58 | 59 | %allowexception efoovar; 60 | %allowexception A::efoovar; 61 | 62 | %inline %{ 63 | int efoovar; 64 | int foovar; 65 | const int cfoovar = 1; 66 | 67 | struct E1 68 | { 69 | }; 70 | 71 | struct E2 72 | { 73 | }; 74 | 75 | struct E3 76 | { 77 | }; 78 | 79 | template 80 | struct ET 81 | { 82 | }; 83 | 84 | struct A 85 | { 86 | static int sfoovar; 87 | static const int CSFOOVAR = 1; 88 | int foovar; 89 | int efoovar; 90 | 91 | /* caught by the user's throw definition */ 92 | int foo() throw(E1) 93 | { 94 | throw E1(); 95 | return 0; 96 | } 97 | 98 | int bar() throw(E2) 99 | { 100 | throw E2(); 101 | return 0; 102 | } 103 | 104 | /* caught by %postexception */ 105 | int foobar() 106 | { 107 | throw E3(); 108 | return 0; 109 | } 110 | 111 | 112 | int barfoo(int i) 113 | { 114 | if (i == 1) { 115 | throw E1(); 116 | } else if (i == 2) { 117 | static E2 *ep = new E2(); 118 | throw ep; 119 | } else if (i == 3) { 120 | throw ET(); 121 | } else { 122 | throw ET(); 123 | } 124 | return 0; 125 | } 126 | }; 127 | int A::sfoovar = 1; 128 | 129 | #ifdef SWIGPYTHON_BUILTIN 130 | bool is_python_builtin() { return true; } 131 | #else 132 | bool is_python_builtin() { return false; } 133 | #endif 134 | 135 | %} 136 | 137 | %template(ET_i) ET; 138 | %template(ET_d) ET; 139 | -------------------------------------------------------------------------------- /src/octave/wrappers/wcon-oct-swig.template: -------------------------------------------------------------------------------- 1 | %module SWIG_MOD_NAME 2 | %{ 3 | #include "octaveWconPythonWrapper.h" 4 | #include "swigWrapper.h" 5 | %} 6 | /* No error checks in current (very early) prototype */ 7 | /* First prototype uses C-only interface */ 8 | void initWrapper(void); 9 | int isNullHandle(int handle); 10 | int isNoneHandle(int handle); 11 | 12 | /* WCONWorms - note the lack of a prefix for the prototype */ 13 | int load_from_file(const char *path); 14 | void save_to_file(int selfHandle, const char *path); 15 | int to_canon(int selfHandle); 16 | int add(int selfHandle, int handle2); 17 | int eq(int selfHandle, int handle2); 18 | WconOctUnitsDict *units(int selfHandle); 19 | int metadata(int selfHandle); 20 | long num_worms(int selfHandle); 21 | int worm_ids(int selfHandle); 22 | int data_as_odict(int selfHandle); 23 | 24 | /* MeasurementUnit - prefixes cannot be avoided */ 25 | int MU_create(const char *unitStr); 26 | double MU_to_canon(int selfHandle, double value); 27 | double MU_from_canon(int selfHandle, double value); 28 | const char *MU_unit_string(int selfHandle); 29 | const char *MU_canonical_unit_string(int selfHandle); 30 | 31 | /* Preliminary accessors for Units dictionaries */ 32 | int unitsDict_numElements(WconOctUnitsDict *dictionary); 33 | int unitsDict_valueFromKey(WconOctUnitsDict *dictionary, 34 | const char *key); -------------------------------------------------------------------------------- /src/octave/wrappers/wrapperInternal.cpp: -------------------------------------------------------------------------------- 1 | #include "wrapperInternal.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include // for srand and rand 9 | using namespace std; 10 | 11 | // Note that while the theoretical range on the key 12 | // is of the order of unsigned int, the fact that 13 | // we need to indicate error conditions means 14 | // that the effective range of the keys are from 15 | // 0..INT_MAX 16 | unordered_map refHandles; 17 | unsigned int totalActiveRefs = 0; 18 | // The more I think about it, the more I think 19 | // there has to be a better way than using a 20 | // random key or a sequential key, both of 21 | // which have their problems. 22 | 23 | WconOctHandle wrapInternalStoreReference(PyObject *pythonRef) { 24 | 25 | if (pythonRef == NULL) { 26 | cerr << "ERROR: NULL reference object supplied" << endl; 27 | return WCONOCT_NULL_HANDLE; 28 | } 29 | 30 | if (totalActiveRefs >= INT_MAX) { 31 | // We're already maxed out. Immediately return error. 32 | cerr << "ERROR: Out of room for new Python references" << endl; 33 | return WCONOCT_NULL_HANDLE; 34 | } 35 | 36 | // get a random number 37 | unsigned int randkey; 38 | randkey = rand()%INT_MAX; 39 | // assume rand implementation is good 40 | unsigned int count = INT_MAX; 41 | while (count >= 0) { 42 | // try key against hash table 43 | unordered_map::const_iterator result = 44 | refHandles.find(randkey); 45 | if (result == refHandles.end()) { 46 | pair refKeyPair(randkey,pythonRef); 47 | refHandles.insert(refKeyPair); 48 | totalActiveRefs++; 49 | return randkey; 50 | } 51 | count--; 52 | } 53 | // Error condition 54 | return WCONOCT_NULL_HANDLE; 55 | } 56 | 57 | PyObject *wrapInternalGetReference(WconOctHandle handle) { 58 | unsigned int key = 0; 59 | 60 | if (handle == WCONOCT_NULL_HANDLE) { 61 | cerr << "ERROR: Trying to access a NULL handle." << endl; 62 | return NULL; 63 | } else if (handle == WCONOCT_NONE_HANDLE) { 64 | cerr << "ERROR: Py_None is not a valid wrapper access object." << endl; 65 | return NULL; 66 | } else { 67 | key = (unsigned int)handle; 68 | } 69 | 70 | unordered_map::const_iterator result = 71 | refHandles.find(key); 72 | if (result != refHandles.end()) { 73 | return result->second; 74 | } else { 75 | return NULL; 76 | } 77 | } 78 | 79 | void wrapInternalCheckErrorVariable(WconOctError *err) { 80 | // passing a NULL value is strictly forbidden. Shut the entire 81 | // code down if this is detected. 82 | if (err == NULL) { 83 | cerr << "ERROR: Error return variable may not be NULL. Shutting Down!" 84 | << endl; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/octave/wrappers/wrapperInternal.h: -------------------------------------------------------------------------------- 1 | #ifndef __WRAPPER_INTERNAL_H_ 2 | #define __WRAPPER_INTERNAL_H_ 3 | // For internal (non-API) wrapper functionality 4 | #include 5 | 6 | #include "wrapperTypes.h" 7 | 8 | // Special handle return values 9 | #define WCONOCT_NULL_HANDLE -1337 10 | #define WCONOCT_NONE_HANDLE -42 11 | 12 | // Internal functions 13 | WconOctHandle wrapInternalStoreReference(PyObject *pythonRef); 14 | PyObject *wrapInternalGetReference(WconOctHandle key); 15 | 16 | // Internal Checks 17 | void wrapInternalCheckErrorVariable(WconOctError *err); 18 | #endif /* __WRAPPER_INTERNAL_H_ */ 19 | -------------------------------------------------------------------------------- /src/octave/wrappers/wrapperTypes.h: -------------------------------------------------------------------------------- 1 | #ifndef __WRAPPER_TYPES_H_ 2 | #define __WRAPPER_TYPES_H_ 3 | 4 | typedef int WconOctHandle; 5 | typedef enum WconOctErrCodes { 6 | SUCCESS, 7 | FAILED 8 | } WconOctError; 9 | typedef struct unitskeyValuePair { 10 | char *key; 11 | WconOctHandle value; 12 | } WconOctUnitsKeyValue; 13 | typedef struct unitsDictStruct { 14 | int numElements; 15 | WconOctUnitsKeyValue *unitsDict; 16 | } WconOctUnitsDict; 17 | #endif /* __WRAPPER_TYPES_H_ */ 18 | -------------------------------------------------------------------------------- /src/scala/README.md: -------------------------------------------------------------------------------- 1 | # Tracker Commons Scala Reference Implementation 2 | 3 | This project contains source code in Scala that implements the WCON data 4 | format for tracking data. When the text specification and the behavior of 5 | the Scala implementation differ, and it is not obvious which one is correct, 6 | the behavior of the Scala implementation should be presumed to be authoritative. 7 | 8 | ## Requirements 9 | 10 | This project requires Java 8 and [Scala 2.12](http://scala-lang.org). You do 11 | not need to install Scala explicitly, as the build process is managed by 12 | [SBT](http://www.scala-sbt.org/). SBT must be installed to build the project, 13 | and it will take care of downloading Scala and any needed libraries. 14 | 15 | ## Building the project 16 | 17 | Type `sbt package` in this directory. SBT will tell you where it has 18 | created the jar file. You can include this jar in your classpath. 19 | 20 | ## Using the reference implementation to read worm data 21 | 22 | Here is an example of reading WCON data with the Scala implementation. 23 | This example is meant to run in the SBT console (`sbt console` from the 24 | command prompt, when within the `src/scala` directory). The `scala` 25 | REPL works just as well if you put the TrackerCommons jar file on the 26 | classpath. 27 | 28 | ```scala 29 | import org.openworm.trackercommons._ 30 | val worms = ReadWrite.read("../../tests/intermediate.wcon") match { 31 | case Right(ws) => ws 32 | case Left(err) => println(err); throw new Exception 33 | } 34 | ``` 35 | 36 | The `ReadWrite` object will attempt to read a data file, delivering an 37 | error message if it cannot. The code above extracts the correct data if 38 | there (the `Right` case), and prints the error and throws an exception if 39 | not. (If you don't care about error-handling, replace the `match` and following 40 | statements with `.right.get`.) 41 | 42 | ## Using the reference implementation to create data 43 | 44 | The Scala implementation also contains routines to create data suitable for writing in WCON format. In particular, it helps populate the standard data structures and ensure that minimal requirements are met. 45 | 46 | To create a simple WCON data file in memory, one can do something like the following: 47 | 48 | ```scala 49 | import org.openworm.trackercommons._ 50 | val one = Create.worm(1).add( 51 | 0, Array(-0.6, -0.3, 0, 0.3, 0.6), Array(0, -0.2, 0, 0.2, 0), 52 | ox = 0.6, oy = 0 53 | ).add( 54 | 1, Array(-0.6, -0.3, 0, 0.3, 0.6), Array(-0.16, 0.04, 0.24, 0.04, -0.16), 55 | ox = 0.9, oy = -0.04 56 | ) 57 | val two = Create.worm(2).add( 58 | 1, Array(0, 0.2, 0, -0.2, 0), Array(-0.5, -0.25, 0, 0.25, 0.5), 59 | ox = 0, oy = 1.5 60 | ) 61 | val wcon = Create.wcon(). 62 | addData(one.result, two.result). 63 | setUnits() 64 | val inMemory = wcon.result 65 | val onDisk = wcon.setFile("scala-create-example.wcon").write 66 | ``` 67 | 68 | Note that the creation methods are meant to be used fluently (i.e. `that(arg).other(arg2).more(stuff)`). The reason is that it uses phantom types to make sure you've actually gone through the necessary steps to create a valid WCON file (i.e. you must add some data, and you must set the units; and if you want to write the file, you must set the file). 69 | 70 | Worm creation starts with specification of the worm ID, followed by calls to one or more of the large number of `add` methods that allow you to specify different subsets of optional data. 71 | 72 | Note that all coordinates are _global_ coordinates. If you specify offsets they will be used in the WCON file but not in memory. 73 | 74 | ## Running project tests 75 | 76 | Type `sbt test` in this directory. The test suite will run, which mostly 77 | involves reading basic and advanced sample files, writing them, and checking 78 | that the output is as expected. 79 | 80 | ## What does it mean for the Scala implementation to be the "Reference Implementation?" 81 | 82 | The Tracker Commons code repository will contain code for a variety of languages 83 | that may be used to read and write WCON files. The behavior of all of these 84 | should be identical. Common sense should be used when output disagrees as to 85 | which implementation is correct, but if it is not obvious the Scala version 86 | is considered to have the "correct behavior". 87 | 88 | #### What if implementations and/or the spec disagree? 89 | 90 | Submit a bug report, or make a pull request with a fix! Unless the implmentation 91 | is documented to be incomplete, any differences from specification are bugs. 92 | 93 | #### Why Scala? 94 | 95 | 1. Scala's strong typing and functional approach makes it easier to ensure 96 | that code is correct. 97 | 98 | 2. There are parsing libraries available that compactly represent the logic 99 | of what you are trying to parse. This makes the gap between text and code 100 | especially small, which is an advantage when trying to keep the two 101 | synchronized. 102 | 103 | 3. Rex wrote this code, and he knows Scala well and likes it. 104 | -------------------------------------------------------------------------------- /src/scala/build.sbt: -------------------------------------------------------------------------------- 1 | // Everything uses these settings 2 | lazy val minimalSettings = Seq( 3 | scalaVersion := "2.12.1", 4 | scalacOptions ++= Seq("-unchecked", "-feature", "-deprecation"), 5 | libraryDependencies += "com.lihaoyi" %% "fastparse" % "0.4.2" 6 | ) 7 | 8 | // Everything but the minimal implementation uses these 9 | lazy val commonSettings = minimalSettings ++ Seq( 10 | resolvers += "Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots", 11 | libraryDependencies += "com.github.ichoran" %% "kse" % "0.6-SNAPSHOT" 12 | ) 13 | 14 | // How to build the minimal version of the WCON reader 15 | lazy val minimal: Project = (project in file("src/minimal")). 16 | settings(minimalSettings: _*). 17 | settings( 18 | name := "tracker-commons-minimal", 19 | version := "0.1.0" 20 | ) 21 | 22 | // The main project 23 | lazy val root: Project = (project in file(".")). 24 | settings(commonSettings: _*). 25 | settings( 26 | name := "tracker-commons", 27 | version := "0.2.0", 28 | libraryDependencies += "com.novocode" % "junit-interface" % "0.9" % "test", 29 | unmanagedSourceDirectories in Test += baseDirectory.value / "src/examples" 30 | ). 31 | dependsOn(minimal % "test->compile") 32 | 33 | // To build and run examples 34 | // run with sbt 'examples/runMain org.openworm.trackercommons.examples.ExampleName' 35 | // where `ExampleName` is one of the example files, e.g. `CountAnimals` 36 | lazy val examples: Project = (project in file("src/examples")). 37 | settings(commonSettings: _*). 38 | dependsOn(root) 39 | -------------------------------------------------------------------------------- /src/scala/src/examples/CircleTravel.scala: -------------------------------------------------------------------------------- 1 | package org.openworm.trackercommons.examples 2 | 3 | import org.openworm.trackercommons._ 4 | 5 | object CircleTravel extends ExampleTemplate[String] { 6 | val defaultLocation = "" 7 | 8 | def run(args: Array[String]): Either[String, String] = { 9 | val circle = Create.wcon. 10 | setMeta( 11 | Create.meta("circle example").addSoftware( Create.software.name("CircleTravel.scala") ) 12 | ). 13 | addData({ 14 | val w = Create.worm("1").add(0.0, Array(1.0), Array(0.0)) 15 | for (i <- 1 until 12) 16 | w.add(i.toDouble, Array(math.cos(i*math.Pi/6)), Array(math.sin(i*math.Pi/6))) 17 | w 18 | }). 19 | setUnits(). 20 | result 21 | val output = defaultOutput 22 | ReadWrite.write(circle, output) match { 23 | case Left(err) => Left(err) 24 | case _ => Right(output.getPath) 25 | } 26 | } 27 | 28 | def succeed(s: String) { 29 | println(f"Wrote a circularly traveling point animal to $s") 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/scala/src/examples/CountAnimals.scala: -------------------------------------------------------------------------------- 1 | package org.openworm.trackercommons.examples 2 | 3 | import org.openworm.trackercommons._ 4 | 5 | object CountAnimals extends ExampleTemplate[Int] { 6 | val defaultLocation = "../../tests/examples/count_animals.wcon" 7 | 8 | def run(args: Array[String]): Either[String, Int] = existingFile(args) match { 9 | case Left(msg) => Left(msg) 10 | case Right(file) => 11 | ReadWrite.readOne(file) match { 12 | case Left(err) => Left(err.toString) 13 | case Right(ds) => Right(ds.data.map(_.id).toSet.size) 14 | } 15 | } 16 | 17 | def succeed(n: Int) { 18 | println(f"Found $n animals") 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/scala/src/examples/ExampleTemplate.scala: -------------------------------------------------------------------------------- 1 | package org.openworm.trackercommons.examples 2 | 3 | 4 | case class Selected(count: Int, where: String) {} 5 | 6 | trait ExampleTemplate[A] { 7 | def run(args: Array[String]): Either[String, A] 8 | def succeed(a: A): Unit 9 | def defaultLocation: String 10 | def defaultOutput: java.io.File = (new java.io.File("result.wcon")).getCanonicalFile 11 | 12 | // Expect `run` to call this to get a single file 13 | def existingFile(args: Array[String]): Either[String, java.io.File] = { 14 | val theFileName = args.headOption.getOrElse(defaultLocation) 15 | val theFile = new java.io.File(theFileName) 16 | if (!theFile.exists) { 17 | Left(Seq( 18 | f"Could not find ${theFile.getPath}", 19 | f"Are you running this from the correct directory?" 20 | ).mkString("\n")) 21 | } 22 | else Right(theFile) 23 | } 24 | 25 | def main(args: Array[String]) { 26 | run(args) match { 27 | case Left(msg) => println(msg); sys.exit(1) 28 | case Right(a) => succeed(a) 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/scala/src/examples/MovementSelect.scala: -------------------------------------------------------------------------------- 1 | package org.openworm.trackercommons.examples 2 | 3 | import org.openworm.trackercommons._ 4 | 5 | object MovementSelect extends ExampleTemplate[Selected] { 6 | val defaultLocation = "../../tests/examples/all_movements.wcon" 7 | 8 | def sq(x: Double) = x*x 9 | 10 | def movesFar(d: Data): Boolean = { 11 | if (d.ts.length < 2) return false 12 | 13 | val cx, cy, l = new Array[Double](d.ts.length) 14 | for (i <- l.indices) { 15 | val ni = d.spineN(i) 16 | if (ni < 1) { 17 | cx(i) = Double.NaN 18 | cy(i) = Double.NaN 19 | l(i) = 0 20 | } 21 | else { 22 | cx(i) = if (d.cxs.length == 0) (d.x(i, 0) + d.x(i, ni-1))/2 else d.cxs(i) 23 | cy(i) = if (d.cys.length == 0) (d.y(i, 0) + d.y(i, ni-1))/2 else d.cys(i) 24 | l(i) = math.sqrt(sq(d.x(i, 0) - d.x(i, ni-1)) + sq(d.y(i, 0) - d.y(i, ni-1))) 25 | } 26 | } 27 | 28 | val longest = l.max 29 | if (longest == 0) return false // Reject worms without a size 30 | 31 | var i0 = 0 32 | while (i0 < cx.length && (cx(i0).isNaN || cy(i0).isNaN)) i0 += 1 // i0 is at first valid position 33 | var i1 = cx.length -1 34 | while (i1 > i0 && (cx(i1).isNaN || cy(i1).isNaN)) i1 -= 1 // i1 at last valid position 35 | if (i1 <= i0) return false // Didn't go anywhere valid 36 | 37 | var distmax = 0.0 38 | var i = i0 + 1 39 | while (i <= i1) { 40 | while (cx(i).isNaN || cy(i).isNaN) i += 1 41 | val dist = sq(cx(i) - cx(i0)) + sq(cy(i) - cy(i0)) 42 | if (dist > distmax) distmax = dist 43 | i += 1 44 | } 45 | distmax = math.sqrt(distmax) 46 | 47 | distmax > longest // We have to have traveled at least one nose-to-tail distance 48 | } 49 | 50 | def run(args: Array[String]): Either[String, Selected] = existingFile(args) match { 51 | case Left(msg) => Left(msg) 52 | case Right(file) => 53 | ReadWrite.readOne(file) match { 54 | case Left(err) => Left(err.toString) 55 | case Right(ds) => 56 | val selected = ds.groupByIDs().flatMap(d => if (movesFar(d)) Some(d) else None) 57 | val output = defaultOutput 58 | ReadWrite.write(selected, output) match { 59 | case Left(err) => Left(err) 60 | case _ => Right( 61 | Selected(selected.data.length, output.getPath) 62 | ) 63 | } 64 | } 65 | } 66 | 67 | def succeed(s: Selected) { 68 | println(f"Found ${s.count} records; wrote to ${s.where}") 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/scala/src/main/scala/WconImplicits.scala: -------------------------------------------------------------------------------- 1 | package org.openworm.trackercommons 2 | 3 | object WconImplicits { 4 | // Could just use kse.maths._ for this one! 5 | implicit class PostfixMathematics(private val x: Double) extends AnyVal { 6 | def finite = !(java.lang.Double.isNaN(x) || java.lang.Double.isInfinite(x)) 7 | } 8 | 9 | // Could just use kse.flow_ for this one! 10 | implicit class TapAnything[A](private val a: A) extends AnyVal { 11 | def tap[Z](f: A => Z): A = { f(a); a } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/scala/src/minimal/MinimalWorm.scala: -------------------------------------------------------------------------------- 1 | package org.openworm.trackercommons.minimal 2 | 3 | case class Worm(id: String, ts: Array[Float], xss: Array[Array[Float]], yss: Array[Array[Float]], index: Int) { 4 | def ++(w: Worm) = new Worm(id, ts ++ w.ts, xss ++ w.xss, yss ++ w.yss, math.min(index, w.index)) 5 | } 6 | object Worm { 7 | def merge(ws: Array[Worm]) = if (ws.length == 1) ws.head else { 8 | val ws2 = ws.sortBy(_.ts(0)) 9 | new Worm(ws.head.id, ws2.flatMap(_.ts), ws2.flatMap(_.xss), ws2.flatMap(_.yss), ws.map(_.index).min) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/scala/src/minimal/MinimalWriter.scala: -------------------------------------------------------------------------------- 1 | package org.openworm.trackercommons.minimal 2 | 3 | object Writer { 4 | def apply(x: Float): String = 5 | if (x.isNaN || x.isInfinite) "null" 6 | else "%.4f".format(x).reverse.dropWhile(c => c == '0' || c == '.').reverse 7 | 8 | def apply(xs: Array[Float]): String = 9 | if (xs.length == 1) apply(xs.head) 10 | else "[" + xs.map(x => apply(x)).mkString(",") + "]" 11 | 12 | def apply(xss: Array[Array[Float]], indent: Int = -1): String = 13 | if (indent < 0) "[ " + xss.map(xs => apply(xs)).mkString(", ") + " ]" 14 | else { 15 | val sp = " "*indent 16 | xss.map(xs => sp + " " + apply(xs)).mkString("[\n", ",\n", "\n"+sp+"]") 17 | } 18 | 19 | def apply(worm: Worm, indent: Int): String = { 20 | val sp = " "*indent 21 | if (worm.ts.length <= 1) sp + s"""{ "id":${worm.id}, "t":${apply(worm.ts)}, "x":${apply(worm.xss)}, "y":${apply(worm.yss)} }""" 22 | else 23 | s"""$sp{ 24 | $sp "id": ${worm.id}, 25 | $sp "t": ${apply(worm.ts)}, 26 | $sp "x": ${apply(worm.xss, indent+2)}, 27 | $sp "y": ${apply(worm.yss, indent+2)} 28 | $sp}""" 29 | } 30 | 31 | def apply(worms: Array[Worm]): String = 32 | s"""{ 33 | "units": {"t": "s", "x": "mm", "y": "mm"}, 34 | "data": [ 35 | ${worms.map(w => apply(w, 4)).mkString(",\n")} 36 | ] 37 | }""" 38 | } 39 | -------------------------------------------------------------------------------- /tests/data/centroid.wcon: -------------------------------------------------------------------------------- 1 | { "units":{ "t":"s", "x":"mm", "y":"mm", "cx":"mm", "cy":"mm" }, 2 | "data":[{"id":"123", "t":[0], "x":[[2.0]], "y":[[1.7]], "cx":[0.3], "cy":[1.0]}], 3 | "comment": "The centroid is at 0.3, 1.0" 4 | } 5 | -------------------------------------------------------------------------------- /tests/data/centroids.wcon: -------------------------------------------------------------------------------- 1 | { "units":{ "t":"s", "x":"mm", "y":"mm", "cx":"mm", "cy":"mm" }, 2 | "data":[{ 3 | "id":"123", "t":[0,1], "x":[[2.0],[2.0]], "y":[[1.7],[1.7]], 4 | "cx":[0.3,0.4], "cy":[1.0,0.9] }], 5 | "comment": "The centroid is at 0.3, 1.0, then drifts by 0.1, -0.1" 6 | } 7 | -------------------------------------------------------------------------------- /tests/data/length-one-arrayed-single-array-xy.wcon: -------------------------------------------------------------------------------- 1 | { "units":{ "t":"s", "x":"mm", "y":"mm" }, 2 | "data":[{"id":"123", "t":[0], "x":[2.0], "y":[1.7]}], 3 | "comment": "Data is arrayed despite being length one" 4 | } 5 | -------------------------------------------------------------------------------- /tests/data/length-one-arrayed.wcon: -------------------------------------------------------------------------------- 1 | { "units":{ "t":"s", "x":"mm", "y":"mm" }, 2 | "data":[{"id":"123", "t":[0], "x":[[2.0]], "y":[[1.7]]}], 3 | "comment": "Data is arrayed despite being length one" 4 | } 5 | -------------------------------------------------------------------------------- /tests/data/offset.wcon: -------------------------------------------------------------------------------- 1 | { "units":{ "t":"s", "x":"mm", "y":"mm", "ox":"mm", "oy":"mm" }, 2 | "data":[{"id":"123", "t":[0], "x":[[1.0]], "y":[[1.2]], "ox":[1], "oy":[0.5] }], 3 | "comment": "The actual position of x and y are 2.0 and 1.7" 4 | } 5 | -------------------------------------------------------------------------------- /tests/data/offsets.wcon: -------------------------------------------------------------------------------- 1 | { "units":{ "t":"s", "x":"mm", "y":"mm", "ox":"mm", "oy":"mm" }, 2 | "data":[{ 3 | "id":"123", "t":[0, 1], "x":[[1.0], [0.5]], "y":[[1.2], [0.2]], 4 | "ox":[1, 1.5], "oy":[0.5, 1.5] }], 5 | "comment": "The actual position of x and y are 2.0 and 1.7 at both times" 6 | } 7 | -------------------------------------------------------------------------------- /tests/data/perimeter.wcon: -------------------------------------------------------------------------------- 1 | { "units":{ "t":"s", "x":"mm", "y":"mm", "px":"mm", "py":"mm" }, 2 | "data":[{ 3 | "id":"123", "t":[0], 4 | "x":[[1.6, 1.8, 2.0, 2.2, 2.4]], "y":[[1.1, 1.4, 1.7, 2.0, 2.3]], 5 | "px":[[1.6, 1.9, 2.1, 2.3, 2.4, 2.1, 1.9, 1.7]], 6 | "py":[[1.1, 1.3, 1.6, 1.9, 2.3, 2.1, 1.8, 1.5]], 7 | "ptail":4 8 | }], 9 | "comments": [ 10 | "The perimeter is a constant distance of ~ 0.14 away from the spine,", 11 | "except at the endpoints, where the perimeter touches the spine." 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /tests/data/pixelwalk.wcon: -------------------------------------------------------------------------------- 1 | { "units":{ "t":"s", "x":"mm", "y":"mm", "px":"mm", "py":"mm" }, 2 | "data":[{ 3 | "id":"123", "t":[0], 4 | "x":[[1.6, 1.8, 2.0, 2.2, 2.4]], "y":[[1.1, 1.4, 1.7, 2.0, 2.3]], 5 | "walk":[{"px": [1.6, 1.1, 0.1], "n": [38, 19], "4": "iIgoomh3d9_9DQ"}] 6 | }], 7 | "comments": [ 8 | "The perimeter touches the same points as the point-based version.", 9 | "The step size is 0.1 mm." 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /tests/data/singleton-xy.wcon: -------------------------------------------------------------------------------- 1 | { "units":{ "t":"s", "x":"mm", "y":"mm" }, 2 | "data":[{"id":"123", "t":[0], "x":[2.0], "y":[1.7]}], 3 | "comment": "'x' and 'y' are singletons with values 2.0 and 1.7" 4 | } 5 | -------------------------------------------------------------------------------- /tests/data/spine-head-left.wcon: -------------------------------------------------------------------------------- 1 | { "units":{ "t":"s", "x":"mm", "y":"mm" }, 2 | "data":[{ 3 | "id":"123", "t":[0], "head":"left", 4 | "x":[[1.6, 1.8, 2.0, 2.2, 2.4]], "y":[[1.1, 1.4, 1.7, 2.0, 2.3]] }], 5 | "comment": "The head is at the left of the spine" 6 | } 7 | -------------------------------------------------------------------------------- /tests/data/spine-head-right.wcon: -------------------------------------------------------------------------------- 1 | { "units":{ "t":"s", "x":"mm", "y":"mm" }, 2 | "data":[{ 3 | "id":"123", "t":[0], "head":"right", 4 | "x":[[1.6, 1.8, 2.0, 2.2, 2.4]], "y":[[1.1, 1.4, 1.7, 2.0, 2.3]] }], 5 | "comment": "The head is at the right (last point) of the spine" 6 | } 7 | -------------------------------------------------------------------------------- /tests/data/spine-head-unknown.wcon: -------------------------------------------------------------------------------- 1 | { "units":{ "t":"s", "x":"mm", "y":"mm" }, 2 | "data":[{ 3 | "id":"123", "t":[0], "head":"?", 4 | "x":[[1.6, 1.8, 2.0, 2.2, 2.4]], "y":[[1.1, 1.4, 1.7, 2.0, 2.3]] }], 5 | "comment": "The head position is not known" 6 | } 7 | -------------------------------------------------------------------------------- /tests/data/spine-ventral-ccw.wcon: -------------------------------------------------------------------------------- 1 | { "units":{ "t":"s", "x":"mm", "y":"mm" }, 2 | "data":[{ 3 | "id":"123", "t":[0], "ventral":"CCW", 4 | "x":[[1.6, 1.8, 2.0, 2.2, 2.4]], "y":[[1.1, 1.4, 1.7, 2.0, 2.3]] }], 5 | "comment": "Ventral side is the one counterclockwise from the first point" 6 | } 7 | -------------------------------------------------------------------------------- /tests/data/spine-ventral-cw.wcon: -------------------------------------------------------------------------------- 1 | { "units":{ "t":"s", "x":"mm", "y":"mm" }, 2 | "data":[{ 3 | "id":"123", "t":[0], "ventral":"CW", 4 | "x":[[1.6, 1.8, 2.0, 2.2, 2.4]], "y":[[1.1, 1.4, 1.7, 2.0, 2.3]] }], 5 | "comment": "Ventral side is the one clockwise from the first point" 6 | } 7 | -------------------------------------------------------------------------------- /tests/data/spine-ventral-unknown.wcon: -------------------------------------------------------------------------------- 1 | { "units":{ "t":"s", "x":"mm", "y":"mm" }, 2 | "data":[{ 3 | "id":"123", "t":[0], "ventral":"?", 4 | "x":[[1.6, 1.8, 2.0, 2.2, 2.4]], "y":[[1.1, 1.4, 1.7, 2.0, 2.3]] }], 5 | "comment": "Ventral side is not known" 6 | } 7 | -------------------------------------------------------------------------------- /tests/data/spine.wcon: -------------------------------------------------------------------------------- 1 | { "units":{ "t":"s", "x":"mm", "y":"mm" }, 2 | "data":[{ 3 | "id":"123", "t":[0], 4 | "x":[[1.6, 1.8, 2.0, 2.2, 2.4]], "y":[[1.1, 1.4, 1.7, 2.0, 2.3]] }], 5 | "comments": [ 6 | "The spine is 5 points long and increases by 0.2 or 0.3 (x or y resp.)", 7 | "The central spine point is at 2.0, 1.7" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /tests/data/string-id.wcon: -------------------------------------------------------------------------------- 1 | { "units":{ "t":"s", "x":"mm", "y":"mm" }, 2 | "data":[{"id":"wiggy", "t":[0], "x":[2.0], "y":[1.7]}], 3 | "comment": "The animal has an ID of 'wiggy'" 4 | } 5 | -------------------------------------------------------------------------------- /tests/data/two-ids.wcon: -------------------------------------------------------------------------------- 1 | { "units":{ "t":"s", "x":"mm", "y":"mm" }, 2 | "data":[ 3 | {"id":"123", "t":[0], "x":[2.0], "y":[1.7]}, 4 | {"id":"124", "t":[0], "x":[1.9], "y":[9.9]} 5 | ], 6 | "comment": "The animals have IDs of 123 and 124" 7 | } 8 | -------------------------------------------------------------------------------- /tests/data/two-times-arrayed-bare-xy.wcon: -------------------------------------------------------------------------------- 1 | { "units":{ "t":"s", "x":"mm", "y":"mm" }, 2 | "data":[ {"id":"123", "t":[0, 1], "x":[2.0, 2.1], "y":[1.7, 1.6]} ], 3 | "comment": "Two timepoints 1s apart; 'x' changes by +0.1 and 'y' by -0.1" 4 | } 5 | -------------------------------------------------------------------------------- /tests/data/two-times-arrayed.wcon: -------------------------------------------------------------------------------- 1 | { "units":{ "t":"s", "x":"mm", "y":"mm" }, 2 | "data":[ {"id":"123", "t":[0, 1], "x":[[2.0], [2.1]], "y":[[1.7], [1.6]]} ], 3 | "comment": "Two timepoints 1s apart; 'x' changes by +0.1 and 'y' by -0.1" 4 | } 5 | -------------------------------------------------------------------------------- /tests/data/two-times-separate.wcon: -------------------------------------------------------------------------------- 1 | { "units":{ "t":"s", "x":"mm", "y":"mm" }, 2 | "data":[ 3 | {"id":"123", "t":[0], "x":[2.0], "y":[1.7]}, 4 | {"id":"123", "t":[1], "x":[2.1], "y":[1.6]} 5 | ], 6 | "comment": "Two timepoints 1s apart; 'x' changes by +0.1 and 'y' by -0.1" 7 | } 8 | -------------------------------------------------------------------------------- /tests/examples/all_movements.wcon: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "protocol": "Six animals that travel different distances." 4 | }, 5 | "units": { "t":"s", "x":"mm", "y":"mm" }, 6 | "data": [ 7 | { "id":"1", "t":[0, 1, 2, 3], 8 | "x":[[1,2], [3,4], [5,6], [7,8]], 9 | "y":[[3,4], [4,3], [3,2], [2,3]], 10 | "comment":"Length = sqrt(2), travels more than 6 (include)" 11 | }, 12 | { "id":"2", "t":[0, 1, 2, 3], 13 | "x":[[0,5], [1,6], [0,5], [-1,4]], 14 | "y":[[-3,3], [-3,2], [-3,2], [-2,3]], 15 | "comment":"Length over 6, travels less than 3 (reject)" 16 | }, 17 | { "id":"3", "t":[0], "x":[[1,3]], "y":[[3,5]] }, 18 | { "id":"3", "t":[1], "x":[[2,4]], "y":[[3,5]] }, 19 | { "id":"3", "t":[2], "x":[[3,5]], "y":[[3,5]] }, 20 | { "id":"3", "t":[3], "x":[[4,6]], "y":[[3,5]] }, 21 | { "id":"4", "t":[2], "x":[[-5,-5]], "y":[[3,4]] }, 22 | { "id":"5", "t":[0, 1, 2, 3], 23 | "x":[[2,2,2], [2,2,2], [2,2,2], [2,2,2]], 24 | "y":[[4,4,4], [4,4,4], [4,4,4], [4,4,4]] }, 25 | { "id":"6", "t":[0, 1, 2, 3], 26 | "x":[[4], [4], [4], [4]], 27 | "y":[[2], [2], [2], [2]] } 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /tests/examples/all_times.wcon: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "protocol": "Six animals present for different lengths of time." 4 | }, 5 | "units": { "t":"s", "x":"mm", "y":"mm" }, 6 | "data": [ 7 | { "id":"1", "t":[0, 8, 16, 24, 32, 40], 8 | "x":[[1,2], [3,4], [5,6], [7,8], [9,8], [7,6]], 9 | "y":[[3,4], [4,3], [3,2], [2,3], [1,2], [2,1]] 10 | }, 11 | { "id":"2", "t":[0, 3, 6, 9, 12, 15], 12 | "x":[[8,9], [7,9], [6,9], [6,8], [6,7], [6,6]], 13 | "y":[[7,6], [6,5], [5,4], [4,3], [3,2], [2,1]] 14 | }, 15 | { "id":"3", "t":[0, 1], "x":[[1,3],[2,3]], "y":[[3,5],[4,5]] }, 16 | { "id":"3", "t":[5, 11], "x":[[1,3],[2,3]], "y":[[3,5],[4,5]] }, 17 | { "id":"3", "t":[15, 16], "x":[[1,3],[2,3]], "y":[[3,5],[4,5]] }, 18 | { "id":"3", "t":[19, 26], "x":[[1,3],[2,3]], "y":[[3,5],[4,5]] }, 19 | { "id":"3", "t":[33, 34], "x":[[1,3],[2,3]], "y":[[3,5],[4,5]] }, 20 | { "id":"4", "t":[2], "x":[[-5,-5]], "y":[[3,4]] }, 21 | { "id":"5", "t":[22], "x":[[-5,-5]], "y":[[3,4]] }, 22 | { "id":"6", "t":[14, 18, 22, 26], 23 | "x":[[-1,-2], [-3,-4], [-5,-6], [-7,-8]], 24 | "y":[[-3,-4], [-4,-3], [-3,-2], [-2,-3]] 25 | } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /tests/examples/count_animals.wcon: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "protocol": "Three animals created by hand in four data points." 4 | }, 5 | "units": { "t":"s", "x":"mm", "y":"mm" }, 6 | "data": [ 7 | {"id":"1", "t":[0], "x":[[1,2]], "y":[[3,4]] }, 8 | {"id":"2", "t":[0], "x":[[8,9]], "y":[[7,6]] }, 9 | {"id":"1", "t":[1, 2], "x":[[1,3],[2,3]], "y":[[3,5],[4,5]] }, 10 | {"id":"3", "t":[2], "x":[[-5,-5]], "y":[[3,4]] } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /tests/intermediate.wcon: -------------------------------------------------------------------------------- 1 | { 2 | "units": {"t":"s", "x":"mm", "y":"mm"}, 3 | "metadata": {"strain":"N2", "stage":"adult"}, 4 | "data":[ 5 | {"id":"1", "t":[0], "x":[[0,0.3,0.6,0.9,1.2]], "y":[[0,-0.2,0,0.2,0]] }, 6 | {"id":"1", "t":[1], "x":[[0.3,0.6,0.9,1.2,1.5]], "y":[[-0.2,0,0.2,0,-0.2]] }, 7 | {"id":"2", "t":[1], "x":[[0,0.2,0,-0.2,0]], "y":[[1,1.25,1.5,1.75,2]] } 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /tests/intermediatezipped.wcon.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openworm/tracker-commons/e5a12d3213839e9307f9df5e0973201fdcfeca6e/tests/intermediatezipped.wcon.zip -------------------------------------------------------------------------------- /tests/maximal_0.wcon: -------------------------------------------------------------------------------- 1 | {"files":{"current":"maximal_0.wcon", "prev":[], "next":["maximal_1.wcon","maximal_2.wcon"]},"units":{"t":"s","x":"mm","y":"mm"},"data":[{"id":"3", "t":[1.3], "x":[[3,4]], "y":[[5.4,3]]}]} 2 | -------------------------------------------------------------------------------- /tests/maximal_1.wcon: -------------------------------------------------------------------------------- 1 | {"units":{"t":"s","x":"mm","y":"mm"},"files":{"current":"maximal_1.wcon", "prev":["maximal_0.wcon"], "next":["maximal_2.wcon"]},"data":[{"id":"3", "t":[1.3], "x":[[3,4]], "y":[[5.4,3]]}]} 2 | -------------------------------------------------------------------------------- /tests/maximal_2.wcon: -------------------------------------------------------------------------------- 1 | {"units":{"t":"s","x":"mm","y":"mm"},"files":{"current":"maximal_2.wcon", "prev":["maximal_1.wcon", "maximal_0.wcon"], "next":[]},"data":[{"id":"3", "t":[1.3], "x":[[3,4]], "y":[[5.4,3]]}]} 2 | -------------------------------------------------------------------------------- /tests/maximalzipped.wcon.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openworm/tracker-commons/e5a12d3213839e9307f9df5e0973201fdcfeca6e/tests/maximalzipped.wcon.zip -------------------------------------------------------------------------------- /tests/metadata/all-metadata.wcon: -------------------------------------------------------------------------------- 1 | { "units":{ "t":"s", "x":"mm", "y":"mm", "temperature":"C", "humidity":"1", 2 | "size":"mm", "age":"h" }, 3 | "data":[{"id":"0", "t":[0], "x":[0], "y":[0]}], 4 | "metadata":{ 5 | "id":"myth", 6 | "lab":{"location":"Atlantis", "name":"Aquatic Systems", "PI":"Poseidon", "contact":"Please don't."}, 7 | "who":["Atlas", "Gaderius"], 8 | "timestamp":"2000-01-02T03:04:05", 9 | "temperature":22, 10 | "humidity":54000, 11 | "arena":{"style":"island", "orientation":"toward", "size":7400000 }, 12 | "food":"fish", 13 | "media":"liquid", 14 | "sex":"decline to state", 15 | "stage":"adult", 16 | "age":31557600, 17 | "strain":"mythological", 18 | "protocol":["Sink beneath waves", "Cultivate mystery"], 19 | "interpolate": {"method": "cubic", "values": ["x", "y"]}, 20 | "software":{ 21 | "name":"Timaeus", "version":"1", "featureID":"@plato", 22 | "settings":[1, null, {"this is arbitrary":true}] 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tests/metadata/alt-arena-two-dimensions.wcon: -------------------------------------------------------------------------------- 1 | { "units":{ "t":"s", "x":"mm", "y":"mm", "temperature":"C", "humidity":"1", 2 | "size":"mm", "age":"h" }, 3 | "data":[{"id":"0", "t":[0], "x":[0], "y":[0]}], 4 | "metadata":{ 5 | "arena":{ 6 | "style":"island", "orientation":"toward", 7 | "size":[6000000, 9000000] 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /tests/metadata/alt-one-protocol.wcon: -------------------------------------------------------------------------------- 1 | { "units":{ "t":"s", "x":"mm", "y":"mm", "temperature":"C", "humidity":"1", 2 | "size":"mm", "age":"h" }, 3 | "data":[{"id":"0", "t":[0], "x":[0], "y":[0]}], 4 | "metadata":{ "protocol":"Sink beneath waves" } 5 | } 6 | -------------------------------------------------------------------------------- /tests/metadata/alt-one-who.wcon: -------------------------------------------------------------------------------- 1 | { "units":{ "t":"s", "x":"mm", "y":"mm", "temperature":"C", "humidity":"1", 2 | "size":"mm", "age":"h" }, 3 | "data":[{"id":"0", "t":[0], "x":[0], "y":[0]}], 4 | "metadata":{ "who":"Atlas" } 5 | } 6 | -------------------------------------------------------------------------------- /tests/metadata/alt-two-labs.wcon: -------------------------------------------------------------------------------- 1 | { "units":{ "t":"s", "x":"mm", "y":"mm", "temperature":"C", "humidity":"1", 2 | "size":"mm", "age":"h" }, 3 | "data":[{"id":"0", "t":[0], "x":[0], "y":[0]}], 4 | "metadata":{ 5 | "lab":[ 6 | {"location":"Atlantis", "name":"Aquatic Systems", "PI":"Poseidon"}, 7 | {"location":"Atlantis", "name":"Skyholdings", "PI":"Atlas"} 8 | ] 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /tests/metadata/alt-two-softwares.wcon: -------------------------------------------------------------------------------- 1 | { "units":{ "t":"s", "x":"mm", "y":"mm", "temperature":"C", "humidity":"1", 2 | "size":"mm", "age":"h" }, 3 | "data":[{"id":"0", "t":[0], "x":[0], "y":[0]}], 4 | "metadata":{ 5 | "software":[ 6 | {"name":"Timaeus", "version":"1", "featureID":"@plato"}, 7 | {"name":"Critias", "version":"1.1", "featureID":"@plato"} 8 | ] 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /tests/metadata/just-age.wcon: -------------------------------------------------------------------------------- 1 | { "units":{ "t":"s", "x":"mm", "y":"mm", "temperature":"C", "humidity":"1", 2 | "size":"mm", "age":"h" }, 3 | "data":[{"id":"0", "t":[0], "x":[0], "y":[0]}], 4 | "metadata":{ "age":31557600 } 5 | } 6 | -------------------------------------------------------------------------------- /tests/metadata/just-arena.wcon: -------------------------------------------------------------------------------- 1 | { "units":{ "t":"s", "x":"mm", "y":"mm", "temperature":"C", "humidity":"1", 2 | "size":"mm", "age":"h" }, 3 | "data":[{"id":"0", "t":[0], "x":[0], "y":[0]}], 4 | "metadata":{ 5 | "arena":{"style":"island", "orientation":"toward", "size":7400000 } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tests/metadata/just-food.wcon: -------------------------------------------------------------------------------- 1 | { "units":{ "t":"s", "x":"mm", "y":"mm", "temperature":"C", "humidity":"1", 2 | "size":"mm", "age":"h" }, 3 | "data":[{"id":"0", "t":[0], "x":[0], "y":[0]}], 4 | "metadata":{ "food":"fish" } 5 | } 6 | -------------------------------------------------------------------------------- /tests/metadata/just-humidity.wcon: -------------------------------------------------------------------------------- 1 | { "units":{ "t":"s", "x":"mm", "y":"mm", "temperature":"C", "humidity":"1", 2 | "size":"mm", "age":"h" }, 3 | "data":[{"id":"0", "t":[0], "x":[0], "y":[0]}], 4 | "metadata":{ "humidity":54000 } 5 | } 6 | -------------------------------------------------------------------------------- /tests/metadata/just-id.wcon: -------------------------------------------------------------------------------- 1 | { "units":{ "t":"s", "x":"mm", "y":"mm", "temperature":"C", "humidity":"1", 2 | "size":"mm", "age":"h" }, 3 | "data":[{"id":"0", "t":[0], "x":[0], "y":[0]}], 4 | "metadata":{ 5 | "id":"myth" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tests/metadata/just-interpolate.wcon: -------------------------------------------------------------------------------- 1 | { "units":{ "t":"s", "x":"mm", "y":"mm", "temperature":"C", "humidity":"1", 2 | "size":"mm", "age":"h" }, 3 | "data":[{"id":"0", "t":[0], "x":[0], "y":[0]}], 4 | "metadata":{ "interpolate": {"method": "cubic", "values": ["x", "y"]} } 5 | } 6 | -------------------------------------------------------------------------------- /tests/metadata/just-lab.wcon: -------------------------------------------------------------------------------- 1 | { "units":{ "t":"s", "x":"mm", "y":"mm", "temperature":"C", "humidity":"1", 2 | "size":"mm", "age":"h" }, 3 | "data":[{"id":"0", "t":[0], "x":[0], "y":[0]}], 4 | "metadata":{ 5 | "lab":{"location":"Atlantis", "name":"Aquatic Systems", "PI":"Poseidon", 6 | "contact":"Please don't."} 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tests/metadata/just-media.wcon: -------------------------------------------------------------------------------- 1 | { "units":{ "t":"s", "x":"mm", "y":"mm", "temperature":"C", "humidity":"1", 2 | "size":"mm", "age":"h" }, 3 | "data":[{"id":"0", "t":[0], "x":[0], "y":[0]}], 4 | "metadata":{ "media":"liquid" } 5 | } 6 | -------------------------------------------------------------------------------- /tests/metadata/just-protocol.wcon: -------------------------------------------------------------------------------- 1 | { "units":{ "t":"s", "x":"mm", "y":"mm", "temperature":"C", "humidity":"1", 2 | "size":"mm", "age":"h" }, 3 | "data":[{"id":"0", "t":[0], "x":[0], "y":[0]}], 4 | "metadata":{ "protocol":["Sink beneath waves", "Cultivate mystery"] } 5 | } 6 | -------------------------------------------------------------------------------- /tests/metadata/just-sex.wcon: -------------------------------------------------------------------------------- 1 | { "units":{ "t":"s", "x":"mm", "y":"mm", "temperature":"C", "humidity":"1", 2 | "size":"mm", "age":"h" }, 3 | "data":[{"id":"0", "t":[0], "x":[0], "y":[0]}], 4 | "metadata":{ "sex":"decline to state" } 5 | } 6 | -------------------------------------------------------------------------------- /tests/metadata/just-software.wcon: -------------------------------------------------------------------------------- 1 | { "units":{ "t":"s", "x":"mm", "y":"mm", "temperature":"C", "humidity":"1", 2 | "size":"mm", "age":"h" }, 3 | "data":[{"id":"0", "t":[0], "x":[0], "y":[0]}], 4 | "metadata":{ 5 | "software":{ 6 | "name":"Timaeus", "version":"1", "featureID":"@plato", 7 | "settings":[1, null, {"this is arbitrary":true}] 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /tests/metadata/just-stage.wcon: -------------------------------------------------------------------------------- 1 | { "units":{ "t":"s", "x":"mm", "y":"mm", "temperature":"C", "humidity":"1", 2 | "size":"mm", "age":"h" }, 3 | "data":[{"id":"0", "t":[0], "x":[0], "y":[0]}], 4 | "metadata":{ "stage":"adult" } 5 | } 6 | -------------------------------------------------------------------------------- /tests/metadata/just-strain.wcon: -------------------------------------------------------------------------------- 1 | { "units":{ "t":"s", "x":"mm", "y":"mm", "temperature":"C", "humidity":"1", 2 | "size":"mm", "age":"h" }, 3 | "data":[{"id":"0", "t":[0], "x":[0], "y":[0]}], 4 | "metadata":{ "strain":"mythological" } 5 | } 6 | -------------------------------------------------------------------------------- /tests/metadata/just-temperature.wcon: -------------------------------------------------------------------------------- 1 | { "units":{ "t":"s", "x":"mm", "y":"mm", "temperature":"C", "humidity":"1", 2 | "size":"mm", "age":"h" }, 3 | "data":[{"id":"0", "t":[0], "x":[0], "y":[0]}], 4 | "metadata":{ "temperature":22 } 5 | } 6 | -------------------------------------------------------------------------------- /tests/metadata/just-timestamp.wcon: -------------------------------------------------------------------------------- 1 | { "units":{ "t":"s", "x":"mm", "y":"mm", "temperature":"C", "humidity":"1", 2 | "size":"mm", "age":"h" }, 3 | "data":[{"id":"0", "t":[0], "x":[0], "y":[0]}], 4 | "metadata":{ "timestamp":"2000-01-02T03:04:05" } 5 | } 6 | -------------------------------------------------------------------------------- /tests/metadata/just-who.wcon: -------------------------------------------------------------------------------- 1 | { "units":{ "t":"s", "x":"mm", "y":"mm", "temperature":"C", "humidity":"1", 2 | "size":"mm", "age":"h" }, 3 | "data":[{"id":"0", "t":[0], "x":[0], "y":[0]}], 4 | "metadata":{ "who":["Atlas", "Gaderius"] } 5 | } 6 | -------------------------------------------------------------------------------- /tests/metadata/no-metadata.wcon: -------------------------------------------------------------------------------- 1 | { "units":{ "t":"s", "x":"mm", "y":"mm" }, 2 | "data":[{"id":"0", "t":[0], "x":[0], "y":[0]}], 3 | "comment": [ 4 | "All metadata that is present is the same across all files.", 5 | "The exception is files starting in alt- which test variations.", 6 | "Files starting in just- contain only one field of metadata.", 7 | "all-metadata.wcon contains all fields. This obviously contains none." ] 8 | } 9 | -------------------------------------------------------------------------------- /tests/minimal.wcon: -------------------------------------------------------------------------------- 1 | {"units":{"t":"s","x":"mm","y":"mm"}, "data":[]} -------------------------------------------------------------------------------- /tests/minimalzipped.wcon.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openworm/tracker-commons/e5a12d3213839e9307f9df5e0973201fdcfeca6e/tests/minimalzipped.wcon.zip -------------------------------------------------------------------------------- /tests/minimax.wcon: -------------------------------------------------------------------------------- 1 | { 2 | "metadata":{ 3 | "lab":{"location":"CRB, room 5020", "name":"Behavioural Genomics" }, 4 | "who":"Firstname Lastname", 5 | "timestamp":"2012-04-23T18:25:43.511Z", 6 | "temperature":22, 7 | "humidity":40.2, 8 | "arena":{ "style":"petri", "size":35, "units":"mm" }, 9 | "food":"none", 10 | "media":"agarose", 11 | "sex":"hermaphrodite", 12 | "stage":"adult", 13 | "age":18.511, 14 | "strain":"CB4856", 15 | "protocol":"text description of protocol", 16 | "software":{ 17 | "tracker":{ "name":"Software Name", "version":"1.3.0"}, 18 | "featureID":"@OMG" 19 | }, 20 | "settings":"Any valid JSON entry with hardware and software configuration can go here" 21 | }, 22 | "units":{ 23 | "t":"s", 24 | "x":"mm", 25 | "y":"m", 26 | "ox":"mm", 27 | "oy":"mm", 28 | "speed":"mm/s", 29 | "curvature":"1/mm", 30 | "width":"mm", 31 | "humidity":"%", 32 | "temperature":"C", 33 | "age":"h" 34 | }, 35 | "data":[ 36 | { "id":"4", "t":[1.4], "x":[[1215.11, 1216.14, 1217.12]], "y":[[234.89, 265.23, 235.08]] }, 37 | { "id":"1", "t":[1.3,1.5], "x":[[1,1,1],[null, 1216.14, 1217.12]], "y":[[2,2,2],[234.89, 265.23, 235.08]], "ox":[5000,5001], "oy":[0, 0], "head":"R" }, 38 | { "id":"2", "t":[1.4], "x":[[125.11, 126.14, 117.12]], "y":[[23.3, 22.23, 21135.08]] }, 39 | { "id":"1", "t":[1.4], "x":[[1215.11, 1216.14, 1217.12]], "y":[[234.89, 265.23, 235.08]]}, 40 | { "id":"1", "t":[1.3], "x":[[1, 1, 1]], "y":[[2, 2, 2]], "head":"R", "@OMG":{"speed":[55,55,55]}}, 41 | { "id":"2", "t":[1.5], "x":[[1215.11, 1216.14, 1217.12]], "y":[[234.89, 265.23, 235.08]], "head":"L", "ventral":"CW" }, 42 | { "id":"1", "t":[2.5], "x":[[1215.11, 1216.14, 1217.12, 1234.3, 3423.2, 3423.1]], "y":[[234.89, 265.23, 235.08,342.1,-323.2,23]] } 43 | ], 44 | "@OMG": 5 45 | } 46 | -------------------------------------------------------------------------------- /tests/minimaxzipped.wcon.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openworm/tracker-commons/e5a12d3213839e9307f9df5e0973201fdcfeca6e/tests/minimaxzipped.wcon.zip -------------------------------------------------------------------------------- /tests/multiworm.wcon: -------------------------------------------------------------------------------- 1 | { 2 | "metadata":{ 3 | "lab":{"location":"CRB, room 5020", "name":"Behavioural Genomics" }, 4 | "who":"Firstname Lastname", 5 | "timestamp":"2012-04-23T18:25:43.511Z", 6 | "temperature":22, 7 | "humidity":40.2, 8 | "arena":{ "style":"petri", "size":35, "units":"mm" }, 9 | "food":"none", 10 | "media":"agarose", 11 | "sex":"hermaphrodite", 12 | "stage":"adult", 13 | "age":18.511, 14 | "strain":"CB4856", 15 | "protocol":"text description of protocol", 16 | "software":{ 17 | "tracker":{ "name":"Software Name", "version":"1.3.0"}, 18 | "featureID":"@OMG" 19 | }, 20 | "settings":"Any valid JSON entry with hardware and software configuration can go here" 21 | }, 22 | "units":{ 23 | "t":"s", 24 | "x":"mm", 25 | "y":"m", 26 | "ox":"mm", 27 | "speed":"mm/s", 28 | "curvature":"1/mm", 29 | "width":"mm", 30 | "humidity":"%", 31 | "temperature":"C", 32 | "age":"h" 33 | }, 34 | "data":[ 35 | { "id":"3111", "t":[1.4], "x":[[1215.11, 1216.14, 1217.12]], "y":[[234.89, 265.23, 235.08]] }, 36 | { "id":"3111", "t":[1.4], "x":[[1215.11, 1216.14, 1217.12]], "y":[[234.89, 265.23, 235.08]] }, 37 | { "id":"3", "t":[1.4], "x":[[1215.11, 1216.14, 1217.12]], "y":[[234.89, 265.23, 235.08]] }, 38 | { "id":"444", "t":[1.4], "x":[[1215.11, 1216.14, 1217.12]], "y":[[234.89, 265.23, 235.08]] }, 39 | { "id":"42", "t":[1.4], "x":[[1215.11, 1216.14, 1217.12]], "y":[[234.89, 265.23, 235.08]] }, 40 | { "id":"11", "t":[1.4], "x":[[1215.11, 1216.14, 1217.12]], "y":[[234.89, 265.23, 235.08]] }, 41 | { "id":"33", "t":[1.4], "x":[[1215.11, 1216.14, 1217.12]], "y":[[234.89, 265.23, 235.08]] }, 42 | { "id":"1", "t":[1.4], "x":[[1215.11, 1216.14, 1217.12]], "y":[[234.89, 265.23, 235.08]] }, 43 | { "id":"0", "t":[1.4], "x":[[1215.11, 1216.14, 1217.12]], "y":[[234.89, 265.23, 235.08]] }, 44 | { "id":"-11", "t":[1.4], "x":[[1215.11, 1216.14, 1217.12]], "y":[[234.89, 265.23, 235.08]] }, 45 | { "id":"3.01", "t":[1.4], "x":[[1215.11, 1216.14, 1217.12]], "y":[[234.89, 265.23, 235.08]] }, 46 | { "id":"434234.34234", "t":[1.4], "x":[[1215.11, 1216.14, 1217.12]], "y":[[234.89, 265.23, 235.08]] }, 47 | { "id":"100", "t":[1.4], "x":[[1215.11, 1216.14, 1217.12]], "y":[[234.89, 265.23, 235.08]] }, 48 | { "id":"10000", "t":[1.4], "x":[[1215.11, 1216.14, 1217.12]], "y":[[234.89, 265.23, 235.08]] }, 49 | { "id":"10001", "t":[1.4], "x":[[1215.11, 1216.14, 1217.12]], "y":[[234.89, 265.23, 235.08]] }, 50 | { "id":"10002", "t":[1.4], "x":[[1215.11, 1216.14, 1217.12]], "y":[[234.89, 265.23, 235.08]] }, 51 | { "id":"10003", "t":[1.4], "x":[[1215.11, 1216.14, 1217.12]], "y":[[234.89, 265.23, 235.08]] }, 52 | { "id":"10004", "t":[1.4], "x":[[1215.11, 1216.14, 1217.12]], "y":[[234.89, 265.23, 235.08]] }, 53 | { "id":"10004.5", "t":[1.4], "x":[[1215.11, 1216.14, 1217.12]], "y":[[234.89, 265.23, 235.08]] }, 54 | { "id":"10005", "t":[1.4], "x":[[1215.11, 1216.14, 1217.12]], "y":[[234.89, 265.23, 235.08]] }, 55 | { "id":"10006", "t":[1.4], "x":[[1215.11, 1216.14, 1217.12]], "y":[[234.89, 265.23, 235.08]] }, 56 | { "id":"100000", "t":[1.4], "x":[[1215.11, 1216.14, 1217.12]], "y":[[234.89, 265.23, 235.08]] }, 57 | { "id":"100010", "t":[1.4], "x":[[1215.11, 1216.14, 1217.12]], "y":[[234.89, 265.23, 235.08]] }, 58 | { "id":"999999", "t":[1.4], "x":[[1215.11, 1216.14, 1217.12]], "y":[[234.89, 265.23, 235.08]] } 59 | ], 60 | "@OMG": 5 61 | } 62 | -------------------------------------------------------------------------------- /tests/offset_and_centroid.wcon: -------------------------------------------------------------------------------- 1 | { 2 | "units":{"t":"s","x":"mm","y":"mm","cx":"mm","cy":"mm","ox":"mm","oy":"mm"}, 3 | "data":[ 4 | { 5 | "id":"1", "t":[0], "cx":[5], "cy":[4], "ox":[2], "oy":[4], 6 | "x":[[4.5, 5, 5.5]], "y":[[4.3, 4, 3.6]] 7 | }, 8 | { 9 | "id":"2", "t":[0, 0.1], 10 | "cx":[3, 3.1], "cy":[3, 2.9], "ox":[4, 4], "oy":[3, 3], 11 | "x":[[2.5, 3.5], [2.6, 3.5]], 12 | "y":[[3.4, 2.7], [3.2, 2.5]] 13 | } 14 | ], 15 | "comment":"All four .wcon test files starting with 'offset_' should represent the same spine points. Additionally, if there are centroids they should match.", 16 | "note":"Neither 'comment' nor 'note' are understood by WCON. They, and other unspecified tags, should be ignored by readers." 17 | } 18 | -------------------------------------------------------------------------------- /tests/offset_no_centroid_yes.wcon: -------------------------------------------------------------------------------- 1 | { 2 | "units":{ "t":"s", "x":"mm", "y":"mm", "cx":"mm", "cy":"mm" }, 3 | "data":[ 4 | { "id":"1", "t":[0], "cx":[7], "cy":[8], 5 | "x":[[6.5, 7, 7.5]], "y":[[8.3, 8, 7.6]] 6 | }, 7 | { 8 | "id":"2", "t":[0, 0.1], 9 | "cx":[7, 7.1], "cy":[6, 5.9], 10 | "x":[[6.5, 7.5], [6.6, 7.5]], 11 | "y":[[6.4, 5.7], [6.2, 5.5]] 12 | } 13 | ], 14 | "comment":"All four .wcon test files starting with 'offset_' should represent the same spine points. Additionally, if there are centroids they should match.", 15 | "note":"Neither 'comment' nor 'note' are understood by WCON. They, and other unspecified tags, should be ignored by readers." 16 | } 17 | -------------------------------------------------------------------------------- /tests/offset_none.wcon: -------------------------------------------------------------------------------- 1 | { 2 | "units":{ "t":"s", "x":"mm", "y":"mm" }, 3 | "data":[ 4 | { 5 | "id":"1", "t":[0], 6 | "x":[[6.5, 7, 7.5]], "y":[[8.3, 8, 7.6]] 7 | }, 8 | { 9 | "id":"2", "t":[0, 0.1], 10 | "x":[[6.5, 7.5], [6.6, 7.5]], 11 | "y":[[6.4, 5.7], [6.2, 5.5]] 12 | } 13 | ], 14 | "comment":"All four .wcon test files starting with 'offset_' should represent the same spine points. Additionally, if there are centroids they should match.", 15 | "note":"Neither 'comment' nor 'note' are understood by WCON. They, and other unspecified tags, should be ignored by readers." 16 | } 17 | -------------------------------------------------------------------------------- /tests/offset_only.wcon: -------------------------------------------------------------------------------- 1 | { 2 | "units":{"t":"s", "x":"mm", "y":"mm", "ox":"mm", "oy":"mm"}, 3 | "data":[ 4 | { 5 | "id":"1", "t":[0], "ox":[2], "oy":[4], 6 | "x":[[4.5, 5, 5.5]], "y":[[4.3, 4, 3.6]] 7 | }, 8 | { 9 | "id":"2", "t":[0, 0.1], 10 | "ox":[4, 4], "oy":[3, 3], 11 | "x":[[2.5, 3.5], [2.6, 3.5]], 12 | "y":[[3.4, 2.7], [3.2, 2.5]] 13 | } 14 | ], 15 | "comment":"All four .wcon test files starting with 'offset_' should represent the same spine points. Additionally, if there are centroids they should match.", 16 | "note":"Neither 'comment' nor 'note' are understood by WCON. They, and other unspecified tags, should be ignored by readers." 17 | } 18 | -------------------------------------------------------------------------------- /tests/perimeter_points.wcon: -------------------------------------------------------------------------------- 1 | { 2 | "units":{ 3 | "t":"s", "x":"mm", "y":"mm", 4 | "ox":"mm", "oy":"mm", "px":"mm", "py":"mm" 5 | }, 6 | "data":[ 7 | { 8 | "id":"1", "t":[0], "ox":[2], "oy":[4], 9 | "x":[[4.5, 5, 5.5]], "y":[[4.3, 4, 3.6]], 10 | "px":[[4.5, 4.8, 5.2, 5.5, 5.3, 4.7]], 11 | "py":[[4.3, 4.2, 3.9, 3.6, 3.7, 4.1]], 12 | "ptail":3 13 | }, 14 | { 15 | "id":"2", "t":[0, 0.1], 16 | "ox":[4, 4], "oy":[3, 3], 17 | "x":[[2.5, 3.5], [2.6, 3.5]], 18 | "y":[[3.4, 2.7], [3.2, 2.5]], 19 | "px":[[2.5, 2.6, 3.5, 3.4], [2.6, 2.7, 3.5, 3.3]], 20 | "py":[[3.4, 3.3, 2.7, 2.8], [3.2, 2.9, 2.5, 2.7]] 21 | } 22 | ], 23 | "comment":"Perimeter points px and py are an optional feature understood by some WCON readers!", 24 | "note":"Neither 'comment' nor 'note' are understood by WCON. They, and other unspecified tags, should be ignored by readers." 25 | } 26 | -------------------------------------------------------------------------------- /tests/units/angle/degrees.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t":"s", "x":"mm", "y":"mm", "theta":"degrees"}, 2 | "data": [{"id":"0", "t":[0], "x":[0], "y":[0]}], 3 | "@example": {"theta":45}, 4 | "comment": "'theta' in '@example' should be the same in all files." 5 | } 6 | -------------------------------------------------------------------------------- /tests/units/angle/degrees2.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t":"s", "x":"mm", "y":"mm", "theta":"degree"}, 2 | "data": [{"id":"0", "t":[0], "x":[0], "y":[0]}], 3 | "@example": {"theta":45}, 4 | "comment": "'theta' in '@example' should be the same in all files." 5 | } 6 | -------------------------------------------------------------------------------- /tests/units/angle/degrees3.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t":"s", "x":"mm", "y":"mm", "theta":"deg"}, 2 | "data": [{"id":"0", "t":[0], "x":[0], "y":[0]}], 3 | "@example": {"theta":45}, 4 | "comment": "'theta' in '@example' should be the same in all files." 5 | } 6 | -------------------------------------------------------------------------------- /tests/units/angle/milliradians.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t":"s", "x":"mm", "y":"mm", "theta":"milliradians"}, 2 | "data": [{"id":"0", "t":[0], "x":[0], "y":[0]}], 3 | "@example": {"theta":785.39816339744830962}, 4 | "comment": "'theta' in '@example' should be the same in all files." 5 | } 6 | -------------------------------------------------------------------------------- /tests/units/angle/milliradians2.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t":"s", "x":"mm", "y":"mm", "theta":"mrad"}, 2 | "data": [{"id":"0", "t":[0], "x":[0], "y":[0]}], 3 | "@example": {"theta":785.39816339744830962}, 4 | "comment": "'theta' in '@example' should be the same in all files." 5 | } 6 | -------------------------------------------------------------------------------- /tests/units/angle/milliradians3.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t":"s", "x":"mm", "y":"mm", "theta":"radian*1e-3"}, 2 | "data": [{"id":"0", "t":[0], "x":[0], "y":[0]}], 3 | "@example": {"theta":785.39816339744830962}, 4 | "comment": "'theta' in '@example' should be the same in all files." 5 | } 6 | -------------------------------------------------------------------------------- /tests/units/angle/radians.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t":"s", "x":"mm", "y":"mm", "theta":"radians"}, 2 | "data": [{"id":"0", "t":[0], "x":[0], "y":[0]}], 3 | "@example": {"theta":0.78539816339744830962}, 4 | "comment": "'theta' in '@example' should be the same in all files." 5 | } 6 | -------------------------------------------------------------------------------- /tests/units/angle/radians2.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t":"s", "x":"mm", "y":"mm", "theta":"radian"}, 2 | "data": [{"id":"0", "t":[0], "x":[0], "y":[0]}], 3 | "@example": {"theta":0.78539816339744830962}, 4 | "comment": "'theta' in '@example' should be the same in all files." 5 | } 6 | -------------------------------------------------------------------------------- /tests/units/angle/radians3.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t":"s", "x":"mm", "y":"mm", "theta":"rad"}, 2 | "data": [{"id":"0", "t":[0], "x":[0], "y":[0]}], 3 | "@example": {"theta":0.78539816339744830962}, 4 | "comment": "'theta' in '@example' should be the same in all files." 5 | } 6 | -------------------------------------------------------------------------------- /tests/units/custom/q-is-one.wcon: -------------------------------------------------------------------------------- 1 | { "units":{ 2 | "t":"s", "x":"cm", "y":"cm", "size":"cm", "q":"cm", 3 | "@example": { "q": 1 } }, 4 | "data":[ 5 | { "id":"0", "t":[0], "x":[0], "y":[0], "@example": { "q":[0.1] } }, 6 | { "id":"0", "t":[1,2], "x":[[0],[0]], "y":[[0],[0]], 7 | "@example": { "q":[0.1, 0.1] } } ], 8 | "metadata":{ 9 | "lab":{"location":"missing", "@example": { "q":0.1 } }, 10 | "arena":{"size":6, "@example": { "q":0.1 } }, 11 | "software":{ "featureID":"@example", "@example": { "q":0.1 } }, 12 | "settings":{ "q": 1 }, 13 | "@example":{ "q": 0.1 , "inner": { "q":1 }, "@": { "q": 0.1 } } }, 14 | "files":{"current":"q-is-one.wcon", "@example": { "q":0.1 }}, 15 | "@example": { "q":0.1 }, 16 | "comments":[ 17 | "This field is ignored because it's not part of the specification.", 18 | "This file contains custom data in every allowed location.", 19 | "Every time it appears, the key 'q' should be converted to 1.", 20 | "Note that the 'q' in 'settings' should NOT be converted.", 21 | "The 'q' in '@example' in 'units' also should not be converted!", 22 | "Also, the 'q' in 'inner' should not be converted but in '@' it should." ] 23 | } 24 | -------------------------------------------------------------------------------- /tests/units/length/foot.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t":"s", "x":"ft", "y":"ft" }, 2 | "data": [{"id":"0", "t":[0], "x":[1], "y":[-1]}], 3 | "comment": "x and y should be identical and opposite sign in all files." 4 | } 5 | -------------------------------------------------------------------------------- /tests/units/length/foot2.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t":"s", "x":"foot", "y":"feet" }, 2 | "data": [{"id":"0", "t":[0], "x":[1], "y":[-1]}], 3 | "comment": "x and y should be identical and opposite sign in all files." 4 | } 5 | -------------------------------------------------------------------------------- /tests/units/length/inch.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t":"s", "x":"in", "y":"in" }, 2 | "data": [{"id":"0", "t":[0], "x":[12], "y":[-12]}], 3 | "comment": "x and y should be identical and opposite sign in all files." 4 | } 5 | -------------------------------------------------------------------------------- /tests/units/length/inch2.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t":"s", "x":"inch", "y":"inches" }, 2 | "data": [{"id":"0", "t":[0], "x":[12], "y":[-12]}], 3 | "comment": "x and y should be identical and opposite sign in all files." 4 | } 5 | -------------------------------------------------------------------------------- /tests/units/length/meter.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t":"s", "x":"m", "y":"m" }, 2 | "data": [{"id":"0", "t":[0], "x":[0.3048], "y":[-0.3048]}], 3 | "comment": "x and y should be identical and opposite sign in all files." 4 | } 5 | -------------------------------------------------------------------------------- /tests/units/length/meter2.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t":"s", "x":"meter", "y":"metre" }, 2 | "data": [{"id":"0", "t":[0], "x":[0.3048], "y":[-0.3048]}], 3 | "comment": "x and y should be identical and opposite sign in all files." 4 | } 5 | -------------------------------------------------------------------------------- /tests/units/length/meter3.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t":"s", "x":"meters", "y":"metres" }, 2 | "data": [{"id":"0", "t":[0], "x":[0.3048], "y":[-0.3048]}], 3 | "comment": "x and y should be identical and opposite sign in all files." 4 | } 5 | -------------------------------------------------------------------------------- /tests/units/length/micron.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t":"s", "x":"micron", "y":"micron" }, 2 | "data": [{"id":"0", "t":[0], "x":[304800], "y":[-304800]}], 3 | "comment": "x and y should be identical and opposite sign in all files." 4 | } 5 | -------------------------------------------------------------------------------- /tests/units/length/micron2.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t":"s", "x":"microns", "y":"um" }, 2 | "data": [{"id":"0", "t":[0], "x":[304800], "y":[-304800]}], 3 | "comment": "x and y should be identical and opposite sign in all files." 4 | } 5 | -------------------------------------------------------------------------------- /tests/units/length/micron3.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t":"s", "x":"micrometer", "y":"micrometres" }, 2 | "data": [{"id":"0", "t":[0], "x":[304800], "y":[-304800]}], 3 | "comment": "x and y should be identical and opposite sign in all files." 4 | } 5 | -------------------------------------------------------------------------------- /tests/units/length/micron4.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t":"s", "x":"mm/1000", "y":"m*1e-6" }, 2 | "data": [{"id":"0", "t":[0], "x":[304800], "y":[-304800]}], 3 | "comment": "x and y should be identical and opposite sign in all files." 4 | } 5 | -------------------------------------------------------------------------------- /tests/units/length/millimeter.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t":"s", "x":"mm", "y":"mm" }, 2 | "data": [{"id":"0", "t":[0], "x":[304.8], "y":[-304.8]}], 3 | "comment": "x and y should be identical and opposite sign in all files." 4 | } 5 | -------------------------------------------------------------------------------- /tests/units/length/millimeter2.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t":"s", "x":"millimeter", "y":"millimetre" }, 2 | "data": [{"id":"0", "t":[0], "x":[304.8], "y":[-304.8]}], 3 | "comment": "x and y should be identical and opposite sign in all files." 4 | } 5 | -------------------------------------------------------------------------------- /tests/units/length/millimeter3.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t":"s", "x":"millimeters", "y":"millimetres" }, 2 | "data": [{"id":"0", "t":[0], "x":[304.8], "y":[-304.8]}], 3 | "comment": "x and y should be identical and opposite sign in all files." 4 | } 5 | -------------------------------------------------------------------------------- /tests/units/length/millimeter4.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t":"s", "x":"m/1000", "y":"m*1e-3" }, 2 | "data": [{"id":"0", "t":[0], "x":[304.8], "y":[-304.8]}], 3 | "comment": "x and y should be identical and opposite sign in all files." 4 | } 5 | -------------------------------------------------------------------------------- /tests/units/si/bare.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t": "s", "x":"mm", "y":"mm" }, 2 | "data": [{"id":"0", "t":[3], "x":[0], "y":[0]}], 3 | "comment": "Timepoint 0 of animal 0 should be the same in all files." 4 | } 5 | -------------------------------------------------------------------------------- /tests/units/si/centi.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t": "cs", "x":"mm", "y":"mm" }, 2 | "data": [{"id":"0", "t":[300], "x":[0], "y":[0]}], 3 | "comment": "Timepoint 0 of animal 0 should be the same in all files." 4 | } 5 | -------------------------------------------------------------------------------- /tests/units/si/centi2.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t": "centiseconds", "x":"mm", "y":"mm" }, 2 | "data": [{"id":"0", "t":[300], "x":[0], "y":[0]}], 3 | "comment": "Timepoint 0 of animal 0 should be the same in all files." 4 | } 5 | -------------------------------------------------------------------------------- /tests/units/si/giga.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t": "Gs", "x":"mm", "y":"mm" }, 2 | "data": [{"id":"0", "t":[3e-9], "x":[0], "y":[0]}], 3 | "comment": "Timepoint 0 of animal 0 should be the same in all files." 4 | } 5 | -------------------------------------------------------------------------------- /tests/units/si/giga2.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t": "gigaseconds", "x":"mm", "y":"mm" }, 2 | "data": [{"id":"0", "t":[3e-9], "x":[0], "y":[0]}], 3 | "comment": "Timepoint 0 of animal 0 should be the same in all files." 4 | } 5 | -------------------------------------------------------------------------------- /tests/units/si/kilo.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t": "ks", "x":"mm", "y":"mm" }, 2 | "data": [{"id":"0", "t":[3e-3], "x":[0], "y":[0]}], 3 | "comment": "Timepoint 0 of animal 0 should be the same in all files." 4 | } 5 | -------------------------------------------------------------------------------- /tests/units/si/kilo2.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t": "kiloseconds", "x":"mm", "y":"mm" }, 2 | "data": [{"id":"0", "t":[3e-3], "x":[0], "y":[0]}], 3 | "comment": "Timepoint 0 of animal 0 should be the same in all files." 4 | } 5 | -------------------------------------------------------------------------------- /tests/units/si/mega.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t": "Ms", "x":"mm", "y":"mm" }, 2 | "data": [{"id":"0", "t":[3e-6], "x":[0], "y":[0]}], 3 | "comment": "Timepoint 0 of animal 0 should be the same in all files." 4 | } 5 | -------------------------------------------------------------------------------- /tests/units/si/mega2.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t": "megaseconds", "x":"mm", "y":"mm" }, 2 | "data": [{"id":"0", "t":[3e-6], "x":[0], "y":[0]}], 3 | "comment": "Timepoint 0 of animal 0 should be the same in all files." 4 | } 5 | -------------------------------------------------------------------------------- /tests/units/si/micro.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t": "us", "x":"mm", "y":"mm" }, 2 | "data": [{"id":"0", "t":[3000000], "x":[0], "y":[0]}], 3 | "comment": "Timepoint 0 of animal 0 should be the same in all files." 4 | } 5 | -------------------------------------------------------------------------------- /tests/units/si/micro2.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t": "microseconds", "x":"mm", "y":"mm" }, 2 | "data": [{"id":"0", "t":[3000000], "x":[0], "y":[0]}], 3 | "comment": "Timepoint 0 of animal 0 should be the same in all files." 4 | } 5 | -------------------------------------------------------------------------------- /tests/units/si/milli.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t": "ms", "x":"mm", "y":"mm" }, 2 | "data": [{"id":"0", "t":[3000], "x":[0], "y":[0]}], 3 | "comment": "Timepoint 0 of animal 0 should be the same in all files." 4 | } 5 | -------------------------------------------------------------------------------- /tests/units/si/milli2.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t": "milliseconds", "x":"mm", "y":"mm" }, 2 | "data": [{"id":"0", "t":[3000], "x":[0], "y":[0]}], 3 | "comment": "Timepoint 0 of animal 0 should be the same in all files." 4 | } 5 | -------------------------------------------------------------------------------- /tests/units/si/nano.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t": "ns", "x":"mm", "y":"mm" }, 2 | "data": [{"id":"0", "t":[3000000000], "x":[0], "y":[0]}], 3 | "comment": "Timepoint 0 of animal 0 should be the same in all files." 4 | } 5 | -------------------------------------------------------------------------------- /tests/units/si/nano2.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t": "nanoseconds", "x":"mm", "y":"mm" }, 2 | "data": [{"id":"0", "t":[3000000000], "x":[0], "y":[0]}], 3 | "comment": "Timepoint 0 of animal 0 should be the same in all files." 4 | } 5 | -------------------------------------------------------------------------------- /tests/units/temperature/celsius.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t":"s", "x":"mm", "y":"mm", "temperature":"C"}, 2 | "data": [{"id": "0", "t":[0], "x":[0], "y":[0]}], 3 | "metadata": { "temperature": 20 }, 4 | "comment": "All files should report the same temperature." 5 | } 6 | -------------------------------------------------------------------------------- /tests/units/temperature/celsius2.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t":"s", "x":"mm", "y":"mm", "temperature":"celsius"}, 2 | "data": [{"id": "0", "t":[0], "x":[0], "y":[0]}], 3 | "metadata": { "temperature": 20 }, 4 | "comment": "All files should report the same temperature." 5 | } 6 | -------------------------------------------------------------------------------- /tests/units/temperature/celsius3.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t":"s", "x":"mm", "y":"mm", "temperature":"Celsius"}, 2 | "data": [{"id": "0", "t":[0], "x":[0], "y":[0]}], 3 | "metadata": { "temperature": 20 }, 4 | "comment": "All files should report the same temperature." 5 | } 6 | -------------------------------------------------------------------------------- /tests/units/temperature/centigrade.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t":"s", "x":"mm", "y":"mm", "temperature":"centigrade"}, 2 | "data": [{"id": "0", "t":[0], "x":[0], "y":[0]}], 3 | "metadata": { "temperature": 20 }, 4 | "comment": "All files should report the same temperature." 5 | } 6 | -------------------------------------------------------------------------------- /tests/units/temperature/centigrade2.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t":"s", "x":"mm", "y":"mm", "temperature":"Centigrade"}, 2 | "data": [{"id": "0", "t":[0], "x":[0], "y":[0]}], 3 | "metadata": { "temperature": 20 }, 4 | "comment": "All files should report the same temperature." 5 | } 6 | -------------------------------------------------------------------------------- /tests/units/temperature/fahrenheit.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t":"s", "x":"mm", "y":"mm", "temperature":"F"}, 2 | "data": [{"id": "0", "t":[0], "x":[0], "y":[0]}], 3 | "metadata": { "temperature": 68 }, 4 | "comment": "All files should report the same temperature." 5 | } 6 | -------------------------------------------------------------------------------- /tests/units/temperature/fahrenheit2.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t":"s", "x":"mm", "y":"mm", "temperature":"fahrenheit"}, 2 | "data": [{"id": "0", "t":[0], "x":[0], "y":[0]}], 3 | "metadata": { "temperature": 68 }, 4 | "comment": "All files should report the same temperature." 5 | } 6 | -------------------------------------------------------------------------------- /tests/units/temperature/fahrenheit3.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t":"s", "x":"mm", "y":"mm", "temperature":"Fahrenheit"}, 2 | "data": [{"id": "0", "t":[0], "x":[0], "y":[0]}], 3 | "metadata": { "temperature": 68 }, 4 | "comment": "All files should report the same temperature." 5 | } 6 | -------------------------------------------------------------------------------- /tests/units/temperature/kelvin.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t":"s", "x":"mm", "y":"mm", "temperature":"K"}, 2 | "data": [{"id": "0", "t":[0], "x":[0], "y":[0]}], 3 | "metadata": { "temperature": 293.15 }, 4 | "comment": "All files should report the same temperature." 5 | } 6 | -------------------------------------------------------------------------------- /tests/units/temperature/kelvin2.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t":"s", "x":"mm", "y":"mm", "temperature":"kelvin"}, 2 | "data": [{"id": "0", "t":[0], "x":[0], "y":[0]}], 3 | "metadata": { "temperature": 293.15 }, 4 | "comment": "All files should report the same temperature." 5 | } 6 | -------------------------------------------------------------------------------- /tests/units/temperature/kelvin3.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t":"s", "x":"mm", "y":"mm", "temperature":"Kelvin"}, 2 | "data": [{"id": "0", "t":[0], "x":[0], "y":[0]}], 3 | "metadata": { "temperature": 293.15 }, 4 | "comment": "All files should report the same temperature." 5 | } 6 | -------------------------------------------------------------------------------- /tests/units/time/centisecond.wcon: -------------------------------------------------------------------------------- 1 | { "units": { "t":"centisecond", "x":"mm", "y":"mm" }, 2 | "data": [{"id": "0", "t":[17280000], "x":[0], "y":[0]}], 3 | "comment": "Timepoint 0 of animal 0 should be the same in all files." 4 | } 5 | -------------------------------------------------------------------------------- /tests/units/time/centisecond2.wcon: -------------------------------------------------------------------------------- 1 | { "units": { "t":"cs", "x":"mm", "y":"mm" }, 2 | "data": [{"id": "0", "t":[17280000], "x":[0], "y":[0]}], 3 | "comment": "Timepoint 0 of animal 0 should be the same in all files." 4 | } 5 | -------------------------------------------------------------------------------- /tests/units/time/centisecond3.wcon: -------------------------------------------------------------------------------- 1 | { "units": { "t":"s/100", "x":"mm", "y":"mm" }, 2 | "data": [{"id": "0", "t":[17280000], "x":[0], "y":[0]}], 3 | "comment": "Timepoint 0 of animal 0 should be the same in all files." 4 | } 5 | -------------------------------------------------------------------------------- /tests/units/time/day.wcon: -------------------------------------------------------------------------------- 1 | { "units": { "t":"day", "x":"mm", "y":"mm" }, 2 | "data": [{"id": "0", "t":[2], "x":[0], "y":[0]}], 3 | "comment": "Timepoint 0 of animal 0 should be the same in all files." 4 | } 5 | -------------------------------------------------------------------------------- /tests/units/time/day2.wcon: -------------------------------------------------------------------------------- 1 | { "units": { "t":"days", "x":"mm", "y":"mm" }, 2 | "data": [{"id": "0", "t":[2], "x":[0], "y":[0]}], 3 | "comment": "Timepoint 0 of animal 0 should be the same in all files." 4 | } 5 | -------------------------------------------------------------------------------- /tests/units/time/day3.wcon: -------------------------------------------------------------------------------- 1 | { "units": { "t":"d", "x":"mm", "y":"mm" }, 2 | "data": [{"id": "0", "t":[2], "x":[0], "y":[0]}], 3 | "comment": "Timepoint 0 of animal 0 should be the same in all files." 4 | } 5 | -------------------------------------------------------------------------------- /tests/units/time/hour.wcon: -------------------------------------------------------------------------------- 1 | { "units": { "t":"hour", "x":"mm", "y":"mm" }, 2 | "data": [{"id": "0", "t":[48], "x":[0], "y":[0]}], 3 | "comment": "Timepoint 0 of animal 0 should be the same in all files." 4 | } 5 | -------------------------------------------------------------------------------- /tests/units/time/hour2.wcon: -------------------------------------------------------------------------------- 1 | { "units": { "t":"hours", "x":"mm", "y":"mm" }, 2 | "data": [{"id": "0", "t":[48], "x":[0], "y":[0]}], 3 | "comment": "Timepoint 0 of animal 0 should be the same in all files." 4 | } 5 | -------------------------------------------------------------------------------- /tests/units/time/hour3.wcon: -------------------------------------------------------------------------------- 1 | { "units": { "t":"h", "x":"mm", "y":"mm" }, 2 | "data": [{"id": "0", "t":[48], "x":[0], "y":[0]}], 3 | "comment": "Timepoint 0 of animal 0 should be the same in all files." 4 | } 5 | -------------------------------------------------------------------------------- /tests/units/time/hour4.wcon: -------------------------------------------------------------------------------- 1 | { "units": { "t":"hr", "x":"mm", "y":"mm" }, 2 | "data": [{"id": "0", "t":[48], "x":[0], "y":[0]}], 3 | "comment": "Timepoint 0 of animal 0 should be the same in all files." 4 | } 5 | -------------------------------------------------------------------------------- /tests/units/time/minute.wcon: -------------------------------------------------------------------------------- 1 | { "units": { "t":"minute", "x":"mm", "y":"mm" }, 2 | "data": [{"id": "0", "t":[2880], "x":[0], "y":[0]}], 3 | "comment": "Timepoint 0 of animal 0 should be the same in all files." 4 | } 5 | -------------------------------------------------------------------------------- /tests/units/time/minute2.wcon: -------------------------------------------------------------------------------- 1 | { "units": { "t":"minutes", "x":"mm", "y":"mm" }, 2 | "data": [{"id": "0", "t":[2880], "x":[0], "y":[0]}], 3 | "comment": "Timepoint 0 of animal 0 should be the same in all files." 4 | } 5 | -------------------------------------------------------------------------------- /tests/units/time/minute3.wcon: -------------------------------------------------------------------------------- 1 | { "units": { "t":"min", "x":"mm", "y":"mm" }, 2 | "data": [{"id": "0", "t":[2880], "x":[0], "y":[0]}], 3 | "comment": "Timepoint 0 of animal 0 should be the same in all files." 4 | } 5 | -------------------------------------------------------------------------------- /tests/units/time/second.wcon: -------------------------------------------------------------------------------- 1 | { "units": { "t":"second", "x":"mm", "y":"mm" }, 2 | "data": [{"id": "0", "t":[172800], "x":[0], "y":[0]}], 3 | "comment": "Timepoint 0 of animal 0 should be the same in all files." 4 | } 5 | -------------------------------------------------------------------------------- /tests/units/time/second2.wcon: -------------------------------------------------------------------------------- 1 | { "units": { "t":"seconds", "x":"mm", "y":"mm" }, 2 | "data": [{"id": "0", "t":[172800], "x":[0], "y":[0]}], 3 | "comment": "Timepoint 0 of animal 0 should be the same in all files." 4 | } 5 | -------------------------------------------------------------------------------- /tests/units/time/second3.wcon: -------------------------------------------------------------------------------- 1 | { "units": { "t":"s", "x":"mm", "y":"mm" }, 2 | "data": [{"id": "0", "t":[172800], "x":[0], "y":[0]}], 3 | "comment": "Timepoint 0 of animal 0 should be the same in all files." 4 | } 5 | -------------------------------------------------------------------------------- /tests/units/unitless/fraction.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t":"s", "x":"mm", "y":"mm", "p":"1"}, 2 | "data": [{"id": "0", "t":[0], "x":[0], "y":[0]}], 3 | "@example": { "p": 0.72 }, 4 | "comment": "'p' in 'example' should have the same value in all files." 5 | } 6 | -------------------------------------------------------------------------------- /tests/units/unitless/percent.wcon: -------------------------------------------------------------------------------- 1 | { "units": {"t":"s", "x":"mm", "y":"mm", "p":"%"}, 2 | "data": [{"id": "0", "t":[0], "x":[0], "y":[0]}], 3 | "@example": { "p": 72 }, 4 | "comment": "'p' in 'example' should have the same value in all files." 5 | } 6 | --------------------------------------------------------------------------------