[
  {
    "path": ".gitattributes",
    "content": "src/main/java/com/neuronrobotics/bowlerstudio/NameGetter.java filter=unchanged\n* text=auto eol=lf\n*.java eol=lf"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n# github: madhephaestus\ngithub: CommonWealthRobotics \npatreon: madhephaestus\n#community_bridge: BowlerStudio\nissuehunt: CommonWealthRobotics/BowlerStudio\n#ko-fi: bowlerstudio\n#open_collective: kevin-harrington\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: \"Release\"\non: \n   push:\n       tags:       \n         - '*'\n\njobs:\n  release:\n\n    runs-on: ubuntu-latest\n\n    steps:\n    - name: Checkout repository and submodules\n      uses: actions/checkout@v2\n      with:\n         submodules: recursive \n    - name: Get the version\n      id: get_version\n      run: echo ::set-output name=VERSION::${GITHUB_REF/refs\\/tags\\//}   \n\n         \n         \n    - name: Set Release Number Studio\n      run: |\n         echo $'app.name=BowlerStudio' > src/main/resources/com/neuronrobotics/bowlerstudio/build.properties \n         echo \"app.version=${{ steps.get_version.outputs.VERSION  }}\" >> src/main/resources/com/neuronrobotics/bowlerstudio/build.properties \n         \n    - name: Set OAuth Key\n      env: # Or as an environment variable\n         OAUTH_SECRET: ${{ secrets.OAUTH_SECRET }}\n      run: |\n         sed -i \"s/REPLACE_ME/$OAUTH_SECRET/g\" src/main/java/com/neuronrobotics/bowlerstudio/NameGetter.java        \n           \n   \n    - name: start xvfb\n      run:\n        Xvfb :99 &\n    - name: initialize the X11 DISPLAY variable\n      run:\n        export DISPLAY=:99  \n\n    - name: After JDK download, list directory contnts\n      run: pwd; ls -la\n\n    - name: Build with Gradle \n      run: bash makeJar.sh\n    - name: Set Java\n      uses: actions/setup-java@v1\n      with:\n        java-version: 25\n        jdkFile: zulu25.32.21-ca-fx-jdk25.0.2-linux_x64.tar.gz\n    - name: release\n      uses: actions/create-release@v1\n      id: create_release\n      with:\n            draft: false\n            prerelease: false\n            release_name: ${{ steps.version.outputs.version }}\n            tag_name: ${{ github.ref }}\n      env:\n            GITHUB_TOKEN: ${{ github.token }}\n    - name: upload JVM Configuration\n      uses: actions/upload-release-asset@v1\n      env:\n            GITHUB_TOKEN: ${{ github.token }}\n      with:\n            upload_url: ${{ steps.create_release.outputs.upload_url }}\n            asset_path: jvm.json\n            asset_name: jvm.json\n            asset_content_type: application/json            \n    - name: upload BowlerStudio artifact\n      uses: actions/upload-release-asset@v1\n      env:\n            GITHUB_TOKEN: ${{ github.token }}\n      with:\n            upload_url: ${{ steps.create_release.outputs.upload_url }}\n            asset_path: ./build/libs/BowlerStudio.jar\n            asset_name: BowlerStudio.jar\n            asset_content_type: application/jar\n"
  },
  {
    "path": ".github/workflows/verify.yml",
    "content": "# test\nname: Test package \non:\n  push:\n    branches:\n      - '*'\n    tags-ignore:\n      - '*'\n  pull_request:\n    branches:\n      - '*'\n\njobs:\n  build:\n\n    runs-on: ubuntu-latest\n\n    steps:\n    - name: Checkout repository and submodules\n      uses: actions/checkout@v2\n      with:\n         submodules: recursive        \n        \n    - name: List directory contents\n      run: pwd; ls -la      \n      \n    - name: start xvfb\n      run:\n        Xvfb :0 &\n\n    - name: initialize the X11 DISPLAY variable\n      run:\n        export DISPLAY=:0\n\n    - name: After checkout, list directory contnts\n      run: pwd; ls -la        \n\n    - name: Build with Gradle \n      run: bash makeJar.sh\n    - name: Set Java\n      uses: actions/setup-java@v1\n      with:\n        java-version: 25\n        jdkFile: zulu25.32.21-ca-fx-jdk25.0.2-linux_x64.tar.gz\n\n    - name: Run Unit Tests\n      run: xvfb-run -s '-screen 0 1024x768x24' ./gradlew test"
  },
  {
    "path": ".gitignore",
    "content": "/build/\n/.nb-gradle/private/\n/bin/\n/JFXScad.jar\n/servoTest.jfxscad\n/wheel.stl\n/.gradle*\n/.gradle*\n/.project\n/.classpath\n/.settings*\n/.idea*\n\n/build/\n/whitecylinder/\n*.iml\n/CSGdatabase.json\n/jbullet-20101010/\n/.DS_Store\n*hs_err_pid*.log*\n*.debuggerDefaults*\n/*.svg\n/*.svg.png\n/.combined.png-autosave.kra\n/combined.png\n/Oracle_VM_VirtualBox_Extension_Pack-5.2.20.vbox-extpack\n/chdk-ptp-java.log\n/deps.txt\n/MUJOCO_LOG.TXT\n/physicsTest/\n/zulu*.tar.gz\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"libraries/bowler-script-kernel\"]\n\tpath = libraries/bowler-script-kernel\n\turl = https://github.com/CommonWealthRobotics/bowler-script-kernel.git\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: java\nbefore_install:\n  - \"export DISPLAY=:99.0\"\n  - \"export TERM=dumb\"\n  - \"sh -e /etc/init.d/xvfb start\"\n\n  \nscript:\n  - TERM=dumb ./gradlew compileJava javadoc  \ncache:\n  directories:\n  - $HOME/.m2\n\njdk:\n  - oraclejdk8\n\n# for running tests on Travis CI container infrastructure for faster builds\nsudo: true\ndist: trusty\n\n"
  },
  {
    "path": "COC.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as\ncontributors and maintainers pledge to making participation in our project and\nour community a harassment-free experience for everyone, regardless of age, body\nsize, disability, ethnicity, sex characteristics, gender identity and expression,\nlevel of experience, education, socio-economic status, nationality, personal\nappearance, race, religion, or sexual identity and orientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment\ninclude:\n\n* Using welcoming and inclusive language\n* Being respectful of differing viewpoints and experiences\n* Gracefully accepting constructive criticism\n* Focusing on what is best for the community\n* Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n* The use of sexualized language or imagery and unwelcome sexual attention or\n  advances\n* Trolling, insulting/derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or electronic\n  address, without explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable\nbehavior and are expected to take appropriate and fair corrective action in\nresponse to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or\nreject comments, commits, code, wiki edits, issues, and other contributions\nthat are not aligned to this Code of Conduct, or to ban temporarily or\npermanently any contributor for other behaviors that they deem inappropriate,\nthreatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies within all project spaces, and it also applies when\nan individual is representing the project or its community in public spaces.\nExamples of representing a project or community include using an official\nproject e-mail address, posting via an official social media account, or acting\nas an appointed representative at an online or offline event. Representation of\na project may be further defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported by contacting the project team at [INSERT EMAIL ADDRESS]. All\ncomplaints will be reviewed and investigated and will result in a response that\nis deemed necessary and appropriate to the circumstances. The project team is\nobligated to maintain confidentiality with regard to the reporter of an incident.\nFurther details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good\nfaith may face temporary or permanent repercussions as determined by other\nmembers of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,\navailable at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html\n\n[homepage]: https://www.contributor-covenant.org\n\nFor answers to common questions about this code of conduct, see\nhttps://www.contributor-covenant.org/faq\n"
  },
  {
    "path": "DEPENDENCIES.md",
    "content": "\nbatik-all.jar , Apache\nbatik-awt-util-1.7.jar , Apache\nbatik-svggen-1.7.jar , Apache\nbatik-util-1.7.jar , Apache\nbowler-kernel.jar , Apache\ncommons-beanutils-1.8.0.jar , Apache\ncommons-codec-1.13.jar , Apache\ncommons-collections-3.2.2.jar , Apache\ncommons-discovery-0.4.jar , Apache\ncommons-fileupload-1.2.1.jar , Apache\ncommons-io-2.6.jar , Apache\ncommons-lang-2.6.jar , Apache\ncommons-lang3-3.11.jar , Apache\ncommons-logging-1.2.jar , Apache\ncommons-math3-3.6.1.jar , Apache\ncommons-net-3.3.jar , Apache\ncontrolsfx-8.0.6.jar , Unknown\ngroovy-2.3.7.jar , Apache\ngroovy-all-2.4.5.jar , Apache\nhamcrest-core-1.1.jar , BSD\nhttpclient-4.5.1.jar , Apache\nhttpcore-4.4.3.jar , Apache\nhttpcore-nio-4.1.jar , Apache\nicu4j-54.1.1.jar , BSD\nivy-2.2.0.jar , Apache\njackson-annotations-2.10.1.jar , Apache\njackson-core-2.10.1.jar , Apache\njackson-databind-2.10.1.jar , Apache\njakarta.activation-api-1.2.1.jar , Unknown\njakarta.xml.bind-api-2.3.2.jar , Unknown\njavax.annotation-api-1.2.jar , CDDL\njavax.servlet-api-3.1.0.jar , CDDL\njetty-continuation-9.4.36.v20210114.jar , Apache\njetty-http-9.4.36.v20210114.jar , Apache\njetty-io-9.4.36.v20210114.jar , Apache\njetty-security-9.4.36.v20210114.jar , Apache\njetty-server-9.4.36.v20210114.jar , Apache\njetty-servlet-9.4.36.v20210114.jar , Apache\njetty-servlets-9.4.36.v20210114.jar , Apache\njetty-util-9.4.36.v20210114.jar , Apache\njetty-util-ajax-9.4.36.v20210114.jar , Apache\njetty-webapp-9.4.36.v20210114.jar , Apache\njetty-xml-9.4.36.v20210114.jar , Apache\njtok-core-1.9.3.jar , Apache\njunit-4.10.jar , BSD\njython-2.5.3.jar , Apache\njython-standalone-2.5.2.jar , Apache\nlog4j-1.2.16.jar , Apache\nmockito-all-1.9.5.jar , MIT License\nopennlp-maxent-3.0.3.jar , Apache\nopennlp-tools-1.5.3.jar , Apache\npdf-transcoder.jar , Apache\nsvg-dom-java-1.1.jar , W3C\nxml-apis-1.3.04.jar , Apache\nxml-apis-ext.jar , Apache\nxml-apis.jar , W3C\nxmlrpc-client-3.1.3.jar , Apache\nxmlrpc-common-3.1.3.jar , Apache\n"
  },
  {
    "path": "DEPENDENCIES_shallow.md",
    "content": "   annotation-indexer-1.4.jar\n   asm5-5.0.1.jar\n   asm-all-3.1.jar\n   asm-debug-all-4.2.jar\n   batik-all.jar\n   batik-awt-util-1.7.jar\n   batik-svggen-1.7.jar\n   batik-util-1.7.jar\n   bcpg-jdk15on-1.64.jar\n   bcpkix-jdk15on-1.64.jar\n   bcprov-jdk15on-1.64.jar\n   bluecove-2.1.1.jar\n   bluecove-gpl-2.1.1.jar\n   bowler-kernel.jar\n   bridge-method-annotation-1.14.jar\n   bridge-method-injector-1.14.jar\n   CHDK-PTP-Java-0.5.3-SNAPSHOT.jar\n   clojure-1.8.0.jar\n   commons-beanutils-1.8.0.jar\n   commons-codec-1.13.jar\n   commons-collections-3.2.2.jar\n   commons-discovery-0.4.jar\n   commons-fileupload-1.2.1.jar\n   commons-io-2.6.jar\n   commons-lang-2.6.jar\n   commons-lang3-3.11.jar\n   commons-logging-1.2.jar\n   commons-math3-3.6.1.jar\n   commons-net-3.3.jar\n   controlsfx-8.0.6.jar\n   de.huxhorn.sulky.3rdparty.jlayer-1.0.jar\n   easybind-1.0.4-SNAPSHOT.jar\n   emotionml-checker-java-1.1.jar\n   ezmorph-1.0.6.jar\n   fast-md5-2.7.1.jar\n   firmata4j-2.3.4.1.jar\n   flowless-0.4.jar\n   github-api-1.101.jar\n   GithubPasswordManager-0.6.1.jar\n   groovy-2.3.7.jar\n   groovy-all-2.4.5.jar\n   gson-2.5.jar\n   guava-14.0.1.jar\n   hamcrest-core-1.1.jar\n   hsqldb-2.0.0.jar\n   httpclient-4.5.1.jar\n   httpcore-4.4.3.jar\n   httpcore-nio-4.1.jar\n   icu4j-54.1.1.jar\n   ihmc-native-library-loader-1.3.1.jar\n   ivy-2.2.0.jar\n   jackson-annotations-2.10.1.jar\n   jackson-core-2.10.1.jar\n   jackson-databind-2.10.1.jar\n   jakarta.activation-api-1.2.1.jar\n   jakarta.xml.bind-api-2.3.2.jar\n   jama-1.0.3.jar\n   Jampack-1.0.jar\n   JavaCad-0.27.0.jar\n   javacpp-1.5.7.jar\n   JavaEWAH-1.1.6.jar\n   javax.annotation-api-1.2.jar\n   javax.servlet-api-3.1.0.jar\n   jbullet-2.72.2.4.jar\n   jbullet-2.72.2.4-sources.jar\n   jcommon-1.0.15.jar\n   jetty-continuation-9.4.36.v20210114.jar\n   jetty-http-9.4.36.v20210114.jar\n   jetty-io-9.4.36.v20210114.jar\n   jetty-security-9.4.36.v20210114.jar\n   jetty-server-9.4.36.v20210114.jar\n   jetty-servlet-9.4.36.v20210114.jar\n   jetty-servlets-9.4.36.v20210114.jar\n   jetty-util-9.4.36.v20210114.jar\n   jetty-util-ajax-9.4.36.v20210114.jar\n   jetty-webapp-9.4.36.v20210114.jar\n   jetty-xml-9.4.36.v20210114.jar\n   jfreechart-1.0.12.jar\n   jinput-2.0.6-ihmc2.jar\n   jinput-platform-2.0.6-natives-linux.jar\n   jinput-platform-2.0.6-natives-osx.jar\n   jinput-platform-2.0.6-natives-windows.jar\n   jl1.0.jar\n   jmf-2.1.1e.jar\n   jsapi.jar\n   jsch-0.1.55.jar\n   js.jar\n   json-20180813.jar\n   json-lib-2.4-jenkins-2.jar\n   json-simple-1.1.jar\n   jsoup-1.8.3.jar\n   jsr305-2.0.1.jar\n   jssc-2.8.0.jar\n   jtok-core-1.9.3.jar\n   junit-4.0.jar\n   junit-4.10.jar\n   jutils-1.0.0.jar\n   jwnl-1.3.3.jar\n   jxl-2.4.2.jar\n   jython-2.5.3.jar\n   jython-standalone-2.5.2.jar\n   jzlib-1.1.3.jar\n   kabeja-0.4.jar\n   kabeja-svg-0.4.jar\n   kabeja-xslt.jar\n   libusb4java-1.2.0-linux-arm.jar\n   libusb4java-1.2.0-linux-x86_64.jar\n   libusb4java-1.2.0-linux-x86.jar\n   libusb4java-1.2.0-osx-x86_64.jar\n   libusb4java-1.2.0-osx-x86.jar\n   libusb4java-1.2.0-windows-x86_64.jar\n   libusb4java-1.2.0-windows-x86.jar\n   localizer-1.7.jar\n   log4j-1.2.16.jar\n   marytts-common-5.2.jar\n   marytts-lang-en-5.2.jar\n   marytts-runtime-5.2.jar\n   marytts-signalproc-5.2.jar\n   maven-plugin-api-2.0.1.jar\n   mbrola.jar\n   Medusa-7.1.jar\n   miethxml-toolkit.jar\n   miethxml-ui.jar\n   miglayout-core-4.2.jar\n   miglayout-swing-4.2.jar\n   mockito-all-1.9.5.jar\n   motej.0.9.jar\n   mujoco-java-2.2.0-pre.5.jar\n   nrjavaserial-5.1.1.jar\n   nrsdk-3.33.0-jar-with-dependencies.jar\n   nrV4J-3.8.1.jar\n   okhttp-2.0.0.jar\n   okhttp-urlconnection-2.0.0.jar\n   okio-1.0.0.jar\n   opennlp-maxent-3.0.3.jar\n   opennlp-tools-1.5.3.jar\n   org.eclipse.jgit-5.6.0.201912101111-r.jar\n   pdf-transcoder.jar\n   protobuf-java-3.8.0.jar\n   reactfx-2.0-M5.jar\n   richtextfx-0.6.jar\n   rosjava-0.1.6.jar\n   rsyntaxtextarea-2.6.0.jar\n   slf4j-api-1.7.7.jar\n   slf4j-simple-1.6.1.jar\n   smack-3.2.1.jar\n   smackx-3.2.1.jar\n   smil-boston-dom-java-2000-02-25.jar\n   stack-alloc.jar\n   stapler-1.237.jar\n   svg-dom-java-1.1.jar\n   swing-layout-1.0.3.jar\n   tiger-types-1.3.jar\n   tink-1.3.0-rc1.jar\n   trove4j-2.0.2.jar\n   undofx-2.1.1.jar\n   usb4java-1.2.0.jar\n   usb4java-javax-1.2.0.jar\n   usb-api-1.0.2.jar\n   vecmath-1.3.1.jar\n   voice-cmu-slt-hsmm-5.2.jar\n   voice-dfki-poppy-hsmm-5.2.jar\n   voice-dfki-prudence-hsmm-5.2.jar\n   voice-dfki-spike-hsmm-5.2.jar\n   vvecmath-0.3.2.jar\n   WalnutiQ-2.3.3.jar\n   wellbehavedfx-0.1.1.jar\n   wordnet-random-name-1.2.jar\n   ws-commons-util-1.0.2.jar\n   xml-apis-1.3.04.jar\n   xml-apis-ext.jar\n   xml-apis.jar\n   xmlrpc-client-3.1.3.jar\n   xmlrpc-common-3.1.3.jar\n"
  },
  {
    "path": "DEPENDENCIES_unknown.md",
    "content": "\nannotation-indexer-1.4.jar\nasm5-5.0.1.jar\nasm-all-3.1.jar\nasm-debug-all-4.2.jar\nbcpg-jdk15on-1.64.jar\nbcpkix-jdk15on-1.64.jar\nbcprov-jdk15on-1.64.jar\nbluecove-2.1.1.jar\nbluecove-gpl-2.1.1.jar\nbridge-method-annotation-1.14.jar\nbridge-method-injector-1.14.jar\nCHDK-PTP-Java-0.5.3-SNAPSHOT.jar\nclojure-1.8.0.jar\nde.huxhorn.sulky.3rdparty.jlayer-1.0.jar\neasybind-1.0.4-SNAPSHOT.jar\nemotionml-checker-java-1.1.jar\nezmorph-1.0.6.jar\nfast-md5-2.7.1.jar\nfirmata4j-2.3.4.1.jar\nflowless-0.4.jar\ngithub-api-1.101.jar\nGithubPasswordManager-0.6.1.jar\ngson-2.5.jar\nguava-14.0.1.jar\nhsqldb-2.0.0.jar\nihmc-native-library-loader-1.3.1.jar\njama-1.0.3.jar\nJampack-1.0.jar\nJavaCad-0.27.0.jar\njavacpp-1.5.7.jar\nJavaEWAH-1.1.6.jar\njbullet-2.72.2.4.jar\njbullet-2.72.2.4-sources.jar\njcommon-1.0.15.jar\njfreechart-1.0.12.jar\njinput-2.0.6-ihmc2.jar\njinput-platform-2.0.6-natives-linux.jar\njinput-platform-2.0.6-natives-osx.jar\njinput-platform-2.0.6-natives-windows.jar\njl1.0.jar\njmf-2.1.1e.jar\njsapi.jar\njsch-0.1.55.jar\njs.jar\njson-20180813.jar\njson-lib-2.4-jenkins-2.jar\njson-simple-1.1.jar\njsoup-1.8.3.jar\njsr305-2.0.1.jar\njssc-2.8.0.jar\njunit-4.0.jar\njutils-1.0.0.jar\njwnl-1.3.3.jar\njxl-2.4.2.jar\njzlib-1.1.3.jar\nkabeja-0.4.jar\nkabeja-svg-0.4.jar\nkabeja-xslt.jar\nlibusb4java-1.2.0-linux-arm.jar\nlibusb4java-1.2.0-linux-x86_64.jar\nlibusb4java-1.2.0-linux-x86.jar\nlibusb4java-1.2.0-osx-x86_64.jar\nlibusb4java-1.2.0-osx-x86.jar\nlibusb4java-1.2.0-windows-x86_64.jar\nlibusb4java-1.2.0-windows-x86.jar\nlocalizer-1.7.jar\nmarytts-common-5.2.jar\nmarytts-lang-en-5.2.jar\nmarytts-runtime-5.2.jar\nmarytts-signalproc-5.2.jar\nmaven-plugin-api-2.0.1.jar\nmbrola.jar\nMedusa-7.1.jar\nmiethxml-toolkit.jar\nmiethxml-ui.jar\nmiglayout-core-4.2.jar\nmiglayout-swing-4.2.jar\nmotej.0.9.jar\nmujoco-java-2.2.0-pre.5.jar\nnrjavaserial-5.1.1.jar\nnrsdk-3.33.0-jar-with-dependencies.jar\nnrV4J-3.8.1.jar\nokhttp-2.0.0.jar\nokhttp-urlconnection-2.0.0.jar\nokio-1.0.0.jar\norg.eclipse.jgit-5.6.0.201912101111-r.jar\nprotobuf-java-3.8.0.jar\nreactfx-2.0-M5.jar\nrichtextfx-0.6.jar\nrosjava-0.1.6.jar\nrsyntaxtextarea-2.6.0.jar\nslf4j-api-1.7.7.jar\nslf4j-simple-1.6.1.jar\nsmack-3.2.1.jar\nsmackx-3.2.1.jar\nsmil-boston-dom-java-2000-02-25.jar\nstack-alloc.jar\nstapler-1.237.jar\nswing-layout-1.0.3.jar\ntiger-types-1.3.jar\ntink-1.3.0-rc1.jar\ntrove4j-2.0.2.jar\nundofx-2.1.1.jar\nusb4java-1.2.0.jar\nusb4java-javax-1.2.0.jar\nusb-api-1.0.2.jar\nvecmath-1.3.1.jar\nvoice-cmu-slt-hsmm-5.2.jar\nvoice-dfki-poppy-hsmm-5.2.jar\nvoice-dfki-prudence-hsmm-5.2.jar\nvoice-dfki-spike-hsmm-5.2.jar\nvvecmath-0.3.2.jar\nWalnutiQ-2.3.3.jar\nwellbehavedfx-0.1.1.jar\nwordnet-random-name-1.2.jar\nws-commons-util-1.0.2.jar\n"
  },
  {
    "path": "ISSUE_TEMPLATE.md",
    "content": "### Installed Version \n\n### OS type and version number\n\n### Expected behavior\n\n### Actual Behavior\n\n### Steps to reproduce the behavior\n"
  },
  {
    "path": "LICENSE",
    "content": "\n\nGNU LESSER GENERAL PUBLIC LICENSE\nVersion 3, 29 June 2007\n\nCopyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\n\nEveryone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.\n\nThis version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below.\n\n    0. Additional Definitions.\n\n        As used herein, \"this License\" refers to version 3 of the GNU Lesser General Public License, and the \"GNU GPL\" refers to version 3 of the GNU General Public License.\n\n        \"The Library\" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below.\n\n        An \"Application\" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library.\n\n        A \"Combined Work\" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the \"Linked Version\".\n\n        The \"Minimal Corresponding Source\" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version.\n\n        The \"Corresponding Application Code\" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work.\n    1. Exception to Section 3 of the GNU GPL.\n    You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL.\n    2. Conveying Modified Versions.\n    If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version:\n        a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or\n        b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy.\n    3. Object Code Incorporating Material from Library Header Files.\n    The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following:\n        a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License.\n        b) Accompany the object code with a copy of the GNU GPL and this license document.\n    4. Combined Works.\n    You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following:\n        a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License.\n        b) Accompany the Combined Work with a copy of the GNU GPL and this license document.\n        c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document.\n        d) Do one of the following:\n            0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.\n            1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version.\n        e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.)\n    5. Combined Libraries.\n    You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following:\n        a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License.\n        b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work.\n    6. Revised Versions of the GNU Lesser General Public License.\n\n    The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.\n\n    Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License \"or any later version\" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation.\n\n    If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library.\n"
  },
  {
    "path": "README.md",
    "content": "BowlerStudio\n==========\n\n[![Join the chat at https://gitter.im/CommonWealthRobotics/BowlerStudio](https://badges.gitter.im/CommonWealthRobotics/BowlerStudio.svg)](https://gitter.im/CommonWealthRobotics/BowlerStudioDevelopment?utm_source=share-link&utm_medium=link&utm_campaign=share-link)\n[![Test Build](https://github.com/CommonWealthRobotics/BowlerStudio/actions/workflows/verify.yml/badge.svg)](https://github.com/CommonWealthRobotics/BowlerStudio/actions/workflows/verify.yml)\n[![Github All Releases](https://img.shields.io/github/downloads/CommonWealthRobotics/BowlerStudio/total.svg)](https://github.com/CommonWealthRobotics/BowlerStudio/releases)\n\n\n[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/2904/badge)](https://bestpractices.coreinfrastructure.org/projects/2904)\n\n# [Download Latest](https://commonwealthrobotics.com/#downloads)\n\n\t\t\t\t\t\t\t\n\n# What is BowlerStudio?\n\nBowlerStudio assists you in every step of a robotics project from concept to completion. Tools enable users to:\n* Interface with motors, sensors, and other electronics hardware.\n* Create 3d models for fabrication, and for simulating the motions of your project.\n* Give your robot sight with image processing on camera feeds and Kinect data.\n* Operate 3d printers and other CNC machines.\n* Create custom graphical user interfaces to control yours robots.\n* Create and control animations.\n\n==========\n\n## The Nitty-Gritty Version\n\nBowlerStudio Robotics development IDE is based on\n* [JCSG](https://github.com/miho/JCSG)\n* [Java-Bowler](https://github.com/NeuronRobotics/java-bowler)\n* [Jinput](https://github.com/jinput/jinput)\n* [motej](http://motej.sourceforge.net/)\n* [Usb4Java](https://github.com/usb4java/usb4java)\n* [NrJavaSerial](https://github.com/NeuronRobotics/nrjavaserial)\n* [BlueCove](https://github.com/hcarver/bluecove)\n* JavaFX 8 3d engine. \n* [JBullet](http://jbullet.advel.cz/) physics engine ported from the popular Bullet C++ framework.\n* [Jetty](http://www.eclipse.org/jetty/) Web Framework\n\nBowlerStudio is a device manager, scripting engine, CAD package, and simulation tool all in one application. A user can develop the kinematic of an robot arm using the D-H parameters-based automatic kinematics engine. With this kinematics model, the user can then generate the CAD for new unique parts to match the kinematic model. The user can then export the model to an STL, and connect a Bowler 3d printer to BowlerStudio. The printer can print out the part (using the newly generated STL) while the user connects a DyIO and begins testing the servos with the kinematics model. When the print is done, the user can assemble the arm with the tested servos and run the model again to control the arm with Cartesian instructions. Once this is complete, the user can then attach a wiimote to train the robot arm through a set of tasks, recording them with the animation framework built into BowlerStudio. To be sure the arm is moving to the right place, the user can attach a webcam to the end and use OpenCV to verify the arm's position, or use the arm (in conjunction with the webcam with OpenCV enabled) to track and grab objects (IE \"eye-in-hand\" tracking). \n\nEvery step of this task can be performed from within BowlerStudio!\n\nLet's go through the main features:\n\n# Scripting With Gist\n### About scripts and Gist\n   Scripts are bits of code that BowlerStudio can load and run. BowlerStudio allows you to open a local file and run it, but BowlerStudio is most powerful when the code lives on Github Gist (a code snippet hosting service from Github). Simply give BowlerStudio the URL for a Gist you want to load and execute. Gists can be selected and edited using the built in browser, or inline in another script using the Gist ID.   \n### Java and Groovy\n   BowlerStudio can load and run scripts written in Java, Groovy, and Python. Which parser is used is determined by the file extension. Files that end in .java or .groovy will be run through the Groovy compiler. These Groovy scripts are compiled fully and run directly in the JVM. This means they will execute at full speed, just like a regular application.  \n   \n### Python\n   Python, on the other hand, by virtue of its structure, will generally execute much slower then Java. With the reduction in speed you get lots of flexibility and a clean and easy to understand syntax. The python code can also create and return objects to BowlerStudio (such as CAD CSG objects, or UI Tabs).  \n\n### Return Objects\n   A script can return a few object types that will be handled by BowlerStudio:\n   Objects of type \"CSG\" and \"MeshView\" will be added to the 3d display. If a transform is added to either of these and updated by a script the user can move an object in the 3d view. \n   Objects of type \"Tab\" will be added to the Tabmanager and displayed in BowlerStudio. This is an easy way to make control panels or state displays and monitors. \n   Objects of type \"BowlerAbstractDevice\" (or any subclass) will be added to the connections manager and made available to all other scripts. These can be external devices or virtual communication bus devices. A bowler Server/Client pair is the preferred mechanism for communication between scripts. \n### Device Access\n   All scripts are passed all connected devices by name when the script is run. The name associated with the device in the connections tab is the name to use in the script to access that device. A script can also create and return a device (EG to connect to a specific device in order to give that device a specific name). The device returned will be added to the list of available devices and be available to other scripts. A user can define their own devices to facilitate communication between scripts. \n   \n# Bowler Devices\n   BowlerDevices (such as the Neuron Robotics DyIO) are devices that implement the Bowler Communication System. BowlerDevices are servers of features to applications. The DyIO, for example, is a server of microcontroller features. These devices implement a micro domain-specific language as a protocol. This language synchronizes the device with the application by building the communication system at runtime using a namespace/RPC system. \n   As such, the device is treated as a collection of namespaces. Each namespace has a set of RPCs for communication, some synchronous (IE they are application initiated) and some asynchronous (IE device initiated). In addition, each RPC has full method introspection. This means that all parameters and datatypes (including how to pack and interpret all packets) are able to be queried over the communication system. A Library need only implement the core packet parser and every device will assemble its own communication layer live.  \n\n# Cameras\n   Cameras can be connected to Bowler Studio using one of 3 supported drivers:\n   OpenCV's native Java bindings are provided by installing OpenCV using your OS specific installer (unfortunately not available for Mac at this time). \n   JavaCV is a meta-library that adds support for a wide range of camera device integrations and image processing options. This is a big project and integrated now with a full scripting system. \n   CHDK-PTP-Java is a Java library that adds support for SLR Cannon cameras. CHDK is a camera OS that makes the cameras features available over USB, and the Java library makes those images and controls available to Java and or scripting engine. \n\n# Image processing\n   Image processing is provided be a variety of libraries included in this application. OpenCV and ARToolkit are some of the most widely used image recognition libraries available, and now you can use them directly from our scripting environment! \n   \n# Kinematics Engine\n   The Bowler Kinematics engine is based on [D-H parameters](https://www.youtube.com/embed/rA9tm0gTln8)- the standard mathematical definition of kinematics chains. This standard simplifies the calculation process and allows us to run forward kinematics equations for arbitrary defined chains in real time. For inverse kinematics, a collection of kinematic engines are available for optimizing for speed and accuracy. \n\n# Real-Time validated\n   For applications where real time is required, there is no need to leave the Bowler OS. The Bowler Java stack has been validated as real-time capable when run on JamaicaVM (the real-time Java implementation). The Bowler Kinematics engine is run in a real-time loop for neurosurgery applications (Bowler and Java are fast and reliable enough for brain surgery!)\n   \n# 3D CAD\n   Users can write scripts using Java, Groovy or Python to generate CSG style CAD. This programmatic CAD engine JCSG was inspired by OpenSCAD, but implemented in pure Java with JavaFX visualizations. JCSG implements all basic shape generation and manipulation, using Java's library packaging and distribution for libraries of parts. Gist hosting of parts can also simplify sharing and loading of dependent libraries. \n   \n# Virtualization\n### Virtual links\n   Virtual PID devices allow users to make applications that can be simulated with virtual links before ever connecting a real device. \n   Users can also use the PIDLab to learn about designing and implementing PID controllers with a built in motor physics simulation. \n### Virtual Camera\n   Coming soon! Soon users will be able to interact with the 3D environment camera from their code just like a real camera. Users will be able to manipulate it just like it was another 3d object. \n   \n### Virtual Sensors\n   Coming soon! Soon we will be able to provide virtual sensor devices, simulating real world sensors within the 3d environment. \n\n# Make A Contribution\nBowlerStudio is an open source project and is always looking for help with both the application code and the tutorials content. \n\n### Java Contributions\n\nIf you are a Java developmer, skip ahead to [The Build Instructions](#command-line). The application is a light plugin framework for UI, 3D and Device interaction. You can look at this repository for issues. \n\n### Adding Tutorials\n\nAll of the content for BowlerStudio Tutorials is housed on our [Neuronrobotics.github.io](https://github.com/CommonWealthRobotics/CommonWealthRobotics.github.io) web page. Fork that repository and make contributions based on the README.md file in the root of the repository. To merge the changes into the main website, send a pull request with your changes to official repository. \n\nExamples of tutorials that need to be added are [A simple Java Programming Introduction](https://github.com/CommonWealthRobotics/CommonWealthRobotics.github.io/issues/59). This tutorial set would go through the basic syntax of java and what all of the symbols mean and how to use them. \n\nAnother example of a tutorial that could be added is one for [JavaCad Cheatsheet](https://github.com/CommonWealthRobotics/CommonWealthRobotics.github.io/issues/58) where you would add a 'cheat sheet' of commands to use in the JavaCad system. \n\nIf a tutorial is missing and not described as needed by an issue, feel free to add additional issues. \n\n## How to Build BowlerStudio\n\n### Requirements\n\n- Internet connection (dependencies are downloaded automatically)\n- IDE: [Gradle](http://www.gradle.org/) Plugin (not necessary for command line usage)\n- The Binary installer for BowlerStudio must be installed before develpment. \n\n### IDE\n\nOpen the `BowlerStudio` [Gradle](http://www.gradle.org/) project in your favorite IDE (tested with NetBeans 7.4) and build \nby calling the `assemble` task.\n\n#### Windows Setup Instructions\n\n- Download and install Sloeber Eclipse or an alternative Java IDE of your choice. \n- Install BowlerStudio via the provided installer. This will ensure that the correct JVM is accessible.\n- Enable Clone a Git repository in Eclipse (Window > Show View > Other... > Git > Git Repositories > Clone a Git repository)\n- Clone this repository via HTTPS and use your [GitHub personal access token](https://github.com/settings/tokens).\n  - Make sure to enable the option to clone submodules. \n- Set the default JVM in Eclipse to the following file path: `C:\\Program Files (x86)\\Commonwealth Robotics BowlerStudio\\BowlerStudioApp\\jre`. (Window > Preferences > Java > Installed JREs)\n- In the Eclipse gradle configuration, set the `javahome` path to the following file path: `C:\\Program Files (x86)\\Commonwealth Robotics BowlerStudio\\BowlerStudioApp\\jre`. (Window > Preferences > Gradle > Jave home)\n- Import the cloned project into Eclipse using gradle. (File > Import... > Gradle > Existing Gradle Project)\n  - The path to add can be found in Eclipse by right-clicking the git repo and selecting Copy Path to Clipboard. Remove the /.git from the directory before importing.\n\n\n\n### Command Line\nNavigate to the [Gradle](http://www.gradle.org/) project (e.g., `path/to/BowlerStudio`) and enter the following command:\n\n#### Bash (Linux/OS X/Cygwin/other Unix-like shell)\n##### Ubuntu 18.04 Dependencies\n\n```\nsudo apt install  curl\n```\n\n##### Ubuntu 16.04 Dependencies\n```   \n    sudo apt-get install git gradle\n```\n##### Ubuntu 14.04, install extra dependencies\n```\n\tsudo add-apt-repository \"deb http://ppa.launchpad.net/mad-hephaestus/commonwealthrobotics/ubuntu xenial main\" -y\n\tsudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 71EA898B \n\tsudo add-apt-repository \"deb http://us.archive.ubuntu.com/ubuntu/ trusty universe multiverse\"\n\tsudo apt-get update -qq\n\tsudo apt-get install -y --force-yes gradle \n```\n##### All Unix  \n```  \n    bowlerstudio # run this to get the JVM downloaded into $HOME/bin/java8/jre\n\n    git clone https://github.com/CommonWealthRobotics/BowlerStudio.git\n    \n    JAVA_HOME=$HOME/bin/java8/jre\n\n    export JAVA_HOME\n   \n    cd BowlerStudio\n    \n    git submodule update --init --recursive\n    \n    git submodule update  --recursive\n    \n    ./gradlew shadowJar\n    \n    $JAVA_HOME/bin/java -jar build/libs/BowlerStudio*.jar\n```\nNow you can use the Eclipse Marketplace to install the Gradle Plugin\n    \n#### Windows (CMD)\n\n    gradlew jar\n    \n# History\n\nBowler Studio began [Feb 11, 2015](https://github.com/CommonWealthRobotics/BowlerStudio/releases/tag/0.0.1)  with the goal of making a robotics IDE. \n\n"
  },
  {
    "path": "alllibs.txt",
    "content": "Starting a Gradle Daemon (subsequent builds will be faster)\n\n> Task :showAll\nBowlerStudio:\n-compile:\n   bowler-kernel.jar\n   mbrola.jar\n   rosjava-0.1.6.jar\n   jsapi.jar\n   jl1.0.jar\n   jbullet-2.72.2.4.jar\n   nrV4J-3.8.1.jar\n   asm-all-3.1.jar\n   jbullet-2.72.2.4-sources.jar\n   stack-alloc.jar\n   GithubPasswordManager-0.6.1.jar\n   tink-1.3.0-rc1.jar\n   json-20180813.jar\n   nrsdk-3.33.0-jar-with-dependencies.jar\n   bluecove-gpl-2.1.1.jar\n   motej.0.9.jar\n   bluecove-2.1.1.jar\n   voice-dfki-poppy-hsmm-5.2.jar\n   voice-dfki-prudence-hsmm-5.2.jar\n   voice-dfki-spike-hsmm-5.2.jar\n   voice-cmu-slt-hsmm-5.2.jar\n   marytts-lang-en-5.2.jar\n   marytts-runtime-5.2.jar\n   marytts-signalproc-5.2.jar\n   jama-1.0.3.jar\n   richtextfx-0.6.jar\n   flowless-0.4.jar\n   undofx-2.1.1.jar\n   reactfx-2.0-M5.jar\n   groovy-2.3.7.jar\n   ivy-2.2.0.jar\n   controlsfx-8.0.6.jar\n   stapler-1.237.jar\n   json-lib-2.4-jenkins-2.jar\n   marytts-common-5.2.jar\n   ezmorph-1.0.6.jar\n   commons-lang-2.6.jar\n   httpclient-4.5.1.jar\n   github-api-1.101.jar\n   commons-codec-1.13.jar\n   org.eclipse.jgit-5.6.0.201912101111-r.jar\n   okhttp-urlconnection-2.0.0.jar\n   wordnet-random-name-1.2.jar\n   mockito-all-1.9.5.jar\n   bridge-method-injector-1.14.jar\n   miglayout-swing-4.2.jar\n   JavaCad-0.27.0.jar\n   pdf-transcoder.jar\n   xml-apis-ext.jar\n   kabeja-svg-0.4.jar\n   miethxml-toolkit.jar\n   batik-all.jar\n   js.jar\n   xml-apis.jar\n   kabeja-0.4.jar\n   kabeja-xslt.jar\n   miethxml-ui.jar\n   commons-io-2.6.jar\n   jython-2.5.3.jar\n   jython-standalone-2.5.2.jar\n   clojure-1.8.0.jar\n   jetty-webapp-9.4.36.v20210114.jar\n   jetty-servlet-9.4.36.v20210114.jar\n   jetty-security-9.4.36.v20210114.jar\n   jetty-server-9.4.36.v20210114.jar\n   jetty-servlets-9.4.36.v20210114.jar\n   javax.servlet-api-3.1.0.jar\n   vecmath-1.3.1.jar\n   slf4j-simple-1.6.1.jar\n   CHDK-PTP-Java-0.5.3-SNAPSHOT.jar\n   WalnutiQ-2.3.3.jar\n   jxl-2.4.2.jar\n   de.huxhorn.sulky.3rdparty.jlayer-1.0.jar\n   gson-2.5.jar\n   jsoup-1.8.3.jar\n   jmf-2.1.1e.jar\n   firmata4j-2.3.4.1.jar\n   jfreechart-1.0.12.jar\n   xmlrpc-client-3.1.3.jar\n   rsyntaxtextarea-2.6.0.jar\n   batik-svggen-1.7.jar\n   svg-dom-java-1.1.jar\n   \n   Medusa-7.1.jar\n   protobuf-java-3.8.0.jar\n   wellbehavedfx-0.1.1.jar\n   easybind-1.0.4-SNAPSHOT.jar\n   javax.annotation-api-1.2.jar\n   commons-discovery-0.4.jar\n   commons-beanutils-1.8.0.jar\n   localizer-1.7.jar\n   tiger-types-1.3.jar\n   guava-14.0.1.jar\n   asm5-5.0.1.jar\n   commons-fileupload-1.2.1.jar\n   jsr305-2.0.1.jar\n   jzlib-1.1.3.jar\n   jsch-0.1.55.jar\n   JavaEWAH-1.1.6.jar\n   jtok-core-1.9.3.jar\n   slf4j-api-1.7.7.jar\n   bcpg-jdk15on-1.64.jar\n   bcpkix-jdk15on-1.64.jar\n   bcprov-jdk15on-1.64.jar\n   okhttp-2.0.0.jar\n   bridge-method-annotation-1.14.jar\n   asm-debug-all-4.2.jar\n   maven-plugin-api-2.0.1.jar\n   miglayout-core-4.2.jar\n   jetty-http-9.4.36.v20210114.jar\n   jetty-io-9.4.36.v20210114.jar\n   jetty-util-ajax-9.4.36.v20210114.jar\n   jetty-continuation-9.4.36.v20210114.jar\n   jetty-xml-9.4.36.v20210114.jar\n   jetty-util-9.4.36.v20210114.jar\n   usb4java-javax-1.2.0.jar\n   httpcore-nio-4.1.jar\n   httpcore-4.4.3.jar\n   commons-logging-1.2.jar\n   jssc-2.8.0.jar\n   jcommon-1.0.15.jar\n   xmlrpc-common-3.1.3.jar\n   batik-awt-util-1.7.jar\n   batik-util-1.7.jar\n   ws-commons-util-1.0.2.jar\n   xml-apis-1.3.04.jar\n   smil-boston-dom-java-2000-02-25.jar\n   smack-3.2.1.jar\n   smackx-3.2.1.jar\n   usb4java-1.2.0.jar\n   jinput-2.0.6-ihmc2.jar\n   ihmc-native-library-loader-1.3.1.jar\n   commons-lang3-3.11.jar\n   nrjavaserial-5.1.1.jar\n   commons-math3-3.6.1.jar\n   mujoco-java-2.2.0-pre.5.jar\n   junit-4.0.jar\n   json-simple-1.1.jar\n   vvecmath-0.3.2.jar\n   commons-collections-3.2.2.jar\n   okio-1.0.0.jar\n   annotation-indexer-1.4.jar\n   jackson-databind-2.10.1.jar\n   usb-api-1.0.2.jar\n   libusb4java-1.2.0-linux-x86.jar\n   libusb4java-1.2.0-linux-x86_64.jar\n   libusb4java-1.2.0-linux-arm.jar\n   libusb4java-1.2.0-windows-x86.jar\n   libusb4java-1.2.0-windows-x86_64.jar\n   libusb4java-1.2.0-osx-x86.jar\n   libusb4java-1.2.0-osx-x86_64.jar\n   commons-net-3.3.jar\n   jutils-1.0.0.jar\n   jinput-platform-2.0.6-natives-windows.jar\n   jinput-platform-2.0.6-natives-osx.jar\n   jinput-platform-2.0.6-natives-linux.jar\n   jakarta.xml.bind-api-2.3.2.jar\n   javacpp-1.5.7.jar\n   icu4j-54.1.1.jar\n   emotionml-checker-java-1.1.jar\n   trove4j-2.0.2.jar\n   opennlp-tools-1.5.3.jar\n   opennlp-maxent-3.0.3.jar\n   hsqldb-2.0.0.jar\n   log4j-1.2.16.jar\n   fast-md5-2.7.1.jar\n   groovy-all-2.4.5.jar\n   Jampack-1.0.jar\n   swing-layout-1.0.3.jar\n   jackson-annotations-2.10.1.jar\n   jackson-core-2.10.1.jar\n   jakarta.activation-api-1.2.1.jar\n   jwnl-1.3.3.jar\n-testCompile:\n   bowler-kernel.jar\n   mbrola.jar\n   rosjava-0.1.6.jar\n   jsapi.jar\n   jl1.0.jar\n   jbullet-2.72.2.4.jar\n   nrV4J-3.8.1.jar\n   asm-all-3.1.jar\n   jbullet-2.72.2.4-sources.jar\n   stack-alloc.jar\n   GithubPasswordManager-0.6.1.jar\n   tink-1.3.0-rc1.jar\n   json-20180813.jar\n   nrsdk-3.33.0-jar-with-dependencies.jar\n   bluecove-gpl-2.1.1.jar\n   motej.0.9.jar\n   bluecove-2.1.1.jar\n   voice-dfki-poppy-hsmm-5.2.jar\n   voice-dfki-prudence-hsmm-5.2.jar\n   voice-dfki-spike-hsmm-5.2.jar\n   voice-cmu-slt-hsmm-5.2.jar\n   marytts-lang-en-5.2.jar\n   marytts-runtime-5.2.jar\n   marytts-signalproc-5.2.jar\n   jama-1.0.3.jar\n   richtextfx-0.6.jar\n   flowless-0.4.jar\n   undofx-2.1.1.jar\n   reactfx-2.0-M5.jar\n   groovy-2.3.7.jar\n   ivy-2.2.0.jar\n   controlsfx-8.0.6.jar\n   stapler-1.237.jar\n   json-lib-2.4-jenkins-2.jar\n   marytts-common-5.2.jar\n   ezmorph-1.0.6.jar\n   commons-lang-2.6.jar\n   httpclient-4.5.1.jar\n   github-api-1.101.jar\n   commons-codec-1.13.jar\n   org.eclipse.jgit-5.6.0.201912101111-r.jar\n   okhttp-urlconnection-2.0.0.jar\n   wordnet-random-name-1.2.jar\n   mockito-all-1.9.5.jar\n   bridge-method-injector-1.14.jar\n   miglayout-swing-4.2.jar\n   JavaCad-0.27.0.jar\n   pdf-transcoder.jar\n   xml-apis-ext.jar\n   kabeja-svg-0.4.jar\n   miethxml-toolkit.jar\n   batik-all.jar\n   js.jar\n   xml-apis.jar\n   kabeja-0.4.jar\n   kabeja-xslt.jar\n   miethxml-ui.jar\n   commons-io-2.6.jar\n   jython-2.5.3.jar\n   jython-standalone-2.5.2.jar\n   clojure-1.8.0.jar\n   jetty-webapp-9.4.36.v20210114.jar\n   jetty-servlet-9.4.36.v20210114.jar\n   jetty-security-9.4.36.v20210114.jar\n   jetty-server-9.4.36.v20210114.jar\n   jetty-servlets-9.4.36.v20210114.jar\n   javax.servlet-api-3.1.0.jar\n   vecmath-1.3.1.jar\n   slf4j-simple-1.6.1.jar\n   CHDK-PTP-Java-0.5.3-SNAPSHOT.jar\n   WalnutiQ-2.3.3.jar\n   jxl-2.4.2.jar\n   de.huxhorn.sulky.3rdparty.jlayer-1.0.jar\n   gson-2.5.jar\n   jsoup-1.8.3.jar\n   jmf-2.1.1e.jar\n   firmata4j-2.3.4.1.jar\n   jfreechart-1.0.12.jar\n   xmlrpc-client-3.1.3.jar\n   rsyntaxtextarea-2.6.0.jar\n   batik-svggen-1.7.jar\n   svg-dom-java-1.1.jar\n   \n   Medusa-7.1.jar\n   xmlrpc-common-3.1.3.jar\n   ws-commons-util-1.0.2.jar\n   junit-4.10.jar\n   protobuf-java-3.8.0.jar\n   wellbehavedfx-0.1.1.jar\n   easybind-1.0.4-SNAPSHOT.jar\n   javax.annotation-api-1.2.jar\n   commons-discovery-0.4.jar\n   commons-beanutils-1.8.0.jar\n   localizer-1.7.jar\n   tiger-types-1.3.jar\n   guava-14.0.1.jar\n   asm5-5.0.1.jar\n   commons-fileupload-1.2.1.jar\n   jsr305-2.0.1.jar\n   jzlib-1.1.3.jar\n   jsch-0.1.55.jar\n   JavaEWAH-1.1.6.jar\n   jtok-core-1.9.3.jar\n   slf4j-api-1.7.7.jar\n   bcpg-jdk15on-1.64.jar\n   bcpkix-jdk15on-1.64.jar\n   bcprov-jdk15on-1.64.jar\n   okhttp-2.0.0.jar\n   bridge-method-annotation-1.14.jar\n   asm-debug-all-4.2.jar\n   maven-plugin-api-2.0.1.jar\n   miglayout-core-4.2.jar\n   jetty-http-9.4.36.v20210114.jar\n   jetty-io-9.4.36.v20210114.jar\n   jetty-util-ajax-9.4.36.v20210114.jar\n   jetty-continuation-9.4.36.v20210114.jar\n   jetty-xml-9.4.36.v20210114.jar\n   jetty-util-9.4.36.v20210114.jar\n   usb4java-javax-1.2.0.jar\n   httpcore-nio-4.1.jar\n   httpcore-4.4.3.jar\n   commons-logging-1.2.jar\n   jssc-2.8.0.jar\n   jcommon-1.0.15.jar\n   batik-awt-util-1.7.jar\n   batik-util-1.7.jar\n   xml-apis-1.3.04.jar\n   smil-boston-dom-java-2000-02-25.jar\n   smack-3.2.1.jar\n   smackx-3.2.1.jar\n   usb4java-1.2.0.jar\n   jinput-2.0.6-ihmc2.jar\n   ihmc-native-library-loader-1.3.1.jar\n   commons-lang3-3.11.jar\n   nrjavaserial-5.1.1.jar\n   commons-math3-3.6.1.jar\n   mujoco-java-2.2.0-pre.5.jar\n   json-simple-1.1.jar\n   vvecmath-0.3.2.jar\n   hamcrest-core-1.1.jar\n   commons-collections-3.2.2.jar\n   okio-1.0.0.jar\n   annotation-indexer-1.4.jar\n   jackson-databind-2.10.1.jar\n   usb-api-1.0.2.jar\n   libusb4java-1.2.0-linux-x86.jar\n   libusb4java-1.2.0-linux-x86_64.jar\n   libusb4java-1.2.0-linux-arm.jar\n   libusb4java-1.2.0-windows-x86.jar\n   libusb4java-1.2.0-windows-x86_64.jar\n   libusb4java-1.2.0-osx-x86.jar\n   libusb4java-1.2.0-osx-x86_64.jar\n   commons-net-3.3.jar\n   jutils-1.0.0.jar\n   jinput-platform-2.0.6-natives-windows.jar\n   jinput-platform-2.0.6-natives-osx.jar\n   jinput-platform-2.0.6-natives-linux.jar\n   jakarta.xml.bind-api-2.3.2.jar\n   javacpp-1.5.7.jar\n   icu4j-54.1.1.jar\n   emotionml-checker-java-1.1.jar\n   trove4j-2.0.2.jar\n   opennlp-tools-1.5.3.jar\n   opennlp-maxent-3.0.3.jar\n   hsqldb-2.0.0.jar\n   log4j-1.2.16.jar\n   fast-md5-2.7.1.jar\n   groovy-all-2.4.5.jar\n   Jampack-1.0.jar\n   swing-layout-1.0.3.jar\n   jackson-annotations-2.10.1.jar\n   jackson-core-2.10.1.jar\n   jakarta.activation-api-1.2.1.jar\n   jwnl-1.3.3.jar\nlibraries:\n-compile:\n   bowler-kernel.jar\n   mbrola.jar\n   rosjava-0.1.6.jar\n   jsapi.jar\n   jl1.0.jar\n   jbullet-2.72.2.4.jar\n   nrV4J-3.8.1.jar\n   asm-all-3.1.jar\n   jbullet-2.72.2.4-sources.jar\n   stack-alloc.jar\n   GithubPasswordManager-0.6.1.jar\n   tink-1.3.0-rc1.jar\n   json-20180813.jar\n   nrsdk-3.33.0-jar-with-dependencies.jar\n   bluecove-gpl-2.1.1.jar\n   motej.0.9.jar\n   bluecove-2.1.1.jar\n   voice-dfki-poppy-hsmm-5.2.jar\n   voice-dfki-prudence-hsmm-5.2.jar\n   voice-dfki-spike-hsmm-5.2.jar\n   voice-cmu-slt-hsmm-5.2.jar\n   marytts-lang-en-5.2.jar\n   marytts-runtime-5.2.jar\n   marytts-signalproc-5.2.jar\n   jama-1.0.3.jar\n   richtextfx-0.6.jar\n   flowless-0.4.jar\n   undofx-2.1.1.jar\n   reactfx-2.0-M5.jar\n   groovy-2.3.7.jar\n   ivy-2.2.0.jar\n   controlsfx-8.0.6.jar\n   stapler-1.237.jar\n   json-lib-2.4-jenkins-2.jar\n   marytts-common-5.2.jar\n   ezmorph-1.0.6.jar\n   commons-lang-2.6.jar\n   httpclient-4.5.1.jar\n   github-api-1.101.jar\n   commons-codec-1.13.jar\n   org.eclipse.jgit-5.6.0.201912101111-r.jar\n   okhttp-urlconnection-2.0.0.jar\n   wordnet-random-name-1.2.jar\n   mockito-all-1.9.5.jar\n   bridge-method-injector-1.14.jar\n   miglayout-swing-4.2.jar\n   JavaCad-0.27.0.jar\n   pdf-transcoder.jar\n   xml-apis-ext.jar\n   kabeja-svg-0.4.jar\n   miethxml-toolkit.jar\n   batik-all.jar\n   js.jar\n   xml-apis.jar\n   kabeja-0.4.jar\n   kabeja-xslt.jar\n   miethxml-ui.jar\n   commons-io-2.6.jar\n   jython-2.5.3.jar\n   jython-standalone-2.5.2.jar\n   clojure-1.8.0.jar\n   jetty-webapp-9.4.36.v20210114.jar\n   jetty-servlet-9.4.36.v20210114.jar\n   jetty-security-9.4.36.v20210114.jar\n   jetty-server-9.4.36.v20210114.jar\n   jetty-servlets-9.4.36.v20210114.jar\n   javax.servlet-api-3.1.0.jar\n   vecmath-1.3.1.jar\n   slf4j-simple-1.6.1.jar\n   CHDK-PTP-Java-0.5.3-SNAPSHOT.jar\n   WalnutiQ-2.3.3.jar\n   jxl-2.4.2.jar\n   de.huxhorn.sulky.3rdparty.jlayer-1.0.jar\n   gson-2.5.jar\n   jsoup-1.8.3.jar\n   jmf-2.1.1e.jar\n   firmata4j-2.3.4.1.jar\n   jfreechart-1.0.12.jar\n   xmlrpc-client-3.1.3.jar\n   rsyntaxtextarea-2.6.0.jar\n   batik-svggen-1.7.jar\n   svg-dom-java-1.1.jar\n   \n   Medusa-7.1.jar\n   protobuf-java-3.8.0.jar\n   wellbehavedfx-0.1.1.jar\n   easybind-1.0.4-SNAPSHOT.jar\n   javax.annotation-api-1.2.jar\n   commons-discovery-0.4.jar\n   commons-beanutils-1.8.0.jar\n   localizer-1.7.jar\n   tiger-types-1.3.jar\n   guava-14.0.1.jar\n   asm5-5.0.1.jar\n   commons-fileupload-1.2.1.jar\n   jsr305-2.0.1.jar\n   jzlib-1.1.3.jar\n   jsch-0.1.55.jar\n   JavaEWAH-1.1.6.jar\n   jtok-core-1.9.3.jar\n   slf4j-api-1.7.7.jar\n   bcpg-jdk15on-1.64.jar\n   bcpkix-jdk15on-1.64.jar\n   bcprov-jdk15on-1.64.jar\n   okhttp-2.0.0.jar\n   bridge-method-annotation-1.14.jar\n   asm-debug-all-4.2.jar\n   maven-plugin-api-2.0.1.jar\n   miglayout-core-4.2.jar\n   jetty-http-9.4.36.v20210114.jar\n   jetty-io-9.4.36.v20210114.jar\n   jetty-util-ajax-9.4.36.v20210114.jar\n   jetty-continuation-9.4.36.v20210114.jar\n   jetty-xml-9.4.36.v20210114.jar\n   jetty-util-9.4.36.v20210114.jar\n   usb4java-javax-1.2.0.jar\n   httpcore-nio-4.1.jar\n   httpcore-4.4.3.jar\n   commons-logging-1.2.jar\n   jssc-2.8.0.jar\n   jcommon-1.0.15.jar\n   xmlrpc-common-3.1.3.jar\n   batik-awt-util-1.7.jar\n   batik-util-1.7.jar\n   ws-commons-util-1.0.2.jar\n   xml-apis-1.3.04.jar\n   smil-boston-dom-java-2000-02-25.jar\n   smack-3.2.1.jar\n   smackx-3.2.1.jar\n   usb4java-1.2.0.jar\n   jinput-2.0.6-ihmc2.jar\n   ihmc-native-library-loader-1.3.1.jar\n   commons-lang3-3.11.jar\n   nrjavaserial-5.1.1.jar\n   commons-math3-3.6.1.jar\n   mujoco-java-2.2.0-pre.5.jar\n   junit-4.0.jar\n   json-simple-1.1.jar\n   vvecmath-0.3.2.jar\n   commons-collections-3.2.2.jar\n   okio-1.0.0.jar\n   annotation-indexer-1.4.jar\n   jackson-databind-2.10.1.jar\n   usb-api-1.0.2.jar\n   libusb4java-1.2.0-linux-x86.jar\n   libusb4java-1.2.0-linux-x86_64.jar\n   libusb4java-1.2.0-linux-arm.jar\n   libusb4java-1.2.0-windows-x86.jar\n   libusb4java-1.2.0-windows-x86_64.jar\n   libusb4java-1.2.0-osx-x86.jar\n   libusb4java-1.2.0-osx-x86_64.jar\n   commons-net-3.3.jar\n   jutils-1.0.0.jar\n   jinput-platform-2.0.6-natives-windows.jar\n   jinput-platform-2.0.6-natives-osx.jar\n   jinput-platform-2.0.6-natives-linux.jar\n   jakarta.xml.bind-api-2.3.2.jar\n   javacpp-1.5.7.jar\n   icu4j-54.1.1.jar\n   emotionml-checker-java-1.1.jar\n   trove4j-2.0.2.jar\n   opennlp-tools-1.5.3.jar\n   opennlp-maxent-3.0.3.jar\n   hsqldb-2.0.0.jar\n   log4j-1.2.16.jar\n   fast-md5-2.7.1.jar\n   groovy-all-2.4.5.jar\n   Jampack-1.0.jar\n   swing-layout-1.0.3.jar\n   jackson-annotations-2.10.1.jar\n   jackson-core-2.10.1.jar\n   jakarta.activation-api-1.2.1.jar\n   jwnl-1.3.3.jar\n-testCompile:\n   bowler-kernel.jar\n   mbrola.jar\n   rosjava-0.1.6.jar\n   jsapi.jar\n   jl1.0.jar\n   jbullet-2.72.2.4.jar\n   nrV4J-3.8.1.jar\n   asm-all-3.1.jar\n   jbullet-2.72.2.4-sources.jar\n   stack-alloc.jar\n   GithubPasswordManager-0.6.1.jar\n   tink-1.3.0-rc1.jar\n   json-20180813.jar\n   nrsdk-3.33.0-jar-with-dependencies.jar\n   bluecove-gpl-2.1.1.jar\n   motej.0.9.jar\n   bluecove-2.1.1.jar\n   voice-dfki-poppy-hsmm-5.2.jar\n   voice-dfki-prudence-hsmm-5.2.jar\n   voice-dfki-spike-hsmm-5.2.jar\n   voice-cmu-slt-hsmm-5.2.jar\n   marytts-lang-en-5.2.jar\n   marytts-runtime-5.2.jar\n   marytts-signalproc-5.2.jar\n   jama-1.0.3.jar\n   richtextfx-0.6.jar\n   flowless-0.4.jar\n   undofx-2.1.1.jar\n   reactfx-2.0-M5.jar\n   groovy-2.3.7.jar\n   ivy-2.2.0.jar\n   controlsfx-8.0.6.jar\n   stapler-1.237.jar\n   json-lib-2.4-jenkins-2.jar\n   marytts-common-5.2.jar\n   ezmorph-1.0.6.jar\n   commons-lang-2.6.jar\n   httpclient-4.5.1.jar\n   github-api-1.101.jar\n   commons-codec-1.13.jar\n   org.eclipse.jgit-5.6.0.201912101111-r.jar\n   okhttp-urlconnection-2.0.0.jar\n   wordnet-random-name-1.2.jar\n   mockito-all-1.9.5.jar\n   bridge-method-injector-1.14.jar\n   miglayout-swing-4.2.jar\n   JavaCad-0.27.0.jar\n   pdf-transcoder.jar\n   xml-apis-ext.jar\n   kabeja-svg-0.4.jar\n   miethxml-toolkit.jar\n   batik-all.jar\n   js.jar\n   xml-apis.jar\n   kabeja-0.4.jar\n   kabeja-xslt.jar\n   miethxml-ui.jar\n   commons-io-2.6.jar\n   jython-2.5.3.jar\n   jython-standalone-2.5.2.jar\n   clojure-1.8.0.jar\n   jetty-webapp-9.4.36.v20210114.jar\n   jetty-servlet-9.4.36.v20210114.jar\n   jetty-security-9.4.36.v20210114.jar\n   jetty-server-9.4.36.v20210114.jar\n   jetty-servlets-9.4.36.v20210114.jar\n   javax.servlet-api-3.1.0.jar\n   vecmath-1.3.1.jar\n   slf4j-simple-1.6.1.jar\n   CHDK-PTP-Java-0.5.3-SNAPSHOT.jar\n   WalnutiQ-2.3.3.jar\n   jxl-2.4.2.jar\n   de.huxhorn.sulky.3rdparty.jlayer-1.0.jar\n   gson-2.5.jar\n   jsoup-1.8.3.jar\n   jmf-2.1.1e.jar\n   firmata4j-2.3.4.1.jar\n   jfreechart-1.0.12.jar\n   xmlrpc-client-3.1.3.jar\n   rsyntaxtextarea-2.6.0.jar\n   batik-svggen-1.7.jar\n   svg-dom-java-1.1.jar\n   \n   Medusa-7.1.jar\n   xmlrpc-common-3.1.3.jar\n   ws-commons-util-1.0.2.jar\n   junit-4.10.jar\n   protobuf-java-3.8.0.jar\n   wellbehavedfx-0.1.1.jar\n   easybind-1.0.4-SNAPSHOT.jar\n   javax.annotation-api-1.2.jar\n   commons-discovery-0.4.jar\n   commons-beanutils-1.8.0.jar\n   localizer-1.7.jar\n   tiger-types-1.3.jar\n   guava-14.0.1.jar\n   asm5-5.0.1.jar\n   commons-fileupload-1.2.1.jar\n   jsr305-2.0.1.jar\n   jzlib-1.1.3.jar\n   jsch-0.1.55.jar\n   JavaEWAH-1.1.6.jar\n   jtok-core-1.9.3.jar\n   slf4j-api-1.7.7.jar\n   bcpg-jdk15on-1.64.jar\n   bcpkix-jdk15on-1.64.jar\n   bcprov-jdk15on-1.64.jar\n   okhttp-2.0.0.jar\n   bridge-method-annotation-1.14.jar\n   asm-debug-all-4.2.jar\n   maven-plugin-api-2.0.1.jar\n   miglayout-core-4.2.jar\n   jetty-http-9.4.36.v20210114.jar\n   jetty-io-9.4.36.v20210114.jar\n   jetty-util-ajax-9.4.36.v20210114.jar\n   jetty-continuation-9.4.36.v20210114.jar\n   jetty-xml-9.4.36.v20210114.jar\n   jetty-util-9.4.36.v20210114.jar\n   usb4java-javax-1.2.0.jar\n   httpcore-nio-4.1.jar\n   httpcore-4.4.3.jar\n   commons-logging-1.2.jar\n   jssc-2.8.0.jar\n   jcommon-1.0.15.jar\n   batik-awt-util-1.7.jar\n   batik-util-1.7.jar\n   xml-apis-1.3.04.jar\n   smil-boston-dom-java-2000-02-25.jar\n   smack-3.2.1.jar\n   smackx-3.2.1.jar\n   usb4java-1.2.0.jar\n   jinput-2.0.6-ihmc2.jar\n   ihmc-native-library-loader-1.3.1.jar\n   commons-lang3-3.11.jar\n   nrjavaserial-5.1.1.jar\n   commons-math3-3.6.1.jar\n   mujoco-java-2.2.0-pre.5.jar\n   json-simple-1.1.jar\n   vvecmath-0.3.2.jar\n   hamcrest-core-1.1.jar\n   commons-collections-3.2.2.jar\n   okio-1.0.0.jar\n   annotation-indexer-1.4.jar\n   jackson-databind-2.10.1.jar\n   usb-api-1.0.2.jar\n   libusb4java-1.2.0-linux-x86.jar\n   libusb4java-1.2.0-linux-x86_64.jar\n   libusb4java-1.2.0-linux-arm.jar\n   libusb4java-1.2.0-windows-x86.jar\n   libusb4java-1.2.0-windows-x86_64.jar\n   libusb4java-1.2.0-osx-x86.jar\n   libusb4java-1.2.0-osx-x86_64.jar\n   commons-net-3.3.jar\n   jutils-1.0.0.jar\n   jinput-platform-2.0.6-natives-windows.jar\n   jinput-platform-2.0.6-natives-osx.jar\n   jinput-platform-2.0.6-natives-linux.jar\n   jakarta.xml.bind-api-2.3.2.jar\n   javacpp-1.5.7.jar\n   icu4j-54.1.1.jar\n   emotionml-checker-java-1.1.jar\n   trove4j-2.0.2.jar\n   opennlp-tools-1.5.3.jar\n   opennlp-maxent-3.0.3.jar\n   hsqldb-2.0.0.jar\n   log4j-1.2.16.jar\n   fast-md5-2.7.1.jar\n   groovy-all-2.4.5.jar\n   Jampack-1.0.jar\n   swing-layout-1.0.3.jar\n   jackson-annotations-2.10.1.jar\n   jackson-core-2.10.1.jar\n   jakarta.activation-api-1.2.1.jar\n   jwnl-1.3.3.jar\nbowler-script-kernel:\n-compile:\n   bowler-kernel.jar\n   mbrola.jar\n   rosjava-0.1.6.jar\n   jsapi.jar\n   jl1.0.jar\n   jbullet-2.72.2.4.jar\n   nrV4J-3.8.1.jar\n   asm-all-3.1.jar\n   jbullet-2.72.2.4-sources.jar\n   stack-alloc.jar\n   GithubPasswordManager-0.6.1.jar\n   tink-1.3.0-rc1.jar\n   json-20180813.jar\n   nrsdk-3.33.0-jar-with-dependencies.jar\n   bluecove-gpl-2.1.1.jar\n   motej.0.9.jar\n   bluecove-2.1.1.jar\n   voice-dfki-poppy-hsmm-5.2.jar\n   voice-dfki-prudence-hsmm-5.2.jar\n   voice-dfki-spike-hsmm-5.2.jar\n   voice-cmu-slt-hsmm-5.2.jar\n   marytts-lang-en-5.2.jar\n   marytts-runtime-5.2.jar\n   marytts-signalproc-5.2.jar\n   jama-1.0.3.jar\n   richtextfx-0.6.jar\n   flowless-0.4.jar\n   undofx-2.1.1.jar\n   reactfx-2.0-M5.jar\n   groovy-2.3.7.jar\n   ivy-2.2.0.jar\n   controlsfx-8.0.6.jar\n   stapler-1.237.jar\n   json-lib-2.4-jenkins-2.jar\n   marytts-common-5.2.jar\n   ezmorph-1.0.6.jar\n   commons-lang-2.6.jar\n   httpclient-4.5.1.jar\n   github-api-1.101.jar\n   commons-codec-1.13.jar\n   org.eclipse.jgit-5.6.0.201912101111-r.jar\n   okhttp-urlconnection-2.0.0.jar\n   wordnet-random-name-1.2.jar\n   mockito-all-1.9.5.jar\n   bridge-method-injector-1.14.jar\n   miglayout-swing-4.2.jar\n   JavaCad-0.27.0.jar\n   pdf-transcoder.jar\n   xml-apis-ext.jar\n   kabeja-svg-0.4.jar\n   miethxml-toolkit.jar\n   batik-all.jar\n   js.jar\n   xml-apis.jar\n   kabeja-0.4.jar\n   kabeja-xslt.jar\n   miethxml-ui.jar\n   commons-io-2.6.jar\n   jython-2.5.3.jar\n   jython-standalone-2.5.2.jar\n   clojure-1.8.0.jar\n   jetty-webapp-9.4.36.v20210114.jar\n   jetty-servlet-9.4.36.v20210114.jar\n   jetty-security-9.4.36.v20210114.jar\n   jetty-server-9.4.36.v20210114.jar\n   jetty-servlets-9.4.36.v20210114.jar\n   javax.servlet-api-3.1.0.jar\n   vecmath-1.3.1.jar\n   slf4j-simple-1.6.1.jar\n   CHDK-PTP-Java-0.5.3-SNAPSHOT.jar\n   WalnutiQ-2.3.3.jar\n   jxl-2.4.2.jar\n   de.huxhorn.sulky.3rdparty.jlayer-1.0.jar\n   gson-2.5.jar\n   jsoup-1.8.3.jar\n   jmf-2.1.1e.jar\n   firmata4j-2.3.4.1.jar\n   jfreechart-1.0.12.jar\n   xmlrpc-client-3.1.3.jar\n   rsyntaxtextarea-2.6.0.jar\n   batik-svggen-1.7.jar\n   svg-dom-java-1.1.jar\n   \n   Medusa-7.1.jar\n   protobuf-java-3.8.0.jar\n   wellbehavedfx-0.1.1.jar\n   easybind-1.0.4-SNAPSHOT.jar\n   javax.annotation-api-1.2.jar\n   commons-discovery-0.4.jar\n   commons-beanutils-1.8.0.jar\n   localizer-1.7.jar\n   tiger-types-1.3.jar\n   guava-14.0.1.jar\n   asm5-5.0.1.jar\n   commons-fileupload-1.2.1.jar\n   jsr305-2.0.1.jar\n   jzlib-1.1.3.jar\n   jsch-0.1.55.jar\n   JavaEWAH-1.1.6.jar\n   jtok-core-1.9.3.jar\n   slf4j-api-1.7.7.jar\n   bcpg-jdk15on-1.64.jar\n   bcpkix-jdk15on-1.64.jar\n   bcprov-jdk15on-1.64.jar\n   okhttp-2.0.0.jar\n   bridge-method-annotation-1.14.jar\n   asm-debug-all-4.2.jar\n   maven-plugin-api-2.0.1.jar\n   miglayout-core-4.2.jar\n   jetty-http-9.4.36.v20210114.jar\n   jetty-io-9.4.36.v20210114.jar\n   jetty-util-ajax-9.4.36.v20210114.jar\n   jetty-continuation-9.4.36.v20210114.jar\n   jetty-xml-9.4.36.v20210114.jar\n   jetty-util-9.4.36.v20210114.jar\n   usb4java-javax-1.2.0.jar\n   httpcore-nio-4.1.jar\n   httpcore-4.4.3.jar\n   commons-logging-1.2.jar\n   jssc-2.8.0.jar\n   jcommon-1.0.15.jar\n   xmlrpc-common-3.1.3.jar\n   batik-awt-util-1.7.jar\n   batik-util-1.7.jar\n   ws-commons-util-1.0.2.jar\n   xml-apis-1.3.04.jar\n   smil-boston-dom-java-2000-02-25.jar\n   smack-3.2.1.jar\n   smackx-3.2.1.jar\n   usb4java-1.2.0.jar\n   jinput-2.0.6-ihmc2.jar\n   ihmc-native-library-loader-1.3.1.jar\n   commons-lang3-3.11.jar\n   nrjavaserial-5.1.1.jar\n   commons-math3-3.6.1.jar\n   mujoco-java-2.2.0-pre.5.jar\n   junit-4.0.jar\n   json-simple-1.1.jar\n   vvecmath-0.3.2.jar\n   commons-collections-3.2.2.jar\n   okio-1.0.0.jar\n   annotation-indexer-1.4.jar\n   jackson-databind-2.10.1.jar\n   usb-api-1.0.2.jar\n   libusb4java-1.2.0-linux-x86.jar\n   libusb4java-1.2.0-linux-x86_64.jar\n   libusb4java-1.2.0-linux-arm.jar\n   libusb4java-1.2.0-windows-x86.jar\n   libusb4java-1.2.0-windows-x86_64.jar\n   libusb4java-1.2.0-osx-x86.jar\n   libusb4java-1.2.0-osx-x86_64.jar\n   commons-net-3.3.jar\n   jutils-1.0.0.jar\n   jinput-platform-2.0.6-natives-windows.jar\n   jinput-platform-2.0.6-natives-osx.jar\n   jinput-platform-2.0.6-natives-linux.jar\n   jakarta.xml.bind-api-2.3.2.jar\n   javacpp-1.5.7.jar\n   icu4j-54.1.1.jar\n   emotionml-checker-java-1.1.jar\n   trove4j-2.0.2.jar\n   opennlp-tools-1.5.3.jar\n   opennlp-maxent-3.0.3.jar\n   hsqldb-2.0.0.jar\n   log4j-1.2.16.jar\n   fast-md5-2.7.1.jar\n   groovy-all-2.4.5.jar\n   Jampack-1.0.jar\n   swing-layout-1.0.3.jar\n   jackson-annotations-2.10.1.jar\n   jackson-core-2.10.1.jar\n   jakarta.activation-api-1.2.1.jar\n   jwnl-1.3.3.jar\n-testCompile:\n   bowler-kernel.jar\n   mbrola.jar\n   rosjava-0.1.6.jar\n   jsapi.jar\n   jl1.0.jar\n   jbullet-2.72.2.4.jar\n   nrV4J-3.8.1.jar\n   asm-all-3.1.jar\n   jbullet-2.72.2.4-sources.jar\n   stack-alloc.jar\n   GithubPasswordManager-0.6.1.jar\n   tink-1.3.0-rc1.jar\n   json-20180813.jar\n   nrsdk-3.33.0-jar-with-dependencies.jar\n   bluecove-gpl-2.1.1.jar\n   motej.0.9.jar\n   bluecove-2.1.1.jar\n   voice-dfki-poppy-hsmm-5.2.jar\n   voice-dfki-prudence-hsmm-5.2.jar\n   voice-dfki-spike-hsmm-5.2.jar\n   voice-cmu-slt-hsmm-5.2.jar\n   marytts-lang-en-5.2.jar\n   marytts-runtime-5.2.jar\n   marytts-signalproc-5.2.jar\n   jama-1.0.3.jar\n   richtextfx-0.6.jar\n   flowless-0.4.jar\n   undofx-2.1.1.jar\n   reactfx-2.0-M5.jar\n   groovy-2.3.7.jar\n   ivy-2.2.0.jar\n   controlsfx-8.0.6.jar\n   stapler-1.237.jar\n   json-lib-2.4-jenkins-2.jar\n   marytts-common-5.2.jar\n   ezmorph-1.0.6.jar\n   commons-lang-2.6.jar\n   httpclient-4.5.1.jar\n   github-api-1.101.jar\n   commons-codec-1.13.jar\n   org.eclipse.jgit-5.6.0.201912101111-r.jar\n   okhttp-urlconnection-2.0.0.jar\n   wordnet-random-name-1.2.jar\n   mockito-all-1.9.5.jar\n   bridge-method-injector-1.14.jar\n   miglayout-swing-4.2.jar\n   JavaCad-0.27.0.jar\n   pdf-transcoder.jar\n   xml-apis-ext.jar\n   kabeja-svg-0.4.jar\n   miethxml-toolkit.jar\n   batik-all.jar\n   js.jar\n   xml-apis.jar\n   kabeja-0.4.jar\n   kabeja-xslt.jar\n   miethxml-ui.jar\n   commons-io-2.6.jar\n   jython-2.5.3.jar\n   jython-standalone-2.5.2.jar\n   clojure-1.8.0.jar\n   jetty-webapp-9.4.36.v20210114.jar\n   jetty-servlet-9.4.36.v20210114.jar\n   jetty-security-9.4.36.v20210114.jar\n   jetty-server-9.4.36.v20210114.jar\n   jetty-servlets-9.4.36.v20210114.jar\n   javax.servlet-api-3.1.0.jar\n   vecmath-1.3.1.jar\n   slf4j-simple-1.6.1.jar\n   CHDK-PTP-Java-0.5.3-SNAPSHOT.jar\n   WalnutiQ-2.3.3.jar\n   jxl-2.4.2.jar\n   de.huxhorn.sulky.3rdparty.jlayer-1.0.jar\n   gson-2.5.jar\n   jsoup-1.8.3.jar\n   jmf-2.1.1e.jar\n   firmata4j-2.3.4.1.jar\n   jfreechart-1.0.12.jar\n   xmlrpc-client-3.1.3.jar\n   rsyntaxtextarea-2.6.0.jar\n   batik-svggen-1.7.jar\n   svg-dom-java-1.1.jar\n   \n   Medusa-7.1.jar\n   xmlrpc-common-3.1.3.jar\n   ws-commons-util-1.0.2.jar\n   junit-4.10.jar\n   protobuf-java-3.8.0.jar\n   wellbehavedfx-0.1.1.jar\n   easybind-1.0.4-SNAPSHOT.jar\n   javax.annotation-api-1.2.jar\n   commons-discovery-0.4.jar\n   commons-beanutils-1.8.0.jar\n   localizer-1.7.jar\n   tiger-types-1.3.jar\n   guava-14.0.1.jar\n   asm5-5.0.1.jar\n   commons-fileupload-1.2.1.jar\n   jsr305-2.0.1.jar\n   jzlib-1.1.3.jar\n   jsch-0.1.55.jar\n   JavaEWAH-1.1.6.jar\n   jtok-core-1.9.3.jar\n   slf4j-api-1.7.7.jar\n   bcpg-jdk15on-1.64.jar\n   bcpkix-jdk15on-1.64.jar\n   bcprov-jdk15on-1.64.jar\n   okhttp-2.0.0.jar\n   bridge-method-annotation-1.14.jar\n   asm-debug-all-4.2.jar\n   maven-plugin-api-2.0.1.jar\n   miglayout-core-4.2.jar\n   jetty-http-9.4.36.v20210114.jar\n   jetty-io-9.4.36.v20210114.jar\n   jetty-util-ajax-9.4.36.v20210114.jar\n   jetty-continuation-9.4.36.v20210114.jar\n   jetty-xml-9.4.36.v20210114.jar\n   jetty-util-9.4.36.v20210114.jar\n   usb4java-javax-1.2.0.jar\n   httpcore-nio-4.1.jar\n   httpcore-4.4.3.jar\n   commons-logging-1.2.jar\n   jssc-2.8.0.jar\n   jcommon-1.0.15.jar\n   batik-awt-util-1.7.jar\n   batik-util-1.7.jar\n   xml-apis-1.3.04.jar\n   smil-boston-dom-java-2000-02-25.jar\n   smack-3.2.1.jar\n   smackx-3.2.1.jar\n   usb4java-1.2.0.jar\n   jinput-2.0.6-ihmc2.jar\n   ihmc-native-library-loader-1.3.1.jar\n   commons-lang3-3.11.jar\n   nrjavaserial-5.1.1.jar\n   commons-math3-3.6.1.jar\n   mujoco-java-2.2.0-pre.5.jar\n   json-simple-1.1.jar\n   vvecmath-0.3.2.jar\n   hamcrest-core-1.1.jar\n   commons-collections-3.2.2.jar\n   okio-1.0.0.jar\n   annotation-indexer-1.4.jar\n   jackson-databind-2.10.1.jar\n   usb-api-1.0.2.jar\n   libusb4java-1.2.0-linux-x86.jar\n   libusb4java-1.2.0-linux-x86_64.jar\n   libusb4java-1.2.0-linux-arm.jar\n   libusb4java-1.2.0-windows-x86.jar\n   libusb4java-1.2.0-windows-x86_64.jar\n   libusb4java-1.2.0-osx-x86.jar\n   libusb4java-1.2.0-osx-x86_64.jar\n   commons-net-3.3.jar\n   jutils-1.0.0.jar\n   jinput-platform-2.0.6-natives-windows.jar\n   jinput-platform-2.0.6-natives-osx.jar\n   jinput-platform-2.0.6-natives-linux.jar\n   jakarta.xml.bind-api-2.3.2.jar\n   javacpp-1.5.7.jar\n   icu4j-54.1.1.jar\n   emotionml-checker-java-1.1.jar\n   trove4j-2.0.2.jar\n   opennlp-tools-1.5.3.jar\n   opennlp-maxent-3.0.3.jar\n   hsqldb-2.0.0.jar\n   log4j-1.2.16.jar\n   fast-md5-2.7.1.jar\n   groovy-all-2.4.5.jar\n   Jampack-1.0.jar\n   swing-layout-1.0.3.jar\n   jackson-annotations-2.10.1.jar\n   jackson-core-2.10.1.jar\n   jakarta.activation-api-1.2.1.jar\n   jwnl-1.3.3.jar\n\n-compile:\n   bowler-kernel.jar\n   mbrola.jar\n   rosjava-0.1.6.jar\n   jsapi.jar\n   jl1.0.jar\n   jbullet-2.72.2.4.jar\n   nrV4J-3.8.1.jar\n   asm-all-3.1.jar\n   jbullet-2.72.2.4-sources.jar\n   stack-alloc.jar\n   GithubPasswordManager-0.6.1.jar\n   tink-1.3.0-rc1.jar\n   json-20180813.jar\n   nrsdk-3.33.0-jar-with-dependencies.jar\n   bluecove-gpl-2.1.1.jar\n   motej.0.9.jar\n   bluecove-2.1.1.jar\n   voice-dfki-poppy-hsmm-5.2.jar\n   voice-dfki-prudence-hsmm-5.2.jar\n   voice-dfki-spike-hsmm-5.2.jar\n   voice-cmu-slt-hsmm-5.2.jar\n   marytts-lang-en-5.2.jar\n   marytts-runtime-5.2.jar\n   marytts-signalproc-5.2.jar\n   jama-1.0.3.jar\n   richtextfx-0.6.jar\n   flowless-0.4.jar\n   undofx-2.1.1.jar\n   reactfx-2.0-M5.jar\n   groovy-2.3.7.jar\n   ivy-2.2.0.jar\n   controlsfx-8.0.6.jar\n   stapler-1.237.jar\n   json-lib-2.4-jenkins-2.jar\n   marytts-common-5.2.jar\n   ezmorph-1.0.6.jar\n   commons-lang-2.6.jar\n   httpclient-4.5.1.jar\n   github-api-1.101.jar\n   commons-codec-1.13.jar\n   org.eclipse.jgit-5.6.0.201912101111-r.jar\n   okhttp-urlconnection-2.0.0.jar\n   wordnet-random-name-1.2.jar\n   mockito-all-1.9.5.jar\n   bridge-method-injector-1.14.jar\n   miglayout-swing-4.2.jar\n   JavaCad-0.27.0.jar\n   pdf-transcoder.jar\n   xml-apis-ext.jar\n   kabeja-svg-0.4.jar\n   miethxml-toolkit.jar\n   batik-all.jar\n   js.jar\n   xml-apis.jar\n   kabeja-0.4.jar\n   kabeja-xslt.jar\n   miethxml-ui.jar\n   commons-io-2.6.jar\n   jython-2.5.3.jar\n   jython-standalone-2.5.2.jar\n   clojure-1.8.0.jar\n   jetty-webapp-9.4.36.v20210114.jar\n   jetty-servlet-9.4.36.v20210114.jar\n   jetty-security-9.4.36.v20210114.jar\n   jetty-server-9.4.36.v20210114.jar\n   jetty-servlets-9.4.36.v20210114.jar\n   javax.servlet-api-3.1.0.jar\n   vecmath-1.3.1.jar\n   slf4j-simple-1.6.1.jar\n   CHDK-PTP-Java-0.5.3-SNAPSHOT.jar\n   WalnutiQ-2.3.3.jar\n   jxl-2.4.2.jar\n   de.huxhorn.sulky.3rdparty.jlayer-1.0.jar\n   gson-2.5.jar\n   jsoup-1.8.3.jar\n   jmf-2.1.1e.jar\n   firmata4j-2.3.4.1.jar\n   jfreechart-1.0.12.jar\n   xmlrpc-client-3.1.3.jar\n   rsyntaxtextarea-2.6.0.jar\n   batik-svggen-1.7.jar\n   svg-dom-java-1.1.jar\n   \n   Medusa-7.1.jar\n   protobuf-java-3.8.0.jar\n   wellbehavedfx-0.1.1.jar\n   easybind-1.0.4-SNAPSHOT.jar\n   javax.annotation-api-1.2.jar\n   commons-discovery-0.4.jar\n   commons-beanutils-1.8.0.jar\n   localizer-1.7.jar\n   tiger-types-1.3.jar\n   guava-14.0.1.jar\n   asm5-5.0.1.jar\n   commons-fileupload-1.2.1.jar\n   jsr305-2.0.1.jar\n   jzlib-1.1.3.jar\n   jsch-0.1.55.jar\n   JavaEWAH-1.1.6.jar\n   jtok-core-1.9.3.jar\n   slf4j-api-1.7.7.jar\n   bcpg-jdk15on-1.64.jar\n   bcpkix-jdk15on-1.64.jar\n   bcprov-jdk15on-1.64.jar\n   okhttp-2.0.0.jar\n   bridge-method-annotation-1.14.jar\n   asm-debug-all-4.2.jar\n   maven-plugin-api-2.0.1.jar\n   miglayout-core-4.2.jar\n   jetty-http-9.4.36.v20210114.jar\n   jetty-io-9.4.36.v20210114.jar\n   jetty-util-ajax-9.4.36.v20210114.jar\n   jetty-continuation-9.4.36.v20210114.jar\n   jetty-xml-9.4.36.v20210114.jar\n   jetty-util-9.4.36.v20210114.jar\n   usb4java-javax-1.2.0.jar\n   httpcore-nio-4.1.jar\n   httpcore-4.4.3.jar\n   commons-logging-1.2.jar\n   jssc-2.8.0.jar\n   jcommon-1.0.15.jar\n   xmlrpc-common-3.1.3.jar\n   batik-awt-util-1.7.jar\n   batik-util-1.7.jar\n   ws-commons-util-1.0.2.jar\n   xml-apis-1.3.04.jar\n   smil-boston-dom-java-2000-02-25.jar\n   smack-3.2.1.jar\n   smackx-3.2.1.jar\n   usb4java-1.2.0.jar\n   jinput-2.0.6-ihmc2.jar\n   ihmc-native-library-loader-1.3.1.jar\n   commons-lang3-3.11.jar\n   nrjavaserial-5.1.1.jar\n   commons-math3-3.6.1.jar\n   mujoco-java-2.2.0-pre.5.jar\n   junit-4.0.jar\n   json-simple-1.1.jar\n   vvecmath-0.3.2.jar\n   commons-collections-3.2.2.jar\n   okio-1.0.0.jar\n   annotation-indexer-1.4.jar\n   jackson-databind-2.10.1.jar\n   usb-api-1.0.2.jar\n   libusb4java-1.2.0-linux-x86.jar\n   libusb4java-1.2.0-linux-x86_64.jar\n   libusb4java-1.2.0-linux-arm.jar\n   libusb4java-1.2.0-windows-x86.jar\n   libusb4java-1.2.0-windows-x86_64.jar\n   libusb4java-1.2.0-osx-x86.jar\n   libusb4java-1.2.0-osx-x86_64.jar\n   commons-net-3.3.jar\n   jutils-1.0.0.jar\n   jinput-platform-2.0.6-natives-windows.jar\n   jinput-platform-2.0.6-natives-osx.jar\n   jinput-platform-2.0.6-natives-linux.jar\n   jakarta.xml.bind-api-2.3.2.jar\n   javacpp-1.5.7.jar\n   icu4j-54.1.1.jar\n   emotionml-checker-java-1.1.jar\n   trove4j-2.0.2.jar\n   opennlp-tools-1.5.3.jar\n   opennlp-maxent-3.0.3.jar\n   hsqldb-2.0.0.jar\n   log4j-1.2.16.jar\n   fast-md5-2.7.1.jar\n   groovy-all-2.4.5.jar\n   Jampack-1.0.jar\n   swing-layout-1.0.3.jar\n   jackson-annotations-2.10.1.jar\n   jackson-core-2.10.1.jar\n   jakarta.activation-api-1.2.1.jar\n   jwnl-1.3.3.jar\n-testCompile:\n   bowler-kernel.jar\n   mbrola.jar\n   rosjava-0.1.6.jar\n   jsapi.jar\n   jl1.0.jar\n   jbullet-2.72.2.4.jar\n   nrV4J-3.8.1.jar\n   asm-all-3.1.jar\n   jbullet-2.72.2.4-sources.jar\n   stack-alloc.jar\n   GithubPasswordManager-0.6.1.jar\n   tink-1.3.0-rc1.jar\n   json-20180813.jar\n   nrsdk-3.33.0-jar-with-dependencies.jar\n   bluecove-gpl-2.1.1.jar\n   motej.0.9.jar\n   bluecove-2.1.1.jar\n   voice-dfki-poppy-hsmm-5.2.jar\n   voice-dfki-prudence-hsmm-5.2.jar\n   voice-dfki-spike-hsmm-5.2.jar\n   voice-cmu-slt-hsmm-5.2.jar\n   marytts-lang-en-5.2.jar\n   marytts-runtime-5.2.jar\n   marytts-signalproc-5.2.jar\n   jama-1.0.3.jar\n   richtextfx-0.6.jar\n   flowless-0.4.jar\n   undofx-2.1.1.jar\n   reactfx-2.0-M5.jar\n   groovy-2.3.7.jar\n   ivy-2.2.0.jar\n   controlsfx-8.0.6.jar\n   stapler-1.237.jar\n   json-lib-2.4-jenkins-2.jar\n   marytts-common-5.2.jar\n   ezmorph-1.0.6.jar\n   commons-lang-2.6.jar\n   httpclient-4.5.1.jar\n   github-api-1.101.jar\n   commons-codec-1.13.jar\n   org.eclipse.jgit-5.6.0.201912101111-r.jar\n   okhttp-urlconnection-2.0.0.jar\n   wordnet-random-name-1.2.jar\n   mockito-all-1.9.5.jar\n   bridge-method-injector-1.14.jar\n   miglayout-swing-4.2.jar\n   JavaCad-0.27.0.jar\n   pdf-transcoder.jar\n   xml-apis-ext.jar\n   kabeja-svg-0.4.jar\n   miethxml-toolkit.jar\n   batik-all.jar\n   js.jar\n   xml-apis.jar\n   kabeja-0.4.jar\n   kabeja-xslt.jar\n   miethxml-ui.jar\n   commons-io-2.6.jar\n   jython-2.5.3.jar\n   jython-standalone-2.5.2.jar\n   clojure-1.8.0.jar\n   jetty-webapp-9.4.36.v20210114.jar\n   jetty-servlet-9.4.36.v20210114.jar\n   jetty-security-9.4.36.v20210114.jar\n   jetty-server-9.4.36.v20210114.jar\n   jetty-servlets-9.4.36.v20210114.jar\n   javax.servlet-api-3.1.0.jar\n   vecmath-1.3.1.jar\n   slf4j-simple-1.6.1.jar\n   CHDK-PTP-Java-0.5.3-SNAPSHOT.jar\n   WalnutiQ-2.3.3.jar\n   jxl-2.4.2.jar\n   de.huxhorn.sulky.3rdparty.jlayer-1.0.jar\n   gson-2.5.jar\n   jsoup-1.8.3.jar\n   jmf-2.1.1e.jar\n   firmata4j-2.3.4.1.jar\n   jfreechart-1.0.12.jar\n   xmlrpc-client-3.1.3.jar\n   rsyntaxtextarea-2.6.0.jar\n   batik-svggen-1.7.jar\n   svg-dom-java-1.1.jar\n   \n   Medusa-7.1.jar\n   xmlrpc-common-3.1.3.jar\n   ws-commons-util-1.0.2.jar\n   junit-4.10.jar\n   protobuf-java-3.8.0.jar\n   wellbehavedfx-0.1.1.jar\n   easybind-1.0.4-SNAPSHOT.jar\n   javax.annotation-api-1.2.jar\n   commons-discovery-0.4.jar\n   commons-beanutils-1.8.0.jar\n   localizer-1.7.jar\n   tiger-types-1.3.jar\n   guava-14.0.1.jar\n   asm5-5.0.1.jar\n   commons-fileupload-1.2.1.jar\n   jsr305-2.0.1.jar\n   jzlib-1.1.3.jar\n   jsch-0.1.55.jar\n   JavaEWAH-1.1.6.jar\n   jtok-core-1.9.3.jar\n   slf4j-api-1.7.7.jar\n   bcpg-jdk15on-1.64.jar\n   bcpkix-jdk15on-1.64.jar\n   bcprov-jdk15on-1.64.jar\n   okhttp-2.0.0.jar\n   bridge-method-annotation-1.14.jar\n   asm-debug-all-4.2.jar\n   maven-plugin-api-2.0.1.jar\n   miglayout-core-4.2.jar\n   jetty-http-9.4.36.v20210114.jar\n   jetty-io-9.4.36.v20210114.jar\n   jetty-util-ajax-9.4.36.v20210114.jar\n   jetty-continuation-9.4.36.v20210114.jar\n   jetty-xml-9.4.36.v20210114.jar\n   jetty-util-9.4.36.v20210114.jar\n   usb4java-javax-1.2.0.jar\n   httpcore-nio-4.1.jar\n   httpcore-4.4.3.jar\n   commons-logging-1.2.jar\n   jssc-2.8.0.jar\n   jcommon-1.0.15.jar\n   batik-awt-util-1.7.jar\n   batik-util-1.7.jar\n   xml-apis-1.3.04.jar\n   smil-boston-dom-java-2000-02-25.jar\n   smack-3.2.1.jar\n   smackx-3.2.1.jar\n   usb4java-1.2.0.jar\n   jinput-2.0.6-ihmc2.jar\n   ihmc-native-library-loader-1.3.1.jar\n   commons-lang3-3.11.jar\n   nrjavaserial-5.1.1.jar\n   commons-math3-3.6.1.jar\n   mujoco-java-2.2.0-pre.5.jar\n   json-simple-1.1.jar\n   vvecmath-0.3.2.jar\n   hamcrest-core-1.1.jar\n   commons-collections-3.2.2.jar\n   okio-1.0.0.jar\n   annotation-indexer-1.4.jar\n   jackson-databind-2.10.1.jar\n   usb-api-1.0.2.jar\n   libusb4java-1.2.0-linux-x86.jar\n   libusb4java-1.2.0-linux-x86_64.jar\n   libusb4java-1.2.0-linux-arm.jar\n   libusb4java-1.2.0-windows-x86.jar\n   libusb4java-1.2.0-windows-x86_64.jar\n   libusb4java-1.2.0-osx-x86.jar\n   libusb4java-1.2.0-osx-x86_64.jar\n   commons-net-3.3.jar\n   jutils-1.0.0.jar\n   jinput-platform-2.0.6-natives-windows.jar\n   jinput-platform-2.0.6-natives-osx.jar\n   jinput-platform-2.0.6-natives-linux.jar\n   jakarta.xml.bind-api-2.3.2.jar\n   javacpp-1.5.7.jar\n   icu4j-54.1.1.jar\n   emotionml-checker-java-1.1.jar\n   trove4j-2.0.2.jar\n   opennlp-tools-1.5.3.jar\n   opennlp-maxent-3.0.3.jar\n   hsqldb-2.0.0.jar\n   log4j-1.2.16.jar\n   fast-md5-2.7.1.jar\n   groovy-all-2.4.5.jar\n   Jampack-1.0.jar\n   swing-layout-1.0.3.jar\n   jackson-annotations-2.10.1.jar\n   jackson-core-2.10.1.jar\n   jakarta.activation-api-1.2.1.jar\n   jwnl-1.3.3.jar\njava-bowler:\n-compile:\n   bowler-kernel.jar\n   mbrola.jar\n   rosjava-0.1.6.jar\n   jsapi.jar\n   jl1.0.jar\n   jbullet-2.72.2.4.jar\n   nrV4J-3.8.1.jar\n   asm-all-3.1.jar\n   jbullet-2.72.2.4-sources.jar\n   stack-alloc.jar\n   GithubPasswordManager-0.6.1.jar\n   tink-1.3.0-rc1.jar\n   json-20180813.jar\n   nrsdk-3.33.0-jar-with-dependencies.jar\n   bluecove-gpl-2.1.1.jar\n   motej.0.9.jar\n   bluecove-2.1.1.jar\n   voice-dfki-poppy-hsmm-5.2.jar\n   voice-dfki-prudence-hsmm-5.2.jar\n   voice-dfki-spike-hsmm-5.2.jar\n   voice-cmu-slt-hsmm-5.2.jar\n   marytts-lang-en-5.2.jar\n   marytts-runtime-5.2.jar\n   marytts-signalproc-5.2.jar\n   jama-1.0.3.jar\n   richtextfx-0.6.jar\n   flowless-0.4.jar\n   undofx-2.1.1.jar\n   reactfx-2.0-M5.jar\n   groovy-2.3.7.jar\n   ivy-2.2.0.jar\n   controlsfx-8.0.6.jar\n   stapler-1.237.jar\n   json-lib-2.4-jenkins-2.jar\n   marytts-common-5.2.jar\n   ezmorph-1.0.6.jar\n   commons-lang-2.6.jar\n   httpclient-4.5.1.jar\n   github-api-1.101.jar\n   commons-codec-1.13.jar\n   org.eclipse.jgit-5.6.0.201912101111-r.jar\n   okhttp-urlconnection-2.0.0.jar\n   wordnet-random-name-1.2.jar\n   mockito-all-1.9.5.jar\n   bridge-method-injector-1.14.jar\n   miglayout-swing-4.2.jar\n   JavaCad-0.27.0.jar\n   pdf-transcoder.jar\n   xml-apis-ext.jar\n   kabeja-svg-0.4.jar\n   miethxml-toolkit.jar\n   batik-all.jar\n   js.jar\n   xml-apis.jar\n   kabeja-0.4.jar\n   kabeja-xslt.jar\n   miethxml-ui.jar\n   commons-io-2.6.jar\n   jython-2.5.3.jar\n   jython-standalone-2.5.2.jar\n   clojure-1.8.0.jar\n   jetty-webapp-9.4.36.v20210114.jar\n   jetty-servlet-9.4.36.v20210114.jar\n   jetty-security-9.4.36.v20210114.jar\n   jetty-server-9.4.36.v20210114.jar\n   jetty-servlets-9.4.36.v20210114.jar\n   javax.servlet-api-3.1.0.jar\n   vecmath-1.3.1.jar\n   slf4j-simple-1.6.1.jar\n   CHDK-PTP-Java-0.5.3-SNAPSHOT.jar\n   WalnutiQ-2.3.3.jar\n   jxl-2.4.2.jar\n   de.huxhorn.sulky.3rdparty.jlayer-1.0.jar\n   gson-2.5.jar\n   jsoup-1.8.3.jar\n   jmf-2.1.1e.jar\n   firmata4j-2.3.4.1.jar\n   jfreechart-1.0.12.jar\n   xmlrpc-client-3.1.3.jar\n   rsyntaxtextarea-2.6.0.jar\n   batik-svggen-1.7.jar\n   svg-dom-java-1.1.jar\n   \n   Medusa-7.1.jar\n   protobuf-java-3.8.0.jar\n   wellbehavedfx-0.1.1.jar\n   easybind-1.0.4-SNAPSHOT.jar\n   javax.annotation-api-1.2.jar\n   commons-discovery-0.4.jar\n   commons-beanutils-1.8.0.jar\n   localizer-1.7.jar\n   tiger-types-1.3.jar\n   guava-14.0.1.jar\n   asm5-5.0.1.jar\n   commons-fileupload-1.2.1.jar\n   jsr305-2.0.1.jar\n   jzlib-1.1.3.jar\n   jsch-0.1.55.jar\n   JavaEWAH-1.1.6.jar\n   jtok-core-1.9.3.jar\n   slf4j-api-1.7.7.jar\n   bcpg-jdk15on-1.64.jar\n   bcpkix-jdk15on-1.64.jar\n   bcprov-jdk15on-1.64.jar\n   okhttp-2.0.0.jar\n   bridge-method-annotation-1.14.jar\n   asm-debug-all-4.2.jar\n   maven-plugin-api-2.0.1.jar\n   miglayout-core-4.2.jar\n   jetty-http-9.4.36.v20210114.jar\n   jetty-io-9.4.36.v20210114.jar\n   jetty-util-ajax-9.4.36.v20210114.jar\n   jetty-continuation-9.4.36.v20210114.jar\n   jetty-xml-9.4.36.v20210114.jar\n   jetty-util-9.4.36.v20210114.jar\n   usb4java-javax-1.2.0.jar\n   httpcore-nio-4.1.jar\n   httpcore-4.4.3.jar\n   commons-logging-1.2.jar\n   jssc-2.8.0.jar\n   jcommon-1.0.15.jar\n   xmlrpc-common-3.1.3.jar\n   batik-awt-util-1.7.jar\n   batik-util-1.7.jar\n   ws-commons-util-1.0.2.jar\n   xml-apis-1.3.04.jar\n   smil-boston-dom-java-2000-02-25.jar\n   smack-3.2.1.jar\n   smackx-3.2.1.jar\n   usb4java-1.2.0.jar\n   jinput-2.0.6-ihmc2.jar\n   ihmc-native-library-loader-1.3.1.jar\n   commons-lang3-3.11.jar\n   nrjavaserial-5.1.1.jar\n   commons-math3-3.6.1.jar\n   mujoco-java-2.2.0-pre.5.jar\n   junit-4.0.jar\n   json-simple-1.1.jar\n   vvecmath-0.3.2.jar\n   commons-collections-3.2.2.jar\n   okio-1.0.0.jar\n   annotation-indexer-1.4.jar\n   jackson-databind-2.10.1.jar\n   usb-api-1.0.2.jar\n   libusb4java-1.2.0-linux-x86.jar\n   libusb4java-1.2.0-linux-x86_64.jar\n   libusb4java-1.2.0-linux-arm.jar\n   libusb4java-1.2.0-windows-x86.jar\n   libusb4java-1.2.0-windows-x86_64.jar\n   libusb4java-1.2.0-osx-x86.jar\n   libusb4java-1.2.0-osx-x86_64.jar\n   commons-net-3.3.jar\n   jutils-1.0.0.jar\n   jinput-platform-2.0.6-natives-windows.jar\n   jinput-platform-2.0.6-natives-osx.jar\n   jinput-platform-2.0.6-natives-linux.jar\n   jakarta.xml.bind-api-2.3.2.jar\n   javacpp-1.5.7.jar\n   icu4j-54.1.1.jar\n   emotionml-checker-java-1.1.jar\n   trove4j-2.0.2.jar\n   opennlp-tools-1.5.3.jar\n   opennlp-maxent-3.0.3.jar\n   hsqldb-2.0.0.jar\n   log4j-1.2.16.jar\n   fast-md5-2.7.1.jar\n   groovy-all-2.4.5.jar\n   Jampack-1.0.jar\n   swing-layout-1.0.3.jar\n   jackson-annotations-2.10.1.jar\n   jackson-core-2.10.1.jar\n   jakarta.activation-api-1.2.1.jar\n   jwnl-1.3.3.jar\n-testCompile:\n   bowler-kernel.jar\n   mbrola.jar\n   rosjava-0.1.6.jar\n   jsapi.jar\n   jl1.0.jar\n   jbullet-2.72.2.4.jar\n   nrV4J-3.8.1.jar\n   asm-all-3.1.jar\n   jbullet-2.72.2.4-sources.jar\n   stack-alloc.jar\n   GithubPasswordManager-0.6.1.jar\n   tink-1.3.0-rc1.jar\n   json-20180813.jar\n   nrsdk-3.33.0-jar-with-dependencies.jar\n   bluecove-gpl-2.1.1.jar\n   motej.0.9.jar\n   bluecove-2.1.1.jar\n   voice-dfki-poppy-hsmm-5.2.jar\n   voice-dfki-prudence-hsmm-5.2.jar\n   voice-dfki-spike-hsmm-5.2.jar\n   voice-cmu-slt-hsmm-5.2.jar\n   marytts-lang-en-5.2.jar\n   marytts-runtime-5.2.jar\n   marytts-signalproc-5.2.jar\n   jama-1.0.3.jar\n   richtextfx-0.6.jar\n   flowless-0.4.jar\n   undofx-2.1.1.jar\n   reactfx-2.0-M5.jar\n   groovy-2.3.7.jar\n   ivy-2.2.0.jar\n   controlsfx-8.0.6.jar\n   stapler-1.237.jar\n   json-lib-2.4-jenkins-2.jar\n   marytts-common-5.2.jar\n   ezmorph-1.0.6.jar\n   commons-lang-2.6.jar\n   httpclient-4.5.1.jar\n   github-api-1.101.jar\n   commons-codec-1.13.jar\n   org.eclipse.jgit-5.6.0.201912101111-r.jar\n   okhttp-urlconnection-2.0.0.jar\n   wordnet-random-name-1.2.jar\n   mockito-all-1.9.5.jar\n   bridge-method-injector-1.14.jar\n   miglayout-swing-4.2.jar\n   JavaCad-0.27.0.jar\n   pdf-transcoder.jar\n   xml-apis-ext.jar\n   kabeja-svg-0.4.jar\n   miethxml-toolkit.jar\n   batik-all.jar\n   js.jar\n   xml-apis.jar\n   kabeja-0.4.jar\n   kabeja-xslt.jar\n   miethxml-ui.jar\n   commons-io-2.6.jar\n   jython-2.5.3.jar\n   jython-standalone-2.5.2.jar\n   clojure-1.8.0.jar\n   jetty-webapp-9.4.36.v20210114.jar\n   jetty-servlet-9.4.36.v20210114.jar\n   jetty-security-9.4.36.v20210114.jar\n   jetty-server-9.4.36.v20210114.jar\n   jetty-servlets-9.4.36.v20210114.jar\n   javax.servlet-api-3.1.0.jar\n   vecmath-1.3.1.jar\n   slf4j-simple-1.6.1.jar\n   CHDK-PTP-Java-0.5.3-SNAPSHOT.jar\n   WalnutiQ-2.3.3.jar\n   jxl-2.4.2.jar\n   de.huxhorn.sulky.3rdparty.jlayer-1.0.jar\n   gson-2.5.jar\n   jsoup-1.8.3.jar\n   jmf-2.1.1e.jar\n   firmata4j-2.3.4.1.jar\n   jfreechart-1.0.12.jar\n   xmlrpc-client-3.1.3.jar\n   rsyntaxtextarea-2.6.0.jar\n   batik-svggen-1.7.jar\n   svg-dom-java-1.1.jar\n   \n   Medusa-7.1.jar\n   xmlrpc-common-3.1.3.jar\n   ws-commons-util-1.0.2.jar\n   junit-4.10.jar\n   protobuf-java-3.8.0.jar\n   wellbehavedfx-0.1.1.jar\n   easybind-1.0.4-SNAPSHOT.jar\n   javax.annotation-api-1.2.jar\n   commons-discovery-0.4.jar\n   commons-beanutils-1.8.0.jar\n   localizer-1.7.jar\n   tiger-types-1.3.jar\n   guava-14.0.1.jar\n   asm5-5.0.1.jar\n   commons-fileupload-1.2.1.jar\n   jsr305-2.0.1.jar\n   jzlib-1.1.3.jar\n   jsch-0.1.55.jar\n   JavaEWAH-1.1.6.jar\n   jtok-core-1.9.3.jar\n   slf4j-api-1.7.7.jar\n   bcpg-jdk15on-1.64.jar\n   bcpkix-jdk15on-1.64.jar\n   bcprov-jdk15on-1.64.jar\n   okhttp-2.0.0.jar\n   bridge-method-annotation-1.14.jar\n   asm-debug-all-4.2.jar\n   maven-plugin-api-2.0.1.jar\n   miglayout-core-4.2.jar\n   jetty-http-9.4.36.v20210114.jar\n   jetty-io-9.4.36.v20210114.jar\n   jetty-util-ajax-9.4.36.v20210114.jar\n   jetty-continuation-9.4.36.v20210114.jar\n   jetty-xml-9.4.36.v20210114.jar\n   jetty-util-9.4.36.v20210114.jar\n   usb4java-javax-1.2.0.jar\n   httpcore-nio-4.1.jar\n   httpcore-4.4.3.jar\n   commons-logging-1.2.jar\n   jssc-2.8.0.jar\n   jcommon-1.0.15.jar\n   batik-awt-util-1.7.jar\n   batik-util-1.7.jar\n   xml-apis-1.3.04.jar\n   smil-boston-dom-java-2000-02-25.jar\n   smack-3.2.1.jar\n   smackx-3.2.1.jar\n   usb4java-1.2.0.jar\n   jinput-2.0.6-ihmc2.jar\n   ihmc-native-library-loader-1.3.1.jar\n   commons-lang3-3.11.jar\n   nrjavaserial-5.1.1.jar\n   commons-math3-3.6.1.jar\n   mujoco-java-2.2.0-pre.5.jar\n   json-simple-1.1.jar\n   vvecmath-0.3.2.jar\n   hamcrest-core-1.1.jar\n   commons-collections-3.2.2.jar\n   okio-1.0.0.jar\n   annotation-indexer-1.4.jar\n   jackson-databind-2.10.1.jar\n   usb-api-1.0.2.jar\n   libusb4java-1.2.0-linux-x86.jar\n   libusb4java-1.2.0-linux-x86_64.jar\n   libusb4java-1.2.0-linux-arm.jar\n   libusb4java-1.2.0-windows-x86.jar\n   libusb4java-1.2.0-windows-x86_64.jar\n   libusb4java-1.2.0-osx-x86.jar\n   libusb4java-1.2.0-osx-x86_64.jar\n   commons-net-3.3.jar\n   jutils-1.0.0.jar\n   jinput-platform-2.0.6-natives-windows.jar\n   jinput-platform-2.0.6-natives-osx.jar\n   jinput-platform-2.0.6-natives-linux.jar\n   jakarta.xml.bind-api-2.3.2.jar\n   javacpp-1.5.7.jar\n   icu4j-54.1.1.jar\n   emotionml-checker-java-1.1.jar\n   trove4j-2.0.2.jar\n   opennlp-tools-1.5.3.jar\n   opennlp-maxent-3.0.3.jar\n   hsqldb-2.0.0.jar\n   log4j-1.2.16.jar\n   fast-md5-2.7.1.jar\n   groovy-all-2.4.5.jar\n   Jampack-1.0.jar\n   swing-layout-1.0.3.jar\n   jackson-annotations-2.10.1.jar\n   jackson-core-2.10.1.jar\n   jakarta.activation-api-1.2.1.jar\n   jwnl-1.3.3.jar\nJCSG:\n-compile:\n   bowler-kernel.jar\n   mbrola.jar\n   rosjava-0.1.6.jar\n   jsapi.jar\n   jl1.0.jar\n   jbullet-2.72.2.4.jar\n   nrV4J-3.8.1.jar\n   asm-all-3.1.jar\n   jbullet-2.72.2.4-sources.jar\n   stack-alloc.jar\n   GithubPasswordManager-0.6.1.jar\n   tink-1.3.0-rc1.jar\n   json-20180813.jar\n   nrsdk-3.33.0-jar-with-dependencies.jar\n   bluecove-gpl-2.1.1.jar\n   motej.0.9.jar\n   bluecove-2.1.1.jar\n   voice-dfki-poppy-hsmm-5.2.jar\n   voice-dfki-prudence-hsmm-5.2.jar\n   voice-dfki-spike-hsmm-5.2.jar\n   voice-cmu-slt-hsmm-5.2.jar\n   marytts-lang-en-5.2.jar\n   marytts-runtime-5.2.jar\n   marytts-signalproc-5.2.jar\n   jama-1.0.3.jar\n   richtextfx-0.6.jar\n   flowless-0.4.jar\n   undofx-2.1.1.jar\n   reactfx-2.0-M5.jar\n   groovy-2.3.7.jar\n   ivy-2.2.0.jar\n   controlsfx-8.0.6.jar\n   stapler-1.237.jar\n   json-lib-2.4-jenkins-2.jar\n   marytts-common-5.2.jar\n   ezmorph-1.0.6.jar\n   commons-lang-2.6.jar\n   httpclient-4.5.1.jar\n   github-api-1.101.jar\n   commons-codec-1.13.jar\n   org.eclipse.jgit-5.6.0.201912101111-r.jar\n   okhttp-urlconnection-2.0.0.jar\n   wordnet-random-name-1.2.jar\n   mockito-all-1.9.5.jar\n   bridge-method-injector-1.14.jar\n   miglayout-swing-4.2.jar\n   JavaCad-0.27.0.jar\n   pdf-transcoder.jar\n   xml-apis-ext.jar\n   kabeja-svg-0.4.jar\n   miethxml-toolkit.jar\n   batik-all.jar\n   js.jar\n   xml-apis.jar\n   kabeja-0.4.jar\n   kabeja-xslt.jar\n   miethxml-ui.jar\n   commons-io-2.6.jar\n   jython-2.5.3.jar\n   jython-standalone-2.5.2.jar\n   clojure-1.8.0.jar\n   jetty-webapp-9.4.36.v20210114.jar\n   jetty-servlet-9.4.36.v20210114.jar\n   jetty-security-9.4.36.v20210114.jar\n   jetty-server-9.4.36.v20210114.jar\n   jetty-servlets-9.4.36.v20210114.jar\n   javax.servlet-api-3.1.0.jar\n   vecmath-1.3.1.jar\n   slf4j-simple-1.6.1.jar\n   CHDK-PTP-Java-0.5.3-SNAPSHOT.jar\n   WalnutiQ-2.3.3.jar\n   jxl-2.4.2.jar\n   de.huxhorn.sulky.3rdparty.jlayer-1.0.jar\n   gson-2.5.jar\n   jsoup-1.8.3.jar\n   jmf-2.1.1e.jar\n   firmata4j-2.3.4.1.jar\n   jfreechart-1.0.12.jar\n   xmlrpc-client-3.1.3.jar\n   rsyntaxtextarea-2.6.0.jar\n   batik-svggen-1.7.jar\n   svg-dom-java-1.1.jar\n   \n   Medusa-7.1.jar\n   protobuf-java-3.8.0.jar\n   wellbehavedfx-0.1.1.jar\n   easybind-1.0.4-SNAPSHOT.jar\n   javax.annotation-api-1.2.jar\n   commons-discovery-0.4.jar\n   commons-beanutils-1.8.0.jar\n   localizer-1.7.jar\n   tiger-types-1.3.jar\n   guava-14.0.1.jar\n   asm5-5.0.1.jar\n   commons-fileupload-1.2.1.jar\n   jsr305-2.0.1.jar\n   jzlib-1.1.3.jar\n   jsch-0.1.55.jar\n   JavaEWAH-1.1.6.jar\n   jtok-core-1.9.3.jar\n   slf4j-api-1.7.7.jar\n   bcpg-jdk15on-1.64.jar\n   bcpkix-jdk15on-1.64.jar\n   bcprov-jdk15on-1.64.jar\n   okhttp-2.0.0.jar\n   bridge-method-annotation-1.14.jar\n   asm-debug-all-4.2.jar\n   maven-plugin-api-2.0.1.jar\n   miglayout-core-4.2.jar\n   jetty-http-9.4.36.v20210114.jar\n   jetty-io-9.4.36.v20210114.jar\n   jetty-util-ajax-9.4.36.v20210114.jar\n   jetty-continuation-9.4.36.v20210114.jar\n   jetty-xml-9.4.36.v20210114.jar\n   jetty-util-9.4.36.v20210114.jar\n   usb4java-javax-1.2.0.jar\n   httpcore-nio-4.1.jar\n   httpcore-4.4.3.jar\n   commons-logging-1.2.jar\n   jssc-2.8.0.jar\n   jcommon-1.0.15.jar\n   xmlrpc-common-3.1.3.jar\n   batik-awt-util-1.7.jar\n   batik-util-1.7.jar\n   ws-commons-util-1.0.2.jar\n   xml-apis-1.3.04.jar\n   smil-boston-dom-java-2000-02-25.jar\n   smack-3.2.1.jar\n   smackx-3.2.1.jar\n   usb4java-1.2.0.jar\n   jinput-2.0.6-ihmc2.jar\n   ihmc-native-library-loader-1.3.1.jar\n   commons-lang3-3.11.jar\n   nrjavaserial-5.1.1.jar\n   commons-math3-3.6.1.jar\n   mujoco-java-2.2.0-pre.5.jar\n   junit-4.0.jar\n   json-simple-1.1.jar\n   vvecmath-0.3.2.jar\n   commons-collections-3.2.2.jar\n   okio-1.0.0.jar\n   annotation-indexer-1.4.jar\n   jackson-databind-2.10.1.jar\n   usb-api-1.0.2.jar\n   libusb4java-1.2.0-linux-x86.jar\n   libusb4java-1.2.0-linux-x86_64.jar\n   libusb4java-1.2.0-linux-arm.jar\n   libusb4java-1.2.0-windows-x86.jar\n   libusb4java-1.2.0-windows-x86_64.jar\n   libusb4java-1.2.0-osx-x86.jar\n   libusb4java-1.2.0-osx-x86_64.jar\n   commons-net-3.3.jar\n   jutils-1.0.0.jar\n   jinput-platform-2.0.6-natives-windows.jar\n   jinput-platform-2.0.6-natives-osx.jar\n   jinput-platform-2.0.6-natives-linux.jar\n   jakarta.xml.bind-api-2.3.2.jar\n   javacpp-1.5.7.jar\n   icu4j-54.1.1.jar\n   emotionml-checker-java-1.1.jar\n   trove4j-2.0.2.jar\n   opennlp-tools-1.5.3.jar\n   opennlp-maxent-3.0.3.jar\n   hsqldb-2.0.0.jar\n   log4j-1.2.16.jar\n   fast-md5-2.7.1.jar\n   groovy-all-2.4.5.jar\n   Jampack-1.0.jar\n   swing-layout-1.0.3.jar\n   jackson-annotations-2.10.1.jar\n   jackson-core-2.10.1.jar\n   jakarta.activation-api-1.2.1.jar\n   jwnl-1.3.3.jar\n-testCompile:\n   bowler-kernel.jar\n   mbrola.jar\n   rosjava-0.1.6.jar\n   jsapi.jar\n   jl1.0.jar\n   jbullet-2.72.2.4.jar\n   nrV4J-3.8.1.jar\n   asm-all-3.1.jar\n   jbullet-2.72.2.4-sources.jar\n   stack-alloc.jar\n   GithubPasswordManager-0.6.1.jar\n   tink-1.3.0-rc1.jar\n   json-20180813.jar\n   nrsdk-3.33.0-jar-with-dependencies.jar\n   bluecove-gpl-2.1.1.jar\n   motej.0.9.jar\n   bluecove-2.1.1.jar\n   voice-dfki-poppy-hsmm-5.2.jar\n   voice-dfki-prudence-hsmm-5.2.jar\n   voice-dfki-spike-hsmm-5.2.jar\n   voice-cmu-slt-hsmm-5.2.jar\n   marytts-lang-en-5.2.jar\n   marytts-runtime-5.2.jar\n   marytts-signalproc-5.2.jar\n   jama-1.0.3.jar\n   richtextfx-0.6.jar\n   flowless-0.4.jar\n   undofx-2.1.1.jar\n   reactfx-2.0-M5.jar\n   groovy-2.3.7.jar\n   ivy-2.2.0.jar\n   controlsfx-8.0.6.jar\n   stapler-1.237.jar\n   json-lib-2.4-jenkins-2.jar\n   marytts-common-5.2.jar\n   ezmorph-1.0.6.jar\n   commons-lang-2.6.jar\n   httpclient-4.5.1.jar\n   github-api-1.101.jar\n   commons-codec-1.13.jar\n   org.eclipse.jgit-5.6.0.201912101111-r.jar\n   okhttp-urlconnection-2.0.0.jar\n   wordnet-random-name-1.2.jar\n   mockito-all-1.9.5.jar\n   bridge-method-injector-1.14.jar\n   miglayout-swing-4.2.jar\n   JavaCad-0.27.0.jar\n   pdf-transcoder.jar\n   xml-apis-ext.jar\n   kabeja-svg-0.4.jar\n   miethxml-toolkit.jar\n   batik-all.jar\n   js.jar\n   xml-apis.jar\n   kabeja-0.4.jar\n   kabeja-xslt.jar\n   miethxml-ui.jar\n   commons-io-2.6.jar\n   jython-2.5.3.jar\n   jython-standalone-2.5.2.jar\n   clojure-1.8.0.jar\n   jetty-webapp-9.4.36.v20210114.jar\n   jetty-servlet-9.4.36.v20210114.jar\n   jetty-security-9.4.36.v20210114.jar\n   jetty-server-9.4.36.v20210114.jar\n   jetty-servlets-9.4.36.v20210114.jar\n   javax.servlet-api-3.1.0.jar\n   vecmath-1.3.1.jar\n   slf4j-simple-1.6.1.jar\n   CHDK-PTP-Java-0.5.3-SNAPSHOT.jar\n   WalnutiQ-2.3.3.jar\n   jxl-2.4.2.jar\n   de.huxhorn.sulky.3rdparty.jlayer-1.0.jar\n   gson-2.5.jar\n   jsoup-1.8.3.jar\n   jmf-2.1.1e.jar\n   firmata4j-2.3.4.1.jar\n   jfreechart-1.0.12.jar\n   xmlrpc-client-3.1.3.jar\n   rsyntaxtextarea-2.6.0.jar\n   batik-svggen-1.7.jar\n   svg-dom-java-1.1.jar\n   \n   Medusa-7.1.jar\n   xmlrpc-common-3.1.3.jar\n   ws-commons-util-1.0.2.jar\n   junit-4.10.jar\n   protobuf-java-3.8.0.jar\n   wellbehavedfx-0.1.1.jar\n   easybind-1.0.4-SNAPSHOT.jar\n   javax.annotation-api-1.2.jar\n   commons-discovery-0.4.jar\n   commons-beanutils-1.8.0.jar\n   localizer-1.7.jar\n   tiger-types-1.3.jar\n   guava-14.0.1.jar\n   asm5-5.0.1.jar\n   commons-fileupload-1.2.1.jar\n   jsr305-2.0.1.jar\n   jzlib-1.1.3.jar\n   jsch-0.1.55.jar\n   JavaEWAH-1.1.6.jar\n   jtok-core-1.9.3.jar\n   slf4j-api-1.7.7.jar\n   bcpg-jdk15on-1.64.jar\n   bcpkix-jdk15on-1.64.jar\n   bcprov-jdk15on-1.64.jar\n   okhttp-2.0.0.jar\n   bridge-method-annotation-1.14.jar\n   asm-debug-all-4.2.jar\n   maven-plugin-api-2.0.1.jar\n   miglayout-core-4.2.jar\n   jetty-http-9.4.36.v20210114.jar\n   jetty-io-9.4.36.v20210114.jar\n   jetty-util-ajax-9.4.36.v20210114.jar\n   jetty-continuation-9.4.36.v20210114.jar\n   jetty-xml-9.4.36.v20210114.jar\n   jetty-util-9.4.36.v20210114.jar\n   usb4java-javax-1.2.0.jar\n   httpcore-nio-4.1.jar\n   httpcore-4.4.3.jar\n   commons-logging-1.2.jar\n   jssc-2.8.0.jar\n   jcommon-1.0.15.jar\n   batik-awt-util-1.7.jar\n   batik-util-1.7.jar\n   xml-apis-1.3.04.jar\n   smil-boston-dom-java-2000-02-25.jar\n   smack-3.2.1.jar\n   smackx-3.2.1.jar\n   usb4java-1.2.0.jar\n   jinput-2.0.6-ihmc2.jar\n   ihmc-native-library-loader-1.3.1.jar\n   commons-lang3-3.11.jar\n   nrjavaserial-5.1.1.jar\n   commons-math3-3.6.1.jar\n   mujoco-java-2.2.0-pre.5.jar\n   json-simple-1.1.jar\n   vvecmath-0.3.2.jar\n   hamcrest-core-1.1.jar\n   commons-collections-3.2.2.jar\n   okio-1.0.0.jar\n   annotation-indexer-1.4.jar\n   jackson-databind-2.10.1.jar\n   usb-api-1.0.2.jar\n   libusb4java-1.2.0-linux-x86.jar\n   libusb4java-1.2.0-linux-x86_64.jar\n   libusb4java-1.2.0-linux-arm.jar\n   libusb4java-1.2.0-windows-x86.jar\n   libusb4java-1.2.0-windows-x86_64.jar\n   libusb4java-1.2.0-osx-x86.jar\n   libusb4java-1.2.0-osx-x86_64.jar\n   commons-net-3.3.jar\n   jutils-1.0.0.jar\n   jinput-platform-2.0.6-natives-windows.jar\n   jinput-platform-2.0.6-natives-osx.jar\n   jinput-platform-2.0.6-natives-linux.jar\n   jakarta.xml.bind-api-2.3.2.jar\n   javacpp-1.5.7.jar\n   icu4j-54.1.1.jar\n   emotionml-checker-java-1.1.jar\n   trove4j-2.0.2.jar\n   opennlp-tools-1.5.3.jar\n   opennlp-maxent-3.0.3.jar\n   hsqldb-2.0.0.jar\n   log4j-1.2.16.jar\n   fast-md5-2.7.1.jar\n   groovy-all-2.4.5.jar\n   Jampack-1.0.jar\n   swing-layout-1.0.3.jar\n   jackson-annotations-2.10.1.jar\n   jackson-core-2.10.1.jar\n   jakarta.activation-api-1.2.1.jar\n   jwnl-1.3.3.jar\n\nDeprecated Gradle features were used in this build, making it incompatible with Gradle 7.0.\nUse '--warning-mode all' to show the individual deprecation warnings.\nSee https://docs.gradle.org/6.2/userguide/command_line_interface.html#sec:command_line_warnings\n\nBUILD SUCCESSFUL in 2m 39s\n1 actionable task: 1 executed\n"
  },
  {
    "path": "build.gradle",
    "content": "buildscript {\n\trepositories {\n\t\t[\n\t\t\tmavenCentral(),\n\t\t\tmaven {\n\t\t\t\turl \"https://plugins.gradle.org/m2/\"\n\t\t\t},\n\t\t\tmavenLocal(),\n\t\t\tmaven {\n\t\t\t\turl \"https://repo.jenkins-ci.org/releases/\"\n\t\t\t}\n\t\t]\n\t}\n\tdependencies {\n\t\tclasspath 'com.gradleup.shadow:shadow-gradle-plugin:8.3.9'\n\t}\n}\n\nboolean is64Bit() {\n\t////System.out.println(\"Arch: \"+getOsArch());\n\treturn getOsArch().startsWith(\"x86_64\") || getOsArch().startsWith(\"amd64\");\n}\nboolean isARM() {\n\treturn getOsArch().startsWith(\"arm\");\n}\nboolean isCortexA8(){\n\tif(isARM()){\n\t\t//TODO check for cortex a8 vs arm9 generic\n\t\treturn true;\n\t}\n\treturn false;\n}\nboolean isWindows() {\n\t////System.out.println(\"OS name: \"+getOsName());\n\treturn getOsName().toLowerCase().startsWith(\"windows\") ||getOsName().toLowerCase().startsWith(\"microsoft\") || getOsName().toLowerCase().startsWith(\"ms\");\n}\n\nboolean isLinux() {\n\treturn getOsName().toLowerCase().startsWith(\"linux\");\n}\n\nboolean isOSX() {\n\treturn getOsName().toLowerCase().startsWith(\"mac\");\n}\n\nString getExtension() {\n\tif(isWindows()) {\n\t\treturn \".dll\";\n\t}\n\n\tif(isLinux()) {\n\t\treturn \".so\";\n\t}\n\n\tif(isOSX()) {\n\t\treturn \".jnilib\";\n\t}\n\n\treturn \"\";\n}\n\nString getOsName() {\n\treturn System.getProperty(\"os.name\");\n}\n\nString getOsArch() {\n\treturn System.getProperty(\"os.arch\");\n}\n\n\nplugins {\n\tid 'com.github.jk1.dependency-license-report' version '1.17'\n\tid 'java'\n\tid 'signing'\n\tid 'application'\n\tid 'eclipse'\n\tid 'com.diffplug.spotless' version '6.25.0'\n}\nspotless {\n\tjava {\n\t\tlineEndings = com.diffplug.spotless.LineEnding.UNIX\n\t\t// Eclipse formatter with your config file\n\t\teclipse('4.26').configFile('libraries/bowler-script-kernel/config/eclipse-formatter.xml')\t\t// Optional but recommended additions:\n\t\t// Optional but recommended additions:\n\t\tremoveUnusedImports()\n\t\ttrimTrailingWhitespace()\n\t\tendWithNewline()\n\t}\n}\nif (project == rootProject) {\n\tapply plugin: 'application'\n\tapply plugin: 'com.gradleup.shadow'\n}else {\n\tapply plugin: 'java-library'\n}\n\n\nimport com.github.jk1.license.render.*\nimport com.github.jk1.license.importer.*\n\nlicenseReport {\n\t// Set output directory for the report data.\n\t// Defaults to ${project.buildDir}/reports/dependency-license.\n\toutputDir = \"$projectDir/build/licenses\"\n\n\t// Adjust the configurations to fetch dependencies. Default is 'runtimeClasspath'\n\t// For Android projects use 'releaseRuntimeClasspath' or 'yourFlavorNameReleaseRuntimeClasspath'\n\t// Use 'ALL' to dynamically resolve all configurations:\n\t// configurations = ALL\n\tconfigurations = ['runtimeClasspath']\n\n\n\t// List the ids (in module:name format) to exclude from dependency report. Supports regular expressions.\n\t// By default excludes is empty.\n\texcludes = ['moduleGroup:moduleName']\n\n\t// Don't include artifacts of project's own group into the report\n\texcludeOwnGroup = false\n\n\t// Don't exclude bom dependencies.\n\t// If set to true, then all boms will be excluded from the report\n\texcludeBoms = false\n\n\t// Set custom report renderer, implementing ReportRenderer.\n\t// Yes, you can write your own to support any format necessary.\n\trenderers = [\n\t\tnew XmlReportRenderer('third-party-libs.xml', 'Back-End Libraries'),\n\t\tnew CsvReportRenderer('third-party-libs.csv')\n\t]\n\n}\n//apply plugin: 'edu.sc.seis.launch4j'\n\n[compileJava, compileTestJava]*.options*.encoding = 'UTF-8'\n// NetBeans will automatically add \"run\" and \"debug\" tasks relying on the\n// \"mainClass\" property. You may however define the property prior executing\n// tasks by passing a \"-PmainClass=<QUALIFIED_CLASS_NAME>\" argument.\n//\n// Note however, that you may define your own \"run\" and \"debug\" task if you\n// prefer. In this case NetBeans will not add these tasks but you may rely on\n// your own compile.\n\next.mainClass = 'com.neuronrobotics.bowlerstudio.BowlerStudio'\n\nFile buildDir = file(\".\");\n\nconfigurations.all {\n\texclude module: 'slf4j-log4j12'\n}\nsourceSets {\n\n\ttest {\n\t\tjava {\n\t\t\tsrcDirs = [\n\t\t\t\t\"test/java/src\" ]  // Note @Peter's comment below\n\t\t}\n\t}\n}\n/*\n launch4j {\n mainClassName = ext.mainClass\n icon = buildDir.getAbsolutePath()+\"/src/main/resources/CommonWealthRobotics.ico\"\n }\n */\nProperties props = new Properties()\nprops.load(new FileInputStream(buildDir.getAbsolutePath()+\"/src/main/resources/com/neuronrobotics/bowlerstudio/build.properties\"))\n\n\n\nrepositories {\n\tmaven {\n\t\turl \"https://repo.jenkins-ci.org/public/\"\n\t}\n\tmavenCentral()\n\t\n\tmaven { url 'https://repo1.maven.org/maven2/'}\n\n\n//\t    maven {\n//\t        url \"https://repo.myrobotlab.org/artifactory/myrobotlab/\"\n//\t        content {\n//\t            includeGroup(\"de.dfki.mary\")\n//\t        }\n//\t    }\n\tmaven { url 'https://maven-central.storage-download.googleapis.com/repos/central/data/'}\n\tmaven {url 'https://repo.jenkins-ci.org/public/'}\n\tmaven { url 'https://clojars.org/repo' }\n\tmaven { url 'https://jline.sourceforge.net/m2repo' }\n\tmaven { url 'https://repo.spring.io/milestone'}\n\t//\n//\tmaven { \n//\t\turl 'https://jenkinsci.artifactoryonline.com/jenkinsci/public/' \n//\t\tallowInsecureProtocol = true\n//\t}\n    maven {\n        url \"https://raw.githubusercontent.com/DFKI-MLT/Maven-Repository/main\"\n    }\n//\tmaven {\n//\t\turl \"https://repo.spring.io/libs-milestone/\"\n//\t}\n\tmaven {\n\t\turl \"https://maven.cloudsmith.io/marytts/marytts/\"\n\t}\n\tmaven { url 'https://plugins.gradle.org/m2/' }\n\t//maven { url 'https://dl.bintray.com/clearcontrol/ClearControl' }\n\n//\tmaven { url 'https://repo.spring.io/plugins-release/'  }\n\n\tmaven { url \"https://jitpack.io\" }\n\tmaven { url \"https://repo.eclipse.org/content/groups/releases/\" }\n\tmaven { url \"https://dl.bintray.com/dfki-lt/maven/\" }\n\tmaven { url \"https://raw.github.com/marytts/marytts/master/repository/\" }\n\tmaven { url \"https://repo.jenkins-ci.org/public/\" }\n\tmaven { url \"https://raw.githubusercontent.com/DFKI-MLT/DFKI-LT-MAVEN/master/repository\" }\n\t\n\tmaven {\n\t\turl 'https://commonwealthrobotics.com/maven/'\n\t\tallowInsecureProtocol = true\n\t}\n\tmaven { url \"https://clojars.org/repo\" }\n\t\n}\n\ndependencies {\n\n\timplementation 'org.kohsuke.stapler:stapler:1.263'\n\t// https://mvnrepository.com/artifact/org.kohsuke.stapler/stapler\n\t//implementation group: 'org.kohsuke.stapler', name: 'stapler', version: '1881.vd39f3ee5c629'\n\t\n\timplementation group: 'org.json', name: 'json', version: '20180813'\n\timplementation 'com.google.crypto.tink:tink:1.3.0-rc1'\n\n\timplementation 'gov.nist.math:jama:1.0.2'\n\n\timplementation group: 'org.fxmisc.richtext', name: 'richtextfx', version: '0.6'\n\n\n\timplementation group: 'org.controlsfx', name: 'controlsfx', version: '8.0.6'\n\timplementation group: 'commons-lang', name: 'commons-lang', version: '2.6'\n\timplementation group: 'commons-codec', name: 'commons-codec', version: '1.7'\n\n\t// https://mvnrepository.com/artifact/org.eclipse.jgit/org.eclipse.jgit\n\timplementation group: 'org.eclipse.jgit', name: 'org.eclipse.jgit', version: '5.13.1.202206130422-r'\n\n\timplementation group: 'com.squareup.okhttp', name: 'okhttp-urlconnection', version: '2.0.0'\n\timplementation group: 'org.mockito', name: 'mockito-all', version: '1.9.5'\n\timplementation group: 'com.infradna.tool', name: 'bridge-method-injector', version: '1.14'\n\t//compile group: 'com.neuronrobotics', name:'GithubPasswordManager', version:'0.6.1'\n\n\timplementation 'com.miglayout:miglayout-swing:4.2'\n\timplementation 'commons-io:commons-io:2.6'\n\timplementation group:'org.python',name:'jython',version:'2.5.3'\n\timplementation group:'org.python',name:'jython-standalone',version:'2.5.2'\n\timplementation 'org.clojure:clojure:1.8.0'\n\t// jetty server\n\timplementation \"org.eclipse.jetty:jetty-server:9.4.36.v20210114\"\n\timplementation \"org.eclipse.jetty:jetty-servlet:9.4.36.v20210114\"\n\timplementation \"org.eclipse.jetty:jetty-servlets:9.4.36.v20210114\"\n\timplementation \"org.eclipse.jetty:jetty-webapp:9.4.36.v20210114\"\n\timplementation \"javax.servlet:javax.servlet-api:3.1.0\"\n\n\n\t//compile 'org.clojure:tools.nrepl:0.2.10'\n\t//compile \"overtone:overtone:0.9.1\"\n\t//compile \"edu.cmu.sphinx:sphinx4-core:5prealpha-SNAPSHOT\"\n\t//compile \"edu.cmu.sphinx:sphinx4-data:5prealpha-SNAPSHOT\"\n\timplementation group: 'java3d', name: 'vecmath', version: '1.3.1'\n\timplementation 'org.slf4j:slf4j-simple:1.6.1'\n\n\t//implementation \"com.neuronrobotics:CHDK-PTP-Java:0.5.3-SNAPSHOT\"\n\t//compile \"com.neuronrobotics:java-bowler:3.25.4\"\n\t//compile fileTree (dir: '../java-bowler/build/libs/', includes: ['nrsdk-3.23.3-jar-with-dependencies.jar'])\n\timplementation \"com.neuronrobotics:WalnutiQ:2.3.3\"\n\n\n\t/*\n\t String basedir =System.getenv(\"OPENCV_DIR\")+\"/java/opencv-249.jar\";\n\t if(isWindows()){\n\t basedir =System.getenv(\"OPENCV_DIR\")+\"\\\\..\\\\..\\\\java\\\\opencv-249.jar\";\n\t println(\"OPENCV_DIR=\"+basedir);\n\t compile files(basedir)\n\t }\n\t if(isOSX()){\n\t basedir =System.getenv(\"OPENCV_DIR\")+\"../../java/opencv-249.jar\";\n\t println(\"OPENCV_DIR=\"+basedir);\n\t if(System.getenv(\"OPENCV_DIR\")!=null)\n\t compile files(basedir)\n\t else\n\t //If you set your OPENCV_DIR environment variable, then we wouldnt have to do hacky things\n\t compile files('/Applications/BowlerStudio.app/Contents/MacOS/opencv249build/bin/opencv-249.jar')\n\t }\n\t if(isLinux()){\n\t //compile files('/usr/share/java/opencv-249.jar')\n\t if(new File(\"/usr/share/OpenCV/java/\").exists()){\n\t System.out.println(\"Using the legacy opencv dir \")\n\t compile fileTree (dir: '/usr/share/OpenCV/java/', includes: ['*opencv-24*.jar'])\n\t }else{\n\t compile fileTree (dir: '/usr/share/java/', includes: ['*opencv-24*.jar'])\n\t }\n\t }\n\t */\n\t//compile group: 'jfree', name: 'jfreechart', version: '1.0.12'\n\timplementation group: 'jexcelapi', name: 'jxl', version: '2.4.2'\n\t//compile group: 'com.google.zxing', name: 'zxing-parent', version: '3.2.0'\n\t//compile group:'com.github.ellzord', name:'JALSE', version:'1.0.9'\n\n\timplementation group:'de.huxhorn.sulky', name:'de.huxhorn.sulky.3rdparty.jlayer', version:'1.0'\n\t//compile(\"org.springframework.boot:spring-boot-starter-web:1.2.7.RELEASE\")\n\n\timplementation 'com.google.code.gson:gson:2.5'\n\timplementation 'org.jsoup:jsoup:1.8.3'\n\timplementation 'org.apache.httpcomponents:httpclient:4.5.1'\n\t//compile 'cz.advel.jbullet:jbullet:20101010-1'\n\t//compile 'org.bubblecloud.jbullet:jbullet:2.72.2.4'// replaced by local jar because jbullet maven went down\n\t//compile \"alda:alda:1.0.0-rc14\"\n\n\t//Deep Learning 4 j and dependancies\n\t/*\n\t compile 'org.deeplearning4j:deeplearning4j-core:0.4-rc3.8'\n\t //compile 'org.nd4j:nd4j-x86:0.4-rc3.8'\n\t compile 'org.deeplearning4j:deeplearning4j-nlp:0.4-rc3.8'\n\t compile 'org.deeplearning4j:deeplearning4j-ui:0.4-rc3.8'\n\t compile 'com.google.guava:guava:19.0'\n\t compile 'org.nd4j:canova-nd4j-image:0.0.0.14'\n\t compile 'org.nd4j:canova-nd4j-codec:0.0.0.14'\n\t compile 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.5.1'\n\t */\n\t// JScience:\n\t//compile 'org.jscience:jscience:4.3.1'\n\n\timplementation 'javax.media:jmf:2.1.1e'\n\n\t//Weka\n\t//compile 'nz.ac.waikato.cms.weka:weka-stable:3.6.13'\n\n\t//Firmata\n\timplementation 'com.github.kurbatov:firmata4j:2.3.4.1'\n\n\timplementation group: 'jfree', name: 'jfreechart', version: '1.0.12'\n\n\t//compile fileTree (dir: '../bowler-script-kernel/build/libs', includes: ['BowlerScriptingKernel-0.25.4.jar'])\n\ttestImplementation group: 'junit', name: 'junit', version: '4.10'\n\t//compile \"org.jfxtras:jfxtras-common:8.0-r4\"\n\t//compile \"org.jfxtras:jfxtras-fxml:8.0-r4\"\n\t//compile \"org.jfxtras:jfxtras-controls:8.0-r4\"\n\t//compile \"org.jfxtras:jfxtras-agenda:8.0-r4\"\n\t//compile \"org.jfxtras:jfxtras-window:8.0-r4\"\n\t//compile \"org.jfxtras:jfxtras-menu:8.0-r4\"\n\t//compile \"org.jfxtras:jfxtras-labs:8.0-r4\"\n\n\n\t//compile 'com.github.movisens:SmartGattLib:1.7'\n\t// https://mvnrepository.com/artifact/org.apache.xmlrpc/xmlrpc-client\n\timplementation group: 'org.apache.xmlrpc', name: 'xmlrpc-client', version: '3.1.3'\n\t// https://mvnrepository.com/artifact/com.abercap/odoo-java-api\n\t//compile group: 'com.abercap', name: 'odoo-java-api', version: '1.1.0.RELEASE'\n\t//http://www.jocl.org/\n\t//compile group: 'org.jocl', name: 'jocl', version: '2.0.0'\n\t// https://mvnrepository.com/artifact/com.nativelibs4java/jnaerator\n\t//compile group: 'com.nativelibs4java', name: 'jnaerator', version: '0.11'\n\t// https://mvnrepository.com/artifact/com.github.kurbatov/firmata4j\n\n\t// https://mvnrepository.com/artifact/com.fifesoft/rsyntaxtextarea\n\timplementation(\"com.fifesoft:rsyntaxtextarea:3.6.0\")\n\n\t//compile 'org.bubblecloud.jbullet:jbullet:2.72.2.4'\n\t// https://mvnrepository.com/artifact/org.apache.xmlgraphics/batik-svggen\n    implementation group: 'org.apache.xmlgraphics', name: 'batik-all', version: '1.17'\n\t// https://mvnrepository.com/artifact/org.axsl.org.w3c.dom.svg/svg-dom-java\n\timplementation group: 'org.axsl.org.w3c.dom.svg', name: 'svg-dom-java', version: '1.1'\n\n\n\t//compile fileTree (dir: 'libraries/bowler-script-kernel/libs/', includes: ['*.jar'])\n\timplementation project('libraries:bowler-script-kernel:java-bowler')\n\timplementation project('libraries:bowler-script-kernel:JCSG')\n\timplementation project('libraries:bowler-script-kernel')\n\t//compile project(':libraries:bowler-script-kernel:JCSG')\n\t\n\timplementation project('libraries:bowler-script-kernel:GithubPasswordManager:GithubPasswordManager')\n\t//compile project(':kinematics-chef')\n\t//compile 'com.neuronrobotics:kinematics-chef-core:0.0.15'\n\n\t//compile fileTree (dir: 'libraries/java-bowler/libs/', includes: ['*.jar'])\n\timplementation 'com.google.guava:guava:19+'\n\n\timplementation group: 'org.kohsuke', name: 'wordnet-random-name', version: '1.2'\n\n\n\t//https://github.com/HanSolo/Medusa\n\timplementation group: 'eu.hansolo', name: 'Medusa', version: '7.1'\n\t//MuJoCo\n\timplementation group: 'org.bytedeco', name: 'javacpp', version: '1.5.7'\n\timplementation group: 'com.neuronrobotics', name: 'mujoco-java', version:'3.1.3-pre.11'\n\t// Zip and unzip, tar and untar\n\timplementation 'org.apache.commons:commons-compress:1.26.2'\n\n\n}\n\n// create a fat-jar (class files plus dependencies\n// excludes VRL.jar (plugin jar files must not start with 'vrl-\\\\d+')\njar {\n\t//zip64 true\n\tjar.duplicatesStrategy = DuplicatesStrategy.EXCLUDE\n\t// project class files compiled from source\n\t//from files(sourceSets.main.output.classesDir)\n\n\tmanifest {\n\t\tattributes(\n\t\t\t\t\"Main-Class\": 'com.neuronrobotics.bowlerstudio.BowlerStudio',\n\t\t\t\t\"Manifest-Version\": \"1.0\",\n\t\t\t\t\"Created-By\": \"CommonWealth Robotics Cooperative\",\n\t\t\t\t\"Specification-Title\": props.\"app.name\",\n\t\t\t\t\"Specification-Version\": props.\"app.version\",\n\t\t\t\t\"Specification-Vendor\": \"CommonWealth Robotics Cooperative\",\n\t\t\t\t\"Implementation-Title\": props.\"app.name\",\n\t\t\t\t\"Implementation-Version\" : props.\"app.version\",\n\t\t\t\t\"Implementation-Vendor\": \"CommonWealth Robotics Cooperative\"\n\n\n\t\t\t\t)\n\t}\n}\ntasks.withType(JavaCompile).configureEach {\n\toptions.compilerArgs += ['--enable-preview']\n}\n\ntasks.withType(Test).configureEach {\n\tjvmArgs '--enable-preview'\n}\n\ntasks.withType(JavaExec).configureEach {\n\tjvmArgs '--enable-preview'\n}\ntask ('showAll') {\n\tdoLast {\n\t\tallprojects.each {\n\t\t\tprintln(it.name+':')\n\t\t\tprintln('-compile:')\n\t\t\tconfigurations.compile.each { c ->\n\t\t\t\tprintln('   '+c.name)\n\t\t\t}\n\t\t\tprintln '-testCompile:'\n\t\t\tconfigurations.testCompile.each { r->\n\t\t\t\tprintln('   '+ r.name)\n\t\t\t}\n\t\t}\n\t}\n}\neclipse{\n\tjdt {\n//\t\tsourceCompatibility = 1.8\n//\t\ttargetCompatibility = 1.8\n\t\tfile {\n\t\t\twithProperties { properties ->\n\t\t\t\tproperties.setProperty('org.eclipse.jdt.core.compiler.problem.forbiddenReference', 'ignore')\n\t\t\t}\n\t\t}\n\t}\n}\n\napplication {\n    mainClass = 'com.neuronrobotics.bowlerstudio.BowlerStudio'\n}\n\nif (project == rootProject) {\n\tshadowJar {\n\t    dependsOn 'spotlessApply'\n\t    zip64 true\n\t    manifest {\n\t        attributes 'Main-Class': 'com.neuronrobotics.bowlerstudio.BowlerStudio'\n\t    }\n\t\n\t    if (project == rootProject) {\n\t        archiveBaseName.set('BowlerStudio')\n\t        archiveClassifier.set('')\n\t        archiveVersion.set('')\n\t    } else {\n\t        archiveBaseName.set('BowlerStudio')\n\t        archiveClassifier.set('')\n\t        archiveVersion.set('')\n\t    }\n\t\n\t    mergeServiceFiles()\n\t}\n}\n"
  },
  {
    "path": "debian/.gitignore",
    "content": "/changelog.bak\n/.changelog.swp\n"
  },
  {
    "path": "debian/README.Debian",
    "content": "bowlerstudio for Debian\n----------------------\n\n<possible notes regarding this package - if none, delete this file>\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Sun, 26 Jun 2016 11:43:09 -0400\n"
  },
  {
    "path": "debian/README.source",
    "content": "bowlerstudio for Debian\n----------------------\n\n<this file describes information about the source package, see Debian policy\nmanual section 4.14. You WILL either need to modify or delete this file>\n\n\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Sun, 26 Jun 2016 11:43:09 -0400\n\n"
  },
  {
    "path": "debian/bowlerstudio-docs.docs",
    "content": "README.source\nREADME.Debian\n"
  },
  {
    "path": "debian/bowlerstudio.doc-base.EX",
    "content": "Document: bowlerstudio\nTitle: Debian bowlerstudio Manual\nAuthor: <insert document author here>\nAbstract: This manual describes what bowlerstudio is\n and how it can be used to\n manage online manuals on Debian systems.\nSection: unknown\n\nFormat: debiandoc-sgml\nFiles: /usr/share/doc/bowlerstudio/bowlerstudio.sgml.gz\n\nFormat: postscript\nFiles: /usr/share/doc/bowlerstudio/bowlerstudio.ps.gz\n\nFormat: text\nFiles: /usr/share/doc/bowlerstudio/bowlerstudio.text.gz\n\nFormat: HTML\nIndex: /usr/share/doc/bowlerstudio/html/index.html\nFiles: /usr/share/doc/bowlerstudio/html/*.html\n"
  },
  {
    "path": "debian/changelog",
    "content": "bowlerstudio (1.24.0) bionic; urgency=medium\n\n  * adding svg loading\n  * adding a buton to launch advanced editors\n  * removed prints\n  * add info for items\n  * bugfix\n  * adding API for fail over install\n  * approprate construstor order\n  * adding tooltips for the buttons\n  * Adding the launcher class for Eclipse/groovy\n  * fix junk import\n  * modularized\n  * deal with gists better\n  * adding auto-run from external editor\n  * 0.16.0\n  * jcsg\n  * update addFile wizard to create repos instead of gists\n  * changing fork from gist fork to git repo forking\n  * Adding windows inkscape file\n  * adding windows\n  * allow eclipse on windows\n  * Tested working on windows!\n  * check for ignore contents existing\n  * adding windows support\n  * #225 Check for null string before checking the stack\n  * 1.17.1\n  * bugfix\n  * ficing the delimiters for windows in the subclass\n  * Use more than one option for inkscape\n  * 1.17.4\n  * eclipse loading stuff again\n  * jcsg\n  * jcsg\n  * SVG loading\n  * bugfix in the color loading\n  * Disable the advanced editor button if the editor is open\n  * adding a search for inkscape exe\n  * adding arduino to the advanced editors\n  * launch using the finle name\n  * find the workspace\n  * dealing wiht the escaped slashes on windows\n  * 1.18.1\n  * dramatic jog widget performance boost\n  * change controller wrapper api\n  * new game controller framework\n  * controller with multiple sources\n  * 1.20.0\n  * new jog configuration\n  * Adding the game contoller mapping wizard\n  * Calibrate game controllers as a plugin tab\n  * add tooltips for plugin buttons\n  * Moving javaFX depenancies to the kernel\n  * kernel\n  * close the running script and restart in a thread on file change\n  * 1.21.0\n  * addin a mobile base jogging\n  * 1.22.0\n  * #227 #228 #229\n  * revbump jetty to 9.4.36.v20210114\n  * close #206\n  * add clearer printing while installing and opening eclipse\n  * check to see if the workspace exists before checking the lock file\n  * eclipse opening\n  * adding print statements to entering kernel mode\n  * disconnect listeners\n  * kernels\n  * fixing bounding issue\n  * 1.23.2\n  * 1.23.3 Fixing the loading of default creatures by pulling first\n  * Adding a pull step to the XML commit\n  * prevent infinet loops\n  * app.version=1.24.0\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Sun, 21 Feb 2021 10:38:43 -0500\n\nbowlerstudio (1.14.0) bionic; urgency=medium\n\n  * fix l;aunching bug\n  * remove the junk progress viewer\n  * kernel version change\n  * adding SVGLoad API\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Wed, 30 Dec 2020 15:32:07 -0500\n\nbowlerstudio (1.13.3) bionic; urgency=medium\n\n  * adding 1.13.3\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Sat, 26 Dec 2020 18:03:43 -0500\n\nbowlerstudio (1.13.1) bionic; urgency=medium\n\n  [ Octogonapus ]\n  * Create gradle-wrapper-validation.yml\n\n  [ Kevin Harrington ]\n  * Delete .DS_Store\n  * Delete .DS_Store\n  * Delete .DS_Store\n  * Close #169\n  * 1.5.4 start\n  * close #170\n  * close 159\n  * 1.5.5\n  * close #163\n  * close #179\n  * Better logging for #175\n  * Close #177\n  * to 1.5.7\n  * close #178\n  * to 1.5.8\n  * more robust to networless startup\n  * Loading application stable with no network\n  * update to gradle 6.2\n  * Delete readme.html\n  * unused imports\n  * 1.5.10\n  * serial update\n  * close #160\n  * only call login once\n  * JCSG\n  * kernel\n  * kernel\n  * kernel\n  * kernel\n  * updated issue reporting\n  * kernel\n  * 1.5.13\n  * clear all watched files on exit\n  * Also for #190\n  * 1.5.14\n  * #194 Catch the exception pull throws and remove the junk URL\n  * 1.5.15\n  * adding dependant API to solve #195\n  * adding the engineering units\n  * To begin with for #195 link limits to zero\n  * added to support #195\n  * Adding jog buttons and limit-zero linking for #195\n  * kernel\n  * attempting to add the gauge display #195\n  * Adding a test widget to test the link configurations\n  * Implementing the bounds on the dial gauge.\n  * fix java-bowler javadoc\n  * Moving tester app into test sources\n  * #195 Adding the ability to set limits in engineering units\n  * Adding calibration widget\n  * changing what link to look at\n  * focus on the limb root, not the links, keeps the camera orentation clean\n  * formatting\n  * adding interface to focus on affine\n  * creating the focus on affine function\n  * 1.6.1\n  * updating the Jog Widget\n  * 1.6.2\n  * Update LICENSE\n  * java bowler\n  * kernel\n  * kernel\n  * bugfix\n  * kernel\n  * adding a link gauge controller class\n  * setting the gauge at the link\n  * hanging of the gauge widget from the 3d display\n  * alligned the link gauge center\n  * Allign the link gauge limits with the devices limits\n  * kernel\n  * testing the gauge display\n  * Close #184 and only report once when the same exception is thrown.\n  * tuning the gauge display\n  * 1.6.4\n  * Close #196\n  * 1.6.4\n  * Adding an instantanious storage and retrival value\n  * Split the Mobile base jog off from the limb jog widgets\n  * Change the display format to remove the sliders that make no sense for positions.\n  * 1.6.6\n  * 1.6.7\n  * kernel\n  * ensure the target is not changed unless changed explicatly.\n  * 1.7.0\n  * move the target as adjusting the link configuration to see if\n  * 1.7.1\n  * no sliders for DH parameters\n  * Move the slider underneith the rest of the Engineering Units widget\n  * no slider for the zero value\n  * Focus the D-H widget on the link before the link being adjusted\n  * bound the jogging to the limits set in the slider\n  * Add events to jogging the angles\n  * dont zero out the rotation in the jog widget\n  * nrjavaserial\n  * kernel\n  * formatting\n  * sort the fields\n  * link slider less jumpy\n  * 1.7.2\n  * fixed the editing of an event\n  * sort the list of possible vitamins\n  * bugfix in DH widget\n  * spacing out the dh widgets\n  * correct name of A value\n  * Switching to webflow\n  * WebFlow enabled for BowlerStudio\n  * do not go into unknown state when refresh is hit after login\n  * switching to the defined scopes\n  * 1.8.1\n  * take key from the command line\n  * close #198\n  * close #175 again\n  * 1.8.2\n  * check for exceptions in the UI thread\n  * Catch and print the users exception rather than reporting it\n  * kernel\n  * only provide as opotions the link types from the userdefined area\n  * kernel\n  * pass the owner down through the menuies\n  * kernel\n  * support multible bases at the root level for comprehention\n  * select the correct base at root\n  * kernel events\n  * 1.9.0\n  * move this function into the transform set for the limb\n  * update the limb locations on set of the root transform\n  * adjusting the job speed\n  * close #200\n  * COmpressing camera control into single class\n  * move affin into contructor\n  * cleanup\n  * camera startup orentation\n  * Camera startup returned to original framing\n  * startup camera must be in UI thread\n  * 1.9.3\n  * kernel\n  * 1.9.4\n  * this fixes the default orentation\n  * removing junk api\n  * code generation section\n  * kernel\n  * kernel\n  * adding version number\n  * 1.9.6\n  * adding paralell robots to the menufactory\n  * adding parallel widget\n  * mod the paralell widget\n  * make the parallel widget work\n  * updated teh parallel widget\n  * kernel\n  * version\n  * catch exception caused by paralell groups\n  * renaming\n  * adding and removing working\n  * kernel\n  * close #212\n  * kernel\n  * 1.10.5\n  * dropping synchronized\n  * cleaning out cruft and switching PID ticks to float from integer\n  * compile\n  * DH settings more intuitive in the UI\n  * 1.11.0\n  * adding perms for deleting repos\n  * 1.12.0\n  * updating commons-io\n  * kernel\n  * remove DyIO classes\n  * Full installer release to add --force-gpu to linux installer\n  * 1.13.1\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Tue, 08 Dec 2020 15:47:24 -0500\n\nbowlerstudio (1.5.3) bionic; urgency=medium\n\n  * password manager\n  * updating kernel\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Fri, 20 Mar 2020 12:43:22 -0400\n\nbowlerstudio (1.5.2) bionic; urgency=medium\n\n  * 1.5.2\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Thu, 19 Mar 2020 15:24:46 -0400\n\nbowlerstudio (1.5.1) bionic; urgency=medium\n\n  * kernel\n  * 1.5.1\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Thu, 19 Mar 2020 14:53:57 -0400\n\nbowlerstudio (1.5.0) bionic; urgency=medium\n\n  * chasing down memory leak and found this\n  * fixing the memory leak\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Wed, 11 Mar 2020 18:19:17 -0400\n\nbowlerstudio (1.4.7) bionic; urgency=medium\n\n  * check the owner updated to not fail\n  * 1.4.6\n  * assets spoolup procedure\n  * clearing physics on exit\n  * startup with no assets working\n  * address the memory leak\n  * addressed memory leak\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Fri, 06 Mar 2020 15:42:36 -0500\n\nbowlerstudio (1.4.5) bionic; urgency=medium\n\n  * 1.4.5\n  * kernel\n  * check the device for null and print error is none is loaded\n  * close #166\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Thu, 27 Feb 2020 15:03:36 -0500\n\nbowlerstudio (1.4.4) bionic; urgency=medium\n\n  * 1.4.3\n  * close #149\n  * Refresh the menu for #148\n  * close #148\n  * Close #153\n  * 1.4.4\n  * kernel\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Mon, 10 Feb 2020 07:49:11 -0500\n\nbowlerstudio (1.4.2) bionic; urgency=medium\n\n  * kernel to close #144\n  * adding the SVG loader\n  * adding a parser for SVG\n  * 1.4.2\n  * kernel\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Tue, 04 Feb 2020 21:46:34 -0500\n\nbowlerstudio (1.4.1) bionic; urgency=medium\n\n  * remove junk exception handler\n  * close #142 Only highlight when the controller is open\n  * close #139\n  * 1.4.1\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Mon, 03 Feb 2020 18:48:44 -0500\n\nbowlerstudio (1.4.0) bionic; urgency=medium\n\n  * Stagger the opening of tabs to avoid collision\n  * Cleaned up the connection manager\n  * 1.4.0\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Sat, 01 Feb 2020 16:56:21 -0500\n\nbowlerstudio (1.3.2) bionic; urgency=medium\n\n  * removing junk\n  * #120 shoudle reso;ve bey reading the branch\n  * close #124\n  * 1.3.2\n  * close #127\n  * Close #129\n  * Add new repo in copy creature immediatly to the workspace\n  * correct crash on load when downgrading\n  * paralell loading the menu of GitHub\n  * #135 Check for null tabs\n  * #134 Catch the index out of bounds exception\n  * catch and reset teh values\n  * load devices on launch\n  * pull befor rinning both dep injectors\n  * kernel\n  * getting rid of loading scripts makes the copy process cleaner\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Fri, 31 Jan 2020 21:00:51 -0500\n\nbowlerstudio (1.3.1) bionic; urgency=medium\n\n  * Adding Kotlin\n  * tested kotlin tab working\n  * 1.3.0\n  * Close #126 By loading repos as list.\n  * kernel\n  * kernel\n  * removing stale depenancy\n  * adding 64 bit zipping\n  * adding 64 bit zipping\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Sat, 18 Jan 2020 18:15:39 -0500\n\nbowlerstudio (1.2.4) bionic; urgency=medium\n\n  * kernel\n  * build\n  * vitamins\n  * string insertion method no longer nibbles bytes off the end\n  * startup sequence with no files and no login tested working\n  * kernel\n  * close #113 By catching this stack trace and ignoring it\n  * version\n  * ensure that all new vitamins have teh required fields\n  * ensure starter data doesnt look right\n  * kernel\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Tue, 14 Jan 2020 10:45:20 -0500\n\nbowlerstudio (1.2.2) bionic; urgency=medium\n\n  * Resolve #100\n  * updating library\n  * This should address #104\n  * 1.2.0\n  * Close #106 and Close #105\n  * Catch and swallow printing exceptions Close #110\n  * Ensure #110 is handled properly in the future\n  * kernel\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Wed, 08 Jan 2020 16:35:22 -0500\n\nbowlerstudio (1.1.0) bionic; urgency=medium\n\n  * renamed the label on the git file add\n  * version bump\n  * Added a vitamins menu\n  * adding uncaught exception handlers\n  * Adding a new Vitamin wizard\n  * Adding the vitamin creation callbacks\n  * basic workflow added\n  * Basic editing of data\n  * Adding functioning New Vitamins wizard\n  * ensure that motors have all required fields\n  * Vitamins menu for editing works\n  * New vitamin wizard is working\n  * Close #98\n  * better issue reporting\n  * cleaned up codegen section\n  * sanatized configurations\n  * kernel\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Mon, 06 Jan 2020 17:45:52 -0500\n\nbowlerstudio (1.0.11) bionic; urgency=medium\n\n  * Only load menu items when the mouse enters the repository\n  * only one thread allowing for the loading of configuration data.\n  * kernel\n  * Bugfixes to address additional issues raised\n  * loading assets the proper git way\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Sat, 04 Jan 2020 16:14:59 -0500\n\nbowlerstudio (1.0.9) bionic; urgency=medium\n\n  * 1.0.9\n  * #180 This was a side effect of the branching system beginning to work.\n  * more issue reporting\n  * adding exception reporting\n  * kernel\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Sat, 04 Jan 2020 13:29:21 -0500\n\nbowlerstudio (1.0.8) bionic; urgency=medium\n\n  * close #76\n  * updated kernel\n  * menue update\n  * kernel and faster menue loading\n  * adding branches and commit listing\n  * Branch setting works\n  * removing unused imports\n  * reset commits when a commit event is detected\n  * Adding branch select and commit select\n  * removed suspicious slash\n  * safer kernel url parsing\n  * kerne\n  * 1.0.8\n  * formatting\n  * adding more complete messages to creation of branches\n  * Menu driven commits and branches\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Fri, 03 Jan 2020 14:15:24 -0500\n\nbowlerstudio (1.0.7) bionic; urgency=medium\n\n  * loading the name of the device from the link\n  * cleaner launching scripts\n  * adding the updating code for the launcher\n  * kernel\n  * catch uncaught exception\n  * Repaired the copy a creature feature\n  * kernel\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Mon, 30 Dec 2019 01:46:02 -0500\n\nbowlerstudio (1.0.6) bionic; urgency=medium\n\n  * Update README.md\n  * Create FUNDING.yml\n  * Update FUNDING.yml\n  * Update README.md\n  * Create COC.md\n  * Update FUNDING.yml\n  * Update FUNDING.yml\n  * Update FUNDING.yml\n  * Update FUNDING.yml\n  * Update FUNDING.yml\n  * Update FUNDING.yml\n  * Update FUNDING.yml\n  * Update README.md\n  * Update README.md\n  * more robust error handeling\n  * high level error handeling\n  * Update README.md\n  * Update README.md\n  * kernel\n  * Update README.md\n  * Update README.md\n  * Create a new creature with sub directories and loader script\n  * error handling\n  * adding issue reporting\n  * spelling\n  * deep copy of creatures tested working\n  * 1.0.6\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Sun, 29 Dec 2019 15:52:58 -0500\n\nbowlerstudio (1.0.5) bionic; urgency=medium\n\n  * removing offending clones and ensure only one pull thread at a time\n  * add the tutorial git to the configurationDatabase\n  * 1.0.5\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Sun, 29 Dec 2019 00:53:30 -0500\n\nbowlerstudio (1.0.4) bionic; urgency=medium\n\n  * add exception reporting to close #50\n  * Adding code for #50\n  * Storing the tutorial brancha nd base url in the configurations database\n  * 1.0.4\n  * kernel\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Sat, 28 Dec 2019 23:41:27 -0500\n\nbowlerstudio (1.0.3) bionic; urgency=medium\n\n  * kernel\n  * Commented code for computing the total number of verticies\n  * stable save of configurations\n  * remove redundant pulls\n  * Device manager to the left of terminal, not tabbed with it\n  * cleaner disconnectiong of devices and closing of stale configuration window\n  * removed the dhlab plugin form the mobile base\n  * Remove the MobileBase UI and cad from BowlerStudio on disconnect\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Fri, 27 Dec 2019 16:42:08 -0500\n\nbowlerstudio (1.0.2) bionic; urgency=medium\n\n  * javadoc fix\n  * test fixed\n  * select all items made by a line of code rather than just the one\n  * 1.0.2\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Sun, 22 Dec 2019 15:29:23 -0500\n\nbowlerstudio (1.0.1) bionic; urgency=medium\n\n  * thread safing the clear of highlights\n  * THread safing the selection code\n  * thread-safing the Device manager\n  * #68\n  * restructure to make the location of the 3d windo more sane\n  * exception caught, but no resolution\n  * removing the event listener\n  * attempt to remove the scene\n  * just catch the exception for now\n  * exit on error\n  * refactoring to chase down #68\n  * kernel\n  * nrToAffine should be in a UI thread\n  * change the jog widget to use the set all joints method #68\n  * set the affine from the UI thread\n  * move the affine set into the platform runlater #68\n  * Updating kernel for #68\n  * Close #68\n  * code cleanup in jog widget\n  * version 1.0.0 released!\n  * 1.0.1\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Sun, 22 Dec 2019 13:07:17 -0500\n\nbowlerstudio (0.36.1) bionic; urgency=medium\n\n  * print statements\n  * removing print statements\n  * adding a public removeObjects to the static calls\n  * adding public API for selecting a CSG\n  * revbump\n  * #68 This may resolve the issue, but extensive testing is needed\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Sat, 21 Dec 2019 13:26:53 -0500\n\nbowlerstudio (0.36.0) bionic; urgency=medium\n\n  * adding git@ uri support\n  * updating jgit\n  * rev bump kernel\n  * udpating jgit\n  * removing the clone conflict loop\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Thu, 19 Dec 2019 14:10:11 -0500\n\nbowlerstudio (0.35.1) bionic; urgency=medium\n\n  * kernel updated\n  * version bump\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Wed, 18 Dec 2019 18:40:32 -0500\n\nbowlerstudio (0.35.0) bionic; urgency=medium\n\n  * kernel\n  * kernel\n  * kernel\n  * new version\n  * #67 Default to virtual if no other link type exist.\n  * close #66\n  * compileing\n  * moving the make a copy button\n  * added the mass adjust widget\n  * loading the ass of the robots body\n  * close #64\n  * adding a transform widget for the IMU centroid\n  * revbump\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Wed, 18 Dec 2019 17:24:31 -0500\n\nbowlerstudio (0.34.1) bionic; urgency=medium\n\n  * password manager\n  * kernel\n  * 0.34.1\n  * Fixed the logout cleanup\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Thu, 28 Nov 2019 13:09:01 -0500\n\nbowlerstudio (0.34.0) bionic; urgency=medium\n\n  * kernel\n  * kernel\n  * kernel\n  * adding 2 factor authentication\n  * updated the version number\n  * close #6\n  * tagged the kernel update\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Wed, 27 Nov 2019 17:44:44 -0500\n\nbowlerstudio (0.33.3) bionic; urgency=medium\n\n  * cleaned catching errors\n  * 0.33.2\n  * switching to new github api\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Thu, 15 Aug 2019 16:20:25 -0400\n\nbowlerstudio (0.33.1) bionic; urgency=medium\n\n  * pull password manager from library\n  * kernel runtime bug\n  * adding exception handeling to button clicks\n  * all limbs homed to zero degrees directly\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Tue, 06 Aug 2019 20:52:54 -0400\n\nbowlerstudio (0.33.0) bionic; urgency=medium\n\n  [ Kevin Harrington ]\n  * changing default physics compute time\n\n  [ Technocopia PC 01 ]\n  * jcsg\n\n  [ Kevin Harrington ]\n  * Switching over to password manager in kernel\n  * new password manager\n  * updated password loading\n  * move the delete function to the kernel wher it belongs\n  * 0.33.0\n  * print on login\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Sun, 28 Jul 2019 16:50:30 -0400\n\nbowlerstudio (0.32.7) bionic; urgency=medium\n\n  * kernel\n  * merge\n  * clear the text string\n  * Robust loading when uplink is unavailible\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Mon, 22 Jul 2019 20:45:29 -0400\n\nbowlerstudio (0.32.6) bionic; urgency=medium\n\n  * do not add degenerate files to the configurations\n  * when encountering an erronious file, remove from configs in the future and keep going.\n  * 0.32.6\n  * kernel\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Sat, 06 Jul 2019 11:15:48 -0400\n\nbowlerstudio (0.32.5) bionic; urgency=medium\n\n  * kernel\n  * auto-pull latest tutorials\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Fri, 05 Jul 2019 12:57:42 -0400\n\nbowlerstudio (0.32.4) bionic; urgency=medium\n\n  [ Kevin Harrington ]\n  * cleanup\n  * fixing the scrilling freez issue\n\n  [ hephaestus ]\n  * resolved the most recent macos issue\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Fri, 28 Jun 2019 03:27:34 -0400\n\nbowlerstudio (0.32.3) bionic; urgency=medium\n\n  * version\n  * only refesh on mouse entry\n  * 0.32.3\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Mon, 24 Jun 2019 09:36:08 -0400\n\nbowlerstudio (0.32.1) bionic; urgency=medium\n\n  * more robust focus grabbing on mouse entered\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Mon, 24 Jun 2019 09:02:32 -0400\n\nbowlerstudio (0.32.0) bionic; urgency=medium\n\n  * Fixed docking of window to a very small size\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Mon, 24 Jun 2019 07:33:08 -0400\n\nbowlerstudio (0.31.9) bionic; urgency=medium\n\n  [ Technocopia PC 01 ]\n  * when a file is added or the git us pulled, refresh menu\n  * kernel update\n\n  [ Kevin Harrington ]\n  * better javafx initializer\n  * javafx initializer\n  * removing javafx launching via depricated means\n  * kernel\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Sat, 22 Jun 2019 20:47:44 -0400\n\nbowlerstudio (0.31.8) bionic; urgency=medium\n\n  * loading the new tab into workspace\n  * thread wrap loading the tab into the workspace\n  * restructure the menu loading\n  * restructured the workspace manager\n  * 0.31.8\n  * allow for non-blocking menu loading\n  * load menue with known message\n  * kernel\n  * if the rank changes, save\n  * stable rank order, and refresh of UI only when the rank changes\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Fri, 21 Jun 2019 18:05:31 -0400\n\nbowlerstudio (0.31.7) bionic; urgency=medium\n\n  * when no message is given, use the file name\n  * 0.31.7\n  * login event for configurations\n  * re-load the configurations database on login\n  * larger max workspace\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Fri, 21 Jun 2019 12:25:07 -0400\n\nbowlerstudio (0.31.6) bionic; urgency=medium\n\n  * Closes #57 Adding files to empty repos\n  * adding basic adding feature\n  * adding calls to the workspace manager\n  * adding workspace manager\n  * calls to workspace manager\n  * null check\n  * adding file open windows to the workspace menu\n  * set up the files after pull\n  * change it to pull\n  * add repo to workspace button\n  * bugfix\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Thu, 20 Jun 2019 18:31:49 -0400\n\nbowlerstudio (0.31.4) bionic; urgency=medium\n\n  * catch the exception in the lambda\n  * Fix the flicker\n  * delay the print to ensure that the window loads after the web tab loads\n  * 0.31.4\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Thu, 20 Jun 2019 00:47:16 -0400\n\nbowlerstudio (0.31.3) bionic; urgency=medium\n\n  * Loading an image as the tray icon for the splash\n  * 0.31.3\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Wed, 19 Jun 2019 18:09:17 -0400\n\nbowlerstudio (0.31.2) bionic; urgency=medium\n\n  * kernel\n  * more robust refeshing of tab panel\n  * build 0.31.2\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Wed, 19 Jun 2019 15:58:56 -0400\n\nbowlerstudio (0.31.1) bionic; urgency=medium\n\n  * working marytts jar\n  * ignore\n  * kernel\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Tue, 18 Jun 2019 12:17:28 -0400\n\nbowlerstudio (0.31.0) bionic; urgency=medium\n\n  * minimized flicker\n  * cleanup\n  * editable\n  * minimal needed for refresh\n  * cleanup\n  * changing the speach kernel\n  * ignore\n  * text to spech update\n  * 0.31.0\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Tue, 18 Jun 2019 10:53:09 -0400\n\nbowlerstudio (0.30.5) bionic; urgency=medium\n\n  * #63\n  * #63 adding individual invokelaters and a retry loop with timing gating\n  * close #63\n  * 0.30.5\n  * Removing the flashing for #63\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Mon, 17 Jun 2019 13:15:37 -0400\n\nbowlerstudio (0.30.4) bionic; urgency=medium\n\n  * UI loading in kernel\n  * updated travis\n  * setting the modular frame on loading\n  * 0.30.4\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Sun, 16 Jun 2019 14:41:45 -0400\n\nbowlerstudio (0.30.3) bionic; urgency=medium\n\n  * freetts\n  * defaulting the CAD manager to config mode\n  * 0.30.3\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Mon, 10 Jun 2019 16:01:36 -0400\n\nbowlerstudio (0.30.2) bionic; urgency=medium\n\n  * adding obj export now that obj string bug is fixed\n  * 0.30.2\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Sun, 02 Jun 2019 12:24:54 -0400\n\nbowlerstudio (0.30.1) xenial; urgency=medium\n\n  [ hephaestus ]\n  * xenial\n\n  [ Kevin Harrington ]\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Fri, 31 May 2019 08:52:58 -0400\n\nbowlerstudio (0.30.0) xenial; urgency=medium\n\n  * Added parabola\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Fri, 31 May 2019 08:01:51 -0400\n\nbowlerstudio (0.29.8) xenial; urgency=medium\n\n  [ hephaestus ]\n  * xenial\n\n  [ Kevin Harrington ]\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Thu, 30 May 2019 16:31:16 -0400\n\nbowlerstudio (0.29.7) xenial; urgency=medium\n\n  * kernel updates\n  * 0.29.7\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Thu, 30 May 2019 09:35:12 -0400\n\nbowlerstudio (0.29.6) xenial; urgency=medium\n\n  * null checks on the interface frame\n  * wait for splash to be ready before returning\n  * serialVersionUID\n  * 0.29.6\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Mon, 20 May 2019 19:50:35 -0400\n\nbowlerstudio (0.29.5) xenial; urgency=medium\n\n  * kernel\n  * 0.29.5 start\n  * updating the hardware setting menues\n  * formatting\n  * Prevent fault on null passed here\n  * formatting\n  * regenerate on link configuration changes\n  * sanatize text input\n  * Remember the open file and open another from the same directory\n  * bound the link setting\n  * inter-connecting the bounding to ensure a slider never shows invalid data\n  * kernel\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Sun, 19 May 2019 21:39:00 -0400\n\nbowlerstudio (0.29.4) xenial; urgency=medium\n\n  * moving splash screen to a manager class\n  * 0.29.4-dev\n  * Adding a psudo-splash screen using Jframe\n  * set font after loading tab to prevent NPE\n  * java-bowler update\n  * Re-display slash on closing to indicate saving state\n  * Adding toggle buttons for config mode\n  * adding a cad configuration mode\n  * Stable loading of tabs\n  * Better defaults for jog widget\n  * tab loading failure reporting\n  * Set the font with a delay and a fail-over loop in the tab\n  * if spash availible, use the better one\n  * use the same splash for all\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Sat, 18 May 2019 02:43:48 -0400\n\nbowlerstudio (0.29.3) xenial; urgency=medium\n\n  [ Kevin Harrington ]\n  * Update README.md\n  * 0.29.3\n  * jcsg\n\n  [ McKinney ]\n  * Adding the kinematics-chef\n  * Adding the kinematics-chef\n  * Adding the kinematics-chef\n\n  [ Kevin Harrington ]\n  * from compile to implementation\n  * updating gradle\n  * travis update\n  * removing kinematics chef source\n  * kinematics chef submodule\n  * adding the maven switch-over\n  * maven version of kinematicsChef working\n  * removing kinematics chef untill the circular kernel dep is resolved.\n  * added back KC after modifying the depencies\n  * removing kc again\n  * adjusting dh widget\n  * remove kinematics\n  * Fixing the \"double enter\" bug\n  * Correcting the error of file tabs loading and interfearing with each other at boot time\n  * no silent fails\n  * formatting\n  * return logical bounds\n  * Update .gitmodules\n  * Update README.md\n  * Update README.md\n  * removing submodule\n  * updating submodules\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Wed, 15 May 2019 17:29:28 -0400\n\nbowlerstudio (0.29.2) xenial; urgency=medium\n\n  * 0.35.2\n  * 0.29.2\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Sun, 06 Jan 2019 16:07:15 -0500\n\nbowlerstudio (0.29.0) xenial; urgency=medium\n\n  * =Updating gradle\n  * updated kernel\n  * removing OpenCV\n  * moving dockfx off server\n  * Updaing build\n  * Updaing build\n  * gradle version\n  * tests working\n  * remove test\n  * comments\n  * reove javadoc violations\n  * Update README.md\n  * merging\n  * removed opencv components\n  * fixing travis\n  * javadoc fix\n  * fixed travis\n  * removing the opencv from the build tree\n  * updating JCSG\n  * 0.29.0\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Sun, 06 Jan 2019 14:17:56 -0500\n\nbowlerstudio (0.28.1) xenial; urgency=medium\n\n  * 0.28.1\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Wed, 18 Jul 2018 17:14:47 -0400\n\nbowlerstudio (0.28.0) xenial; urgency=medium\n\n  * jcsg\n  * 0.28.0\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Wed, 18 Jul 2018 15:25:23 -0400\n\nbowlerstudio (0.27.6) xenial; urgency=medium\n\n  * Change from exception to throwable dispalys and highlights\n  * easier clearing of the highlights\n  * Avoud competition with the click to highlight\n  * kernel\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Sat, 07 Jul 2018 19:02:21 -0400\n\nbowlerstudio (0.27.5) xenial; urgency=medium\n\n  * Better loading of exceptions and SVG manipulations\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Sat, 07 Jul 2018 11:52:27 -0400\n\nbowlerstudio (0.27.4) xenial; urgency=medium\n\n  * bugfix\n  * 0.27.4\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Thu, 05 Jul 2018 18:17:00 -0400\n\nbowlerstudio (0.27.3) xenial; urgency=medium\n\n  * Adding the version number to the splash screen\n  * error out on 32bit systems\n  * Arror message on 32 bit mode JVM in UI\n  * Gracefull loading of unknown links\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Thu, 05 Jul 2018 16:51:30 -0400\n\nbowlerstudio (0.27.2) xenial; urgency=medium\n\n  * rev bump\n  * Massivly faster slicer\n  * kernel\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Thu, 05 Jul 2018 13:59:57 -0400\n\nbowlerstudio (0.27.1) xenial; urgency=medium\n\n  * kernel\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Fri, 29 Jun 2018 10:15:26 -0400\n\nbowlerstudio (0.27.0) xenial; urgency=medium\n\n  * kernel\n  * kernel\n  * usinf the javacad sources\n  * 0.27.0\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Thu, 28 Jun 2018 15:08:24 -0400\n\nbowlerstudio (0.26.2) xenial; urgency=medium\n\n  * updating java-bowler\n  * java -bowler\n  * java-bowler\n  * Java-bowler to submodule\n  * Java-bowler to submodule\n  * Adding java-bowler submodule\n  * addign the java-bowler build to the final build\n  * Rmoving submodule\n  * Rmoving submodule\n  * Compiling with 2 submodules\n  * build with submodule\n  * Adding the auto-wrapping of objects that act like devices with a connect and disconnect method\n  * java-bowler\n  * java-bowler\n  * move filter to java-bowler\n  * formatting and use DMDevice\n  * device loading more efficient for scripts\n  * working with the fix for mobile base updates\n  * builds as jar\n  * fixed the drifting window issue\n  * kernel\n  * kernel\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Mon, 25 Jun 2018 10:02:48 -0400\n\nbowlerstudio (0.26.1) xenial; urgency=medium\n\n  [ hephaestus ]\n  * xenial\n\n  [ Kevin Harrington ]\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Sun, 03 Jun 2018 16:44:12 -0400\n\nbowlerstudio (0.26.0) xenial; urgency=medium\n\n  * 0.26.0\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Sun, 03 Jun 2018 16:25:06 -0400\n\nbowlerstudio (0.25.9) xenial; urgency=medium\n\n  * 0.25.8\n  * Build archive working\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Sat, 02 Jun 2018 22:41:43 -0400\n\nbowlerstudio (0.25.7) xenial; urgency=medium\n\n  * Filter devices for existance in the device manager already\n  * use the built in UI with filter\n  * bugfix\n  * kernel\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Mon, 28 May 2018 21:25:23 -0400\n\nbowlerstudio (0.25.6) xenial; urgency=medium\n\n  * slower jogging\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Sun, 27 May 2018 19:49:40 -0400\n\nbowlerstudio (0.25.5) xenial; urgency=medium\n\n  * kernel\n  * 0.25.5\n  * process percentage realistic\n  * kernel\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Fri, 25 May 2018 22:50:28 -0400\n\nbowlerstudio (0.25.4) xenial; urgency=medium\n\n  [ hephaestus ]\n  * xenial\n\n  [ Kevin Harrington ]\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Tue, 22 May 2018 20:10:39 -0400\n\nbowlerstudio (0.25.3) xenial; urgency=medium\n\n  * kernel\n  * 0.25.2\n  * kernel\n  * 0.25.3\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Thu, 17 May 2018 14:12:09 -0400\n\nbowlerstudio (0.25.1) xenial; urgency=medium\n\n  * moving lower bound to 0\n  * kernel update\n  * fixing the cold-start issue for the image assets\n  * kernel\n  * getter vor camera view\n  * User draw modes for wireframe\n  * removed stale code\n  * stale imports\n  * moving MobileBaseCadManager and FIle Watcher into kernel\n  * updating kernel to use abstracted interface\n  * Moving script loading of movbile bases into kernel\n  * implementing updated API\n  * library update\n  * 0.31.0 kernel\n  * ordering for clarity\n  * updating kernel\n  * kernel\n  * updated library\n  * movign git file copy to kernel\n  * loading messages more accurate\n  * use javacad intermediate jar in kernel\n  * updating JavaCad\n  * catch exceptions of hotfixes\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Tue, 09 Jan 2018 23:57:10 -0500\n\nbowlerstudio (0.25.0) xenial; urgency=medium\n\n  * updaing kernel to one that builds in travis\n  * Updaing gradle permissions\n  * adding the opencv depenancies\n  * same travis as working kernel\n  * update travis\n  * try xenial verison\n  * command line flags for quiet\n  * adding connecting line to the polygon display\n  * updating the SVGFactory to call the built in slicer\n  * Filter out the csgDatabase.json file from the gist run\n  * adding JCSG with new slicer\n  * adding a cad EXPORT path\n  * javacad update\n  * updating the loading of objects to manufacturing files\n  * updating JCSG\n  * adding the hotfix script\n  * updating the syntax highlighting\n  * never edit the csgDatabse.json\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Wed, 29 Nov 2017 18:56:53 -0500\n\nbowlerstudio (0.24.0) xenial; urgency=medium\n\n  * Updating a database value if found in the root of a repository.\n  * FIxing the file watcher file state upate.\n  * updating JavaCad\n  * using CSG names to make STL files\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Tue, 14 Nov 2017 21:59:50 -0500\n\nbowlerstudio (0.23.1) xenial; urgency=medium\n\n  * Fixing the bounding and rotation problem in the Transform widget when close to Euler singularities.\n  * 0.23.1\n  * Engineering units sliders should be more sane about the ticks\n  * adding the place limb widget to the menu\n  * Making the Transform widget more readable\n  * exception handeling on file selection\n  * only auto-highlight when the check box is checked\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Wed, 18 Oct 2017 14:55:40 -0400\n\nbowlerstudio (0.23.0) xenial; urgency=medium\n\n  * convert lines to cylindar\n  * make thicknesses match the line equivalent\n  * smaller stroke\n  * dump all the STL;s into a single directory as single parts\n  * stagger teh loading to avoid collisions\n  * deal with the inititail case\n  * Much improved progress monitor for cad generation\n  * 0.22.1\n  * Adding a menu item to generate Kinematic STL's\n  * adding the link number into the file name\n  * Adding the link to CSG mapper as a function making teh export of named files easier.\n  * Update ISSUE_TEMPLATE.md\n  * Update ISSUE_TEMPLATE.md\n  * Tutorial loading of GIT should happen outside of the internal thread to ensure loaded git before moving on\n  * more testing of vitamins\n  * updating to 0.23.0\n  * Loading file from gist in web was broken\n  * Ensure new links have a type\n  * Loading link of non-zero radius\n  * type and type string need to beset, this should be handled by the library\n  * when the hardware limits are reset, the slider for the link should be reset\n  * Text field should auto size to the file and Git url\n  * compute the box size using the font\n  * carrot moves to the start of a file\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Sun, 01 Oct 2017 15:05:02 -0400\n\nbowlerstudio (0.21.3) xenial; urgency=medium\n\n  [ Kevin Harrington ]\n  * adding fonts menues\n  * setting the font size is working\n  * cleaner creature lab menues\n  * adding menu option to hide objects in assemblies\n\n  [ Student ]\n  * Cleaning the loading path in MacOS\n  * cleaner fault state launches\n  * setting the stylesheet after the platform is shown\n  * try catch around user agent\n  * slower camera spin\n  * updateing dockfx to 12\n  * moving the Application stylesheet loading\n  * fiddeling with the loading of the user agent sheets\n\n  [ Kevin Harrington ]\n  * converting axis to lines not boxes\n  * 3d line generation\n  * 3d line interface\n  * more complete removal of stale objects\n  * include polygon to the removel datatypes\n  * adding smoother lines and sharper end terminaion\n  * adding the polygon to wireframe conversion to add the manipulator\n  * inset the axis\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Mon, 17 Jul 2017 11:46:13 -0400\n\nbowlerstudio (0.21.2) xenial; urgency=medium\n\n  [ Kevin Harrington ]\n  * 0.21.2 update\n  * updating kernel\n\n  [ Octogonapus ]\n  * Removed AssetFactory from local src and importing the version from the kernel instead\n\n  [ Kevin Harrington ]\n  * updating java-bowler\n  * using the studio build info from the kernel\n  * load a mobile base form git\n  * Cleanly catching the caso of generated class\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Tue, 11 Jul 2017 09:49:54 -0400\n\nbowlerstudio (0.21.1) xenial; urgency=medium\n\n  * updating the default creature URL\n  * updating kernel\n  * updating kernel\n  * removing excessive prints\n  * stagerring code loading\n  * removing the CSG pringing\n  * pulling repos rather thatn loading a file\n  * moving the physics engine into the kernel\n  * adding the print and println function to bowlerstudio static api\n  * Adding the permutations of the call\n  * changing to standard thread\n  * updating kernel\n  * updating kernel version\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Wed, 10 May 2017 08:59:01 -0400\n\nbowlerstudio (0.21.0) xenial; urgency=medium\n\n  * adding additional libraries to the imports\n  * removing synchronization in any UI accessed threads\n  * updating javacad\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Thu, 04 May 2017 09:52:30 -0400\n\nbowlerstudio (0.20.9) xenial; urgency=medium\n\n  * removing junk annotation\n  * updating the kernel to use branches\n  * removing hard coded \"master\"\n  * making the tutorial access master rather than the default \"source\"\n  * updaing kernel\n  * updating kernel\n  * Updating JCSG\n  * 0.20.9\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Fri, 28 Apr 2017 00:25:03 -0400\n\nbowlerstudio (0.20.8) xenial; urgency=medium\n\n  [ Kevin Harrington ]\n  * switching the internal loader of ht emenue to the offical repo\n\n  [ Kevin Harrington ]\n  * ignoring eclipse project files\n  * surpressing restricted api warnings\n\n  [ Kevin Harrington ]\n  * Adding the git submodule instrunctions\n  * null manufacturing call should just not add the part, not fail\n  * when an object is sent to manufacturing, show the new one\n  * setting the controllers carefully\n  * updating the verion to 0.20.8\n\n  [ harrington ]\n  * fixing the build on windows\n  * load JNI on windows from correct location\n\n  [ Kevin Harrington ]\n  * fixing jni\n  * windows build should be working now\n  * #21 Move from overlays to tab\n  * #21 removing the overlays entirely\n  * moving controls into a new tab #21\n  * Anchoring the treeview to the archor panel #21\n  * anchor the tree\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Sun, 26 Mar 2017 21:26:08 -0400\n\nbowlerstudio (0.20.7) xenial; urgency=medium\n\n  * 14.04 instructions update\n  * Update README.md\n  * updating the tutorials URL at boot time\n  * updating bowler kernel\n  * new version\n  * updating kernel\n  * updating the URL's\n  * removing the exception printing\n  * update javacad\n  * 0.20.7\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Mon, 06 Mar 2017 11:50:52 -0500\n\nbowlerstudio (0.20.6) xenial; urgency=medium\n\n  * 14.04 instructions update\n  * Update README.md\n  * updating the tutorials URL at boot time\n  * updating bowler kernel\n  * new version\n  * updating kernel\n  * updating the URL's\n  * removing the exception printing\n  * update javacad\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Mon, 06 Mar 2017 11:13:57 -0500\n\nbowlerstudio (0.20.5) xenial; urgency=medium\n\n  * adding loading progress info\n  * splash loading working\n  * more loading info\n  * public splash rendering\n  * leaving only the print stream in the static declaration\n  * Text should only be appended when there is text to append.\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Fri, 10 Feb 2017 22:58:23 -0500\n\nbowlerstudio (0.20.4) xenial; urgency=medium\n\n  * Wider margin for cancle double click\n  * buffered output stream for log with tail clipping\n  * reduce the memory footprint of the camera spin.\n  * higher efficency camera\n  * making the camera use static objects wherever possible\n  * Load the ASSETS FORM THE CORRECT REPOSITORY\n  * force the mainline version in when the version updates\n  * save changes when they are made to the configurations\n  * changing asset print\n  * deeper read-back buffer\n  * moving the garbage collect to the start side\n  * Auto spin should not be enabled by default\n  * attempting to run the swingnode bounds\n  * more robust rerendering\n  * moving kernel to submodule\n  * compile the submodule version of kernel\n  * Adding imports\n  * adding the vehicle update\n  * return on any error\n  * using wheel position as opposed to its delta\n  * adding dependancies\n  * fixed the import of kernel jars\n  * removing old splash\n  * adding new splash\n  * cleaning up image assets\n  * upating javacad\n  * updating javacad\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Mon, 23 Jan 2017 20:33:04 -0500\n\nbowlerstudio (0.20.3) xenial; urgency=medium\n\n  * Reverse the interpolation for smoother transition\n  * removing the cancel from all clicks\n  * fixing the cancle detection\n  * adding an acceleration to the camera spin\n  * the sin scaling should be across Pi/2\n  * Sinusoidal scaling is now working\n  * slower spin interpolation\n  * Basif fin and replace widgit loading on ctrl f signel\n  * close #17 Find Replace Widget\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Mon, 02 Jan 2017 00:13:49 -0500\n\nbowlerstudio (0.20.2) xenial; urgency=medium\n\n  [ Kevin Harrington ]\n  * 0.21.1\n  * removing junk files\n  * Changing the use of the file watcher\n  * Moving change watcher inot local code to ensure thread management works with the javafx conventions\n  * Update to single thread per file watchers\n  * After much hunting i think the magic Yo is gone\n  * Request focus is needed even with new RSyntax\n  * remove the debug hook\n  * Close #2\n  * Adding blocks to elevation to conform to the bounding standards set by the rotation object.\n  * updateing the transform object to Apache-commons-math3\n  * The camera rotation cna not be a singularity!\n  * catch any exception and clear gits\n  * Updating the kernel to use more strict java-bowle rotation bounding.\n  * Adding a vehicle wrapper to the control class\n  * adding a wheel wrapper\n  * Fixing the link widjet so it displays current value\n  * adding a weel module to the physics engine\n  * putting the physics components back intot he update wrapping function\n  * Adding the looping mechanism\n  * move wheel updates into mobile base physics for user control\n  * use the wheel transform to compute the location of the new wheel.\n  * only add and remove ridgid bodys if they are not parts of vehicles.\n  * adding the rotation test for the conversion to bullet and affine\n\n  [ Octogonapus ]\n  * Added way to change asset repo #9\n\n  [ Kevin Harrington ]\n  * CSG rotation to CSG was broken and now works\n  * Attempting to update the camera, WIP\n  * 0.20.2\n  * Adding a mechanism to clear out the assets based on the current version check\n  * updated the script access to the camera adjustments\n  * load the URL from the database for assets before loading asset files\n  * default value\n  * Verified that the asset change repo is working\n  * Updating Java-Bowler\n  * Use ZYX order for the tests\n  * making the degenerate angle of all 0 is displayed as all 0\n  * Camera rotation for the new rontation schema has been fully integrated\n  * updated the kernel with robust angle error checking\n  * Switched the rotation button action\n  * Fixing the transform orentation hack\n  * Adding focus interpolation\n  * smoother camer transitions\n  * Smooth camera transition between manipulated scgs and regular csgs.\n  * simpler no-transform loading\n  * remove the interpolator\n  * by moving the motions into the removal, the transitions are a bit smoother.\n  * smooth transition for selection from a file\n  * Assing the starting camera rotation back in\n  * The x and y rotations needed inversion to be constistant with the rest of the rotations.\n  * Updating kernel to include rotation with qutaurning values swapped\n  * removing work around\n  * Auto spin the camera when the mouse input goes idle for 10 seconds. A user check box disables this\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Sat, 31 Dec 2016 11:27:26 -0500\n\nbowlerstudio (0.20.0) xenial; urgency=medium\n\n  [ Kevin Harrington ]\n  * 0.19.6\n\n  [ Kevin Harrington ]\n  * catch error s and exceptions\n  * cleanup\n  * clean warnings\n  * The zoom level and enable/disable grid\n  * Adding the axis to the hide feature\n  * Adding the ability for scripts to display to 3d window\n  * Loading of image nodes ttested working\n  * when an image is loaded the creature lab should display\n  * Faster turn over for loading creaturelav\n  * if any other exceptiuon besides the expected one comes up, print right away\n  * Adding the ability to clear the screen\n  * Making the zoom scale by distance to avoid jumpy navigation\n  * remove print\n  * Exiting the application after clearing cache\n  * Using the open flag to prevent multiple openings\n  * Reversing the motion directions\n  * Adding the bezier extrude\n  * Fixing the JavaCad depancy tree.\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Tue, 29 Nov 2016 23:19:44 -0500\n\nbowlerstudio (0.20.0) xenial; urgency=medium\n\n  [ Kevin Harrington ]\n  * 0.19.6\n\n  [ Kevin Harrington ]\n  * catch error s and exceptions\n  * cleanup\n  * clean warnings\n  * The zoom level and enable/disable grid\n  * Adding the axis to the hide feature\n  * Adding the ability for scripts to display to 3d window\n  * Loading of image nodes ttested working\n  * when an image is loaded the creature lab should display\n  * Faster turn over for loading creaturelav\n  * if any other exceptiuon besides the expected one comes up, print right away\n  * Adding the ability to clear the screen\n  * Making the zoom scale by distance to avoid jumpy navigation\n  * remove print\n  * Exiting the application after clearing cache\n  * Using the open flag to prevent multiple openings\n  * Reversing the motion directions\n  * Adding the bezier extrude\n  * Fixing the JavaCad depancy tree.\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Tue, 29 Nov 2016 23:15:58 -0500\n\nbowlerstudio (0.20.0) xenial; urgency=medium\n\n  [ Kevin Harrington ]\n  * 0.19.6\n\n  [ Kevin Harrington ]\n  * catch error s and exceptions\n  * cleanup\n  * clean warnings\n  * The zoom level and enable/disable grid\n  * Adding the axis to the hide feature\n  * Adding the ability for scripts to display to 3d window\n  * Loading of image nodes ttested working\n  * when an image is loaded the creature lab should display\n  * Faster turn over for loading creaturelav\n  * if any other exceptiuon besides the expected one comes up, print right away\n  * Adding the ability to clear the screen\n  * Making the zoom scale by distance to avoid jumpy navigation\n  * remove print\n  * Exiting the application after clearing cache\n  * Using the open flag to prevent multiple openings\n  * Reversing the motion directions\n  * Adding the bezier extrude\n  * Fixing the JavaCad depancy tree.\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Tue, 29 Nov 2016 23:14:05 -0500\n\nbowlerstudio (0.20.0) xenial; urgency=medium\n\n  [ Kevin Harrington ]\n  * 0.19.6\n\n  [ Kevin Harrington ]\n  * catch error s and exceptions\n  * cleanup\n  * clean warnings\n  * The zoom level and enable/disable grid\n  * Adding the axis to the hide feature\n  * Adding the ability for scripts to display to 3d window\n  * Loading of image nodes ttested working\n  * when an image is loaded the creature lab should display\n  * Faster turn over for loading creaturelav\n  * if any other exceptiuon besides the expected one comes up, print right away\n  * Adding the ability to clear the screen\n  * Making the zoom scale by distance to avoid jumpy navigation\n  * remove print\n  * Exiting the application after clearing cache\n  * Using the open flag to prevent multiple openings\n  * Reversing the motion directions\n  * Adding the bezier extrude\n  * Fixing the JavaCad depancy tree.\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Tue, 29 Nov 2016 23:07:07 -0500\n\nbowlerstudio (0.20.0) xenial; urgency=medium\n\n  [ Kevin Harrington ]\n  * 0.19.6\n\n  [ Kevin Harrington ]\n  * catch error s and exceptions\n  * cleanup\n  * clean warnings\n  * The zoom level and enable/disable grid\n  * Adding the axis to the hide feature\n  * Adding the ability for scripts to display to 3d window\n  * Loading of image nodes ttested working\n  * when an image is loaded the creature lab should display\n  * Faster turn over for loading creaturelav\n  * if any other exceptiuon besides the expected one comes up, print right away\n  * Adding the ability to clear the screen\n  * Making the zoom scale by distance to avoid jumpy navigation\n  * remove print\n  * Exiting the application after clearing cache\n  * Using the open flag to prevent multiple openings\n  * Reversing the motion directions\n  * Adding the bezier extrude\n  * Fixing the JavaCad depancy tree.\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Tue, 29 Nov 2016 22:35:28 -0500\n\nbowlerstudio (0.20.0) xenial; urgency=medium\n\n  [ Kevin Harrington ]\n  * 0.19.6\n\n  [ Kevin Harrington ]\n  * catch error s and exceptions\n  * cleanup\n  * clean warnings\n  * The zoom level and enable/disable grid\n  * Adding the axis to the hide feature\n  * Adding the ability for scripts to display to 3d window\n  * Loading of image nodes ttested working\n  * when an image is loaded the creature lab should display\n  * Faster turn over for loading creaturelav\n  * if any other exceptiuon besides the expected one comes up, print right away\n  * Adding the ability to clear the screen\n  * Making the zoom scale by distance to avoid jumpy navigation\n  * remove print\n  * Exiting the application after clearing cache\n  * Using the open flag to prevent multiple openings\n  * Reversing the motion directions\n  * Adding the bezier extrude\n  * Fixing the JavaCad depancy tree.\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Tue, 29 Nov 2016 22:28:22 -0500\n\nbowlerstudio (0.20.0) xenial; urgency=medium\n\n  [ Kevin Harrington ]\n  * 0.19.6\n\n  [ Kevin Harrington ]\n  * catch error s and exceptions\n  * cleanup\n  * clean warnings\n  * The zoom level and enable/disable grid\n  * Adding the axis to the hide feature\n  * Adding the ability for scripts to display to 3d window\n  * Loading of image nodes ttested working\n  * when an image is loaded the creature lab should display\n  * Faster turn over for loading creaturelav\n  * if any other exceptiuon besides the expected one comes up, print right away\n  * Adding the ability to clear the screen\n  * Making the zoom scale by distance to avoid jumpy navigation\n  * remove print\n  * Exiting the application after clearing cache\n  * Using the open flag to prevent multiple openings\n  * Reversing the motion directions\n  * Adding the bezier extrude\n  * Fixing the JavaCad depancy tree.\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Tue, 29 Nov 2016 22:26:12 -0500\n\nbowlerstudio (0.20.0) xenial; urgency=medium\n\n  [ Kevin Harrington ]\n  * 0.19.6\n\n  [ Kevin Harrington ]\n  * catch error s and exceptions\n  * cleanup\n  * clean warnings\n  * The zoom level and enable/disable grid\n  * Adding the axis to the hide feature\n  * Adding the ability for scripts to display to 3d window\n  * Loading of image nodes ttested working\n  * when an image is loaded the creature lab should display\n  * Faster turn over for loading creaturelav\n  * if any other exceptiuon besides the expected one comes up, print right away\n  * Adding the ability to clear the screen\n  * Making the zoom scale by distance to avoid jumpy navigation\n  * remove print\n  * Exiting the application after clearing cache\n  * Using the open flag to prevent multiple openings\n  * Reversing the motion directions\n  * Adding the bezier extrude\n  * Fixing the JavaCad depancy tree.\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Tue, 29 Nov 2016 21:59:08 -0500\n\nbowlerstudio (0.20.0) xenial; urgency=medium\n\n  [ Kevin Harrington ]\n  * 0.19.6\n\n  [ Kevin Harrington ]\n  * catch error s and exceptions\n  * cleanup\n  * clean warnings\n  * The zoom level and enable/disable grid\n  * Adding the axis to the hide feature\n  * Adding the ability for scripts to display to 3d window\n  * Loading of image nodes ttested working\n  * when an image is loaded the creature lab should display\n  * Faster turn over for loading creaturelav\n  * if any other exceptiuon besides the expected one comes up, print right away\n  * Adding the ability to clear the screen\n  * Making the zoom scale by distance to avoid jumpy navigation\n  * remove print\n  * Exiting the application after clearing cache\n  * Using the open flag to prevent multiple openings\n  * Reversing the motion directions\n  * Adding the bezier extrude\n  * Fixing the JavaCad depancy tree.\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Tue, 29 Nov 2016 21:55:03 -0500\n\nbowlerstudio (0.20.0) xenial; urgency=medium\n\n  [ Kevin Harrington ]\n  * 0.19.6\n\n  [ Kevin Harrington ]\n  * catch error s and exceptions\n  * cleanup\n  * clean warnings\n  * The zoom level and enable/disable grid\n  * Adding the axis to the hide feature\n  * Adding the ability for scripts to display to 3d window\n  * Loading of image nodes ttested working\n  * when an image is loaded the creature lab should display\n  * Faster turn over for loading creaturelav\n  * if any other exceptiuon besides the expected one comes up, print right away\n  * Adding the ability to clear the screen\n  * Making the zoom scale by distance to avoid jumpy navigation\n  * remove print\n  * Exiting the application after clearing cache\n  * Using the open flag to prevent multiple openings\n  * Reversing the motion directions\n  * Adding the bezier extrude\n  * Fixing the JavaCad depancy tree.\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Tue, 29 Nov 2016 21:42:42 -0500\n\nbowlerstudio (0.20.0) xenial; urgency=medium\n\n  [ Kevin Harrington ]\n  * 0.19.6\n\n  [ Kevin Harrington ]\n  * catch error s and exceptions\n  * cleanup\n  * clean warnings\n  * The zoom level and enable/disable grid\n  * Adding the axis to the hide feature\n  * Adding the ability for scripts to display to 3d window\n  * Loading of image nodes ttested working\n  * when an image is loaded the creature lab should display\n  * Faster turn over for loading creaturelav\n  * if any other exceptiuon besides the expected one comes up, print right away\n  * Adding the ability to clear the screen\n  * Making the zoom scale by distance to avoid jumpy navigation\n  * remove print\n  * Exiting the application after clearing cache\n  * Using the open flag to prevent multiple openings\n  * Reversing the motion directions\n  * Adding the bezier extrude\n  * Fixing the JavaCad depancy tree.\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Tue, 29 Nov 2016 21:41:07 -0500\n\nbowlerstudio (0.19.5) xenial; urgency=medium\n\n  * 0.19.4\n  * checking for valid tutorial URL as commonwealth not NR\n  * retry opening tab\n  * removing the focus request and relying on the one below\n  * When setting a database value the internal caches should be cleared, ofrcing a reloading of the database files.\n  * Adding try catch in flush widget to prevent thread lock\n  * Adding an event path for refreshing the menus on adding a file.\n  * adding the refreshing of the menues when a file is created or from a menu option\n  * do not use iterators with a threaded opening\n  * adding an err printout of all console prints\n  * Adding the configuration to BowlerConnection allowing it to use the classloader when loading from FXML\n  * close #1\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Tue, 11 Oct 2016 15:10:01 -0400\n\nbowlerstudio (0.19.4) xenial; urgency=medium\n\n  * 0.19.4\n  * checking for valid tutorial URL as commonwealth not NR\n  * retry opening tab\n  * removing the focus request and relying on the one below\n  * When setting a database value the internal caches should be cleared, ofrcing a reloading of the database files.\n  * Adding try catch in flush widget to prevent thread lock\n  * Adding an event path for refreshing the menus on adding a file.\n  * adding the refreshing of the menues when a file is created or from a menu option\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Wed, 05 Oct 2016 19:17:37 -0400\n\nbowlerstudio (0.19.3) xenial; urgency=medium\n\n  [ Kevin Harrington ]\n  * Updating the readme links\n  * Changing the documention cache root to the new website server\n\n  [ Kevin Harrington ]\n  * Update README.md\n\n  [ Kevin Harrington ]\n  * updating JCSG, GRAPES\n  * adding a parameter listener for all objects\n  * 0.19.3\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Sat, 17 Sep 2016 10:54:36 -0400\n\nbowlerstudio (0.19.2) xenial; urgency=medium\n\n  * Adding Apache XML RPC Client for accessing Odoo servers\n  * Adding Odoo command and control\n  * Adjusting to the new type interface\n  * Repairing the Physics centered displays\n  * Adding OpenCL and nativelibs4j\n  * removing opencl for size\n  * moving vitamins out of kernel\n  * Assing a Firmata Link provider\n  * Adding a connection option to the menue for firmata\n  * Adding support for Firmata\n  * Adding rotory and prismatic options\n  * moving firmata linak adding into the link class as a static\n  * Opening of creature lab should more stable\n  * Should make opening creature lab more stable\n  * updateing to kernel 0.23.0\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Fri, 02 Sep 2016 14:41:46 -0400\n\nbowlerstudio (0.19.1) xenial; urgency=medium\n\n  [ Sainath Batthala ]\n  * Documented openUrlInNewTab, speak functions in BowlerStudio.java\n\n  [ Kevin Harrington ]\n  * Update README.md\n  * Update README.md\n\n  [ Kevin Harrington ]\n  * More stable casting of the list\n  * adding better arduino support\n  * Moving Physics engine into BowlerStudio\n  * Moving the simplified cad out of the system and telling the physics engine to compute the hull directly on all the points.\n  * 0.19.0\n  * using the for loop variable, duh\n  * WOring full robot physics\n  * updating the link slider with the links current value\n  * should move link if NOT passive\n  * preserving the links color\n  * tweaking the physics engine params\n  * The cad list should not be changed by the physics, a copy should be made\n  * defaulting physics engine to fast mode\n  * param tweaking for physics engine\n  * seems to cause wild flailing now\n  * Reverting physics configuration\n  * Physics for walking is working but the driving explodes\n  * load mass from robots configuration\n  * loading objects to current object\n  * load string list parameters as combo box\n  * catch all UI exceptions\n  * use the Abstract class iinterface for non specific types\n  * loading and updating the non number parameters\n  * Each regenerate shpould get its own exception catch\n  * The value needs to be updated BEFORE the listener is called\n  * Forcing the CSGCached version update here\n  * Kernel now suports the string list parametrics for CSG's and the vitamins library for accessing lots of standrd hardware\n  * 0.19.1\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Sat, 13 Aug 2016 12:14:50 -0400\n\nbowlerstudio (0.18.10) xenial; urgency=medium\n\n  * adding files to regular git\n  * adding a file extention to the new file menue\n  * Selecting links that hit limits\n  * catching drive exceptions and displaying ghtem\n  * when limit hit the limiting link should highlightitself now\n  * both local and perminant storage is consulted for loading\n  * make loading and unloading of the data from json a standard type\n  * fixed the double adding of transforms\n  * DyIO interface working with curie\n  * updating to lates kernel\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Mon, 25 Jul 2016 19:25:46 -0400\n\nbowlerstudio (0.18.10) xenial; urgency=medium\n\n  * adding a file extention to the new file menue\n  * Selecting links that hit limits\n  * catching drive exceptions and displaying ghtem\n  * when limit hit the limiting link should highlightitself now\n  * both local and perminant storage is consulted for loading\n  * make loading and unloading of the data from json a standard type\n  * fixed the double adding of transforms\n  * DyIO interface working with curie\n  * updating to lates kernel\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Mon, 25 Jul 2016 19:24:53 -0400\n\nbowlerstudio (0.18.9.1) xenial; urgency=medium\n\n  * adding files to regular git\n  * adding a file extention to the new file menue\n  * Selecting links that hit limits\n  * catching drive exceptions and displaying ghtem\n  * when limit hit the limiting link should highlightitself now\n  * both local and perminant storage is consulted for loading\n  * make loading and unloading of the data from json a standard type\n  * fixed the double adding of transforms\n  * DyIO interface working with curie\n  * updating to lates kernel\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Mon, 25 Jul 2016 19:18:28 -0400\n\nbowlerstudio (0.18.9) xenial; urgency=medium\n\n  * adding files to regular git\n  * adding a file extention to the new file menue\n  * Selecting links that hit limits\n  * catching drive exceptions and displaying ghtem\n  * when limit hit the limiting link should highlightitself now\n  * both local and perminant storage is consulted for loading\n  * make loading and unloading of the data from json a standard type\n  * fixed the double adding of transforms\n  * DyIO interface working with curie\n  * updating to lates kernel\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Mon, 25 Jul 2016 19:11:24 -0400\n\nbowlerstudio (0.18.9) xenial; urgency=medium\n\n  * adding files to regular git\n  * adding a file extention to the new file menue\n  * Selecting links that hit limits\n  * catching drive exceptions and displaying ghtem\n  * when limit hit the limiting link should highlightitself now\n  * both local and perminant storage is consulted for loading\n  * make loading and unloading of the data from json a standard type\n  * fixed the double adding of transforms\n  * DyIO interface working with curie\n  * updating to lates kernel\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Mon, 25 Jul 2016 18:45:43 -0400\n\nbowlerstudio (0.18.9) xenial; urgency=medium\n\n  * adding files to regular git\n  * adding a file extention to the new file menue\n  * Selecting links that hit limits\n  * catching drive exceptions and displaying ghtem\n  * when limit hit the limiting link should highlightitself now\n  * both local and perminant storage is consulted for loading\n  * make loading and unloading of the data from json a standard type\n  * fixed the double adding of transforms\n  * DyIO interface working with curie\n  * updating to lates kernel\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Mon, 25 Jul 2016 18:44:01 -0400\n\nbowlerstudio (0.18.9) xenial; urgency=medium\n\n  * adding files to regular git\n  * adding a file extention to the new file menue\n  * Selecting links that hit limits\n  * catching drive exceptions and displaying ghtem\n  * when limit hit the limiting link should highlightitself now\n  * both local and perminant storage is consulted for loading\n  * make loading and unloading of the data from json a standard type\n  * fixed the double adding of transforms\n  * DyIO interface working with curie\n  * updating to lates kernel\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Mon, 25 Jul 2016 17:56:29 -0400\n\nbowlerstudio (0.18.8-1ubuntu2) xenial; urgency=medium\n\n  * adding the printout when a connection fails\n  * retry on serial port to force v4\n  * conecting the device not the connection to startup\n  * making the regenerate function synchronized\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Fri, 15 Jul 2016 17:31:38 -0400\n\nbowlerstudio (0.18.8-1ubuntu1) xenial; urgency=medium\n\n  [ Kevin Harrington ]\n  * Adding java-bowler 2.21.0\n  * adding the physics engine static methods back in\n  * switching to actual login sucess checking form login file existance\n  * fixing the window displays so the login updates it\n  * updating to the gradle version of the kernel\n  * use git repos when displaying files to allow for the browsing of regular github repos\n  * 0.18.7\n  * configurations on no network mode not working\n  * Loading the widgets even when the network is out\n\n  [ Kevin Harrington ]\n\n  [ Kevin Harrington ]\n\n  [ Kevin Harrington ]\n\n  [ Kevin Harrington ]\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Wed, 29 Jun 2016 09:39:56 -0400\n\nbowlerstudio (0.18.6-1ubuntu1~33.gbp533ad1ubuntu1) xenial; urgency=medium\n\n  * changing the initial release to xenial\n  * Adding java-bowler 2.21.0\n  * adding the physics engine static methods back in\n  * switching to actual login sucess checking form login file existance\n  * fixing the window displays so the login updates it\n  * updating to the gradle version of the kernel\n  * use git repos when displaying files to allow for the browsing of regular github repos\n  * 0.18.7\n  * configurations on no network mode not working\n  * Loading the widgets even when the network is out\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Tue, 28 Jun 2016 10:25:47 -0400\n\nbowlerstudio (0.18.6-1ubuntu1~33.gbp533ad1) xenial; urgency=medium\n\n  ** SNAPSHOT build @533ad1e3ef178e8643a4227dd33eb510575dd4ba **\n\n  [ Kevin Harrington ]\n  ** SNAPSHOT build @533ad1e3ef178e8643a4227dd33eb510575dd4ba **\n\n  * changing release to xenial\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Sun, 26 Jun 2016 23:34:47 -0400\n\nbowlerstudio (0.18.6-1ubuntu1~26.gbp19eead) 0.18.6; urgency=medium\n\n  ** SNAPSHOT build @19eead97c9ad3b27a90798f63afcafa2d869eec7 **\n\n  [ Kevin Harrington ]\n  * xenial\n\n  [ Kevin Harrington ]\n  * updating the changlog\n  * changelog\n  * adding debian build files to build debs from gradle\n\n  [ Kevin Harrington ]\n  * ignores\n\n  [ Kevin Harrington ]\n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Sun, 26 Jun 2016 22:35:38 -0400\n\nbowlerstudio (0.18.6-1) xenial; urgency=medium\n\n  * Initial release  \n\n -- Kevin Harrington <mad.hephaestus@gmail.com>  Sat, 25 Jun 2016 15:23:12 -0400\n"
  },
  {
    "path": "debian/compat",
    "content": "7\n"
  },
  {
    "path": "debian/control",
    "content": "Source: bowlerstudio\nPackage: bowlerstudio\nPriority: extra\nMaintainer: Customer Support <mad.hephaestus@gmail.com>\nArchitecture: all\nVersion: 0.18.6\nDepends: oracle-java8-set-default,libopencv2.4-java,libopencv2.4-jni,slic3r,jarwrapper,arduino\nProvides: bowlerstudio\nConflicts: modemmanager,nr-rdk-java\nReplaces: modemmanager,nr-rdk-java\nDescription: Robotics Development Engironment and runtime. \n A scripting platform for writing, designing, simulating \n and manufacturing robots.  \n"
  },
  {
    "path": "debian/copyright",
    "content": "Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/\nUpstream-Name: bowlerstudio\nSource: <url://example.com>\n\nFiles: *\nCopyright: <years> <put author's name and email here>\n           <years> <likewise for another author>\nLicense: <special license>\n <Put the license of the package here indented by 1 space>\n <This follows the format of Description: lines in control file>\n .\n <Including paragraphs>\n\n# If you want to use GPL v2 or later for the /debian/* files use \n# the following clauses, or change it to suit. Delete these two lines\nFiles: debian/*\nCopyright: 2016 Kevin Harrington <mad.hephaestus@gmail.com>\nLicense: GPL-2+\n This package is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n .\n This package is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n .\n You should have received a copy of the GNU General Public License\n along with this program. If not, see <https://www.gnu.org/licenses/>\n .\n On Debian systems, the complete text of the GNU General\n Public License version 2 can be found in \"/usr/share/common-licenses/GPL-2\".\n\n# Please also look if there are files or directories which have a\n# different copyright/license attached and list them here.\n# Please avoid picking licenses with terms that are more restrictive than the\n# packaged work, as it may make Debian's contributions unacceptable upstream.\n"
  },
  {
    "path": "debian/install",
    "content": "usr/share/bowlerstudio/BowlerStudio.jar /usr/share/bowlerstudio/\nusr/share/bowlerstudio/NeuronRobotics.ico /usr/share/bowlerstudio/\nusr/share/bowlerstudio/NeuronRobotics.png /usr/share/bowlerstudio/\nusr/share/bowlerstudio/dyio-3.14.6.xml /usr/share/bowlerstudio/\nusr/bin/bowlerstudio /usr/bin/\nusr/share/bowlerstudio/NeuronRobotics.png /usr/share/themes/base/neuronrobotics/icons/\nusr/share/bowlerstudio/BowlerStudio.desktop /usr/share/applications/\nusr/share/bowlerstudio/81-neuronrobotics.rules /etc/udev/rules.d/\nusr/share/doc/bowlerstudio/copyright /usr/share/doc/bowlerstudio/\nusr/share/doc/bowlerstudio/changelog.gz /usr/share/doc/bowlerstudio/\n"
  },
  {
    "path": "debian/outfile",
    "content": "3.0 (quilt)\n"
  },
  {
    "path": "debian/rules",
    "content": "JAVA_HOME=/usr/lib/jvm/default-java\n\n%:\n    dh $@ --with javahelper\n\noverride_dh_auto_build:\n    dh_auto_build -- dist\n\noverride_dh_install:\n    dh_auto_build -- package-debian\n    dh_install\n\noverride_jh_exec:\n    exit 0\n"
  },
  {
    "path": "genDeps.sh",
    "content": "#!/bin/bash\n\n#./gradlew showAll>alllibs.txt\n\nsort alllibs.txt |grep .jar|uniq >DEPENDENCIES_shallow.md\n\necho \"\" > DEPENDENCIES.md\necho \"\" > DEPENDENCIES_unknown.md\nfor VARIABLE in $(cat DEPENDENCIES_shallow.md)\ndo\n    LOCATION=$(locate -l 1 $VARIABLE)\n    FILE=$(unzip -l $LOCATION | grep LICENSE|grep -v \"LICENSE.\")\n    stringarray=($FILE)\n    LOCENSELOC=$(echo ${stringarray[3]})\n    if [ -z \"$LOCENSELOC\" ]\n    then\n\t\tFILE=$(unzip -l $LOCATION | grep LICENSE|grep \".txt\"|grep -v \"documentation\")\n\t\tstringarray=($FILE)\n\t\tLOCENSELOC=$(echo ${stringarray[3]})\n\t\t\n    fi\n    if [ -z \"$LOCENSELOC\" ]\n    then\n\t\tFILE=$(unzip -l $LOCATION | grep license|grep \".txt\"|grep -v \"documentation\")\n\t\tstringarray=($FILE)\n\t\tLOCENSELOC=$(echo ${stringarray[3]})\n    fi\n    if [ -z \"$LOCENSELOC\" ]\n    then\n\t\tFILE=$(unzip -l $LOCATION | grep LICENSE.md)\n\t\tstringarray=($FILE)\n\t\tLOCENSELOC=$(echo ${stringarray[3]})\n    fi\n    if [ -z \"$LOCENSELOC\" ]\n    then\n\t\tFILE=$(unzip -l $LOCATION | grep license.html)\n\t\tstringarray=($FILE)\n\t\tLOCENSELOC=$(echo ${stringarray[3]})\n    fi\n    if [ -z \"$LOCENSELOC\" ]\n    then\n    \techo \"$VARIABLE No license file\"\n\tTYPE=\"No License \"\n\techo \"$VARIABLE\">> DEPENDENCIES_unknown.md\n    else\n        echo \"Licance file to be used: $VARIABLE $LOCENSELOC\"\t   \n    \t#echo \"Searching $VARIABLE for $LOCENSELOC\"\n\t    \n\tLICENSE=$(unzip -p $LOCATION $LOCENSELOC)\n\n\tTYPE=$LICENSE\n\tif [ -z \"$TYPE\" ]\n\tthen\n\techo \"$VARIABLE No license file\"\n\tTYPE=\"No License $LOCATION\"\n\telse\n\t    shopt -s nocasematch;\n\t    if [[ \"$LICENSE\" =~ \"apache\" ]]; then\n\t\t  TYPE=\"Apache\"\n\t\t  #echo \"Apache license found\"\n\t    elif   [[ \"$LICENSE\" =~ \"MIT License\" ]]; then\n\t    \tTYPE=\"MIT License\"\n\t    elif   [[ \"$LICENSE\" =~ \"BSD\" ]]; then\n\t    \tTYPE=\"BSD\"\n\t    elif   [[ \"$LICENSE\" =~ \"W3C\" ]]; then\n\t    \tTYPE=\"W3C\"\n\t    elif   [[ \"$LICENSE\" =~ \"CDDL\" ]]; then\n\t    \tTYPE=\"CDDL\"\t\n\t    else\n\t    \techo \"  $LOCATION Unknown $LICENSE\" \n\t    \tTYPE=\"Unknown\"\n\t    \t#exit 1\t  \n\t    fi\n\tfi\n\techo \"$VARIABLE , $TYPE\">> DEPENDENCIES.md\n    fi\n    \n    \ndone\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-9.1.0-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "gradle.properties",
    "content": "rg.gradle.jvmargs=-Dprism.forceGPU=true"
  },
  {
    "path": "gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright  2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions $var, ${var}, ${var:-default}, ${var+SET},\n#           ${var#prefix}, ${var%suffix}, and $( cmd );\n#         * compound commands having a testable exit status, especially case;\n#         * various built-in commands including command, set, and ulimit.\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\n# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)\nAPP_HOME=$( cd -P \"${APP_HOME:-./}\" > /dev/null && printf '%s\\n' \"$PWD\" ) || exit\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    if ! command -v java >/dev/null 2>&1\n    then\n        die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Collect all arguments for the java command:\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,\n#     and any embedded shellness will be escaped.\n#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be\n#     treated as '${Hostname}' itself on the command line.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        org.gradle.wrapper.GradleWrapperMain \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n@rem SPDX-License-Identifier: Apache-2.0\r\n@rem\r\n\r\n@if \"%DEBUG%\"==\"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\r\n@rem This is normally unused\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif %ERRORLEVEL% equ 0 goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif %ERRORLEVEL% equ 0 goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nset EXIT_CODE=%ERRORLEVEL%\r\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\r\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\r\nexit /b %EXIT_CODE%\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "jvm.json",
    "content": "{\n\t\"Linux-x64\":{\n\t\t\"url\":\"https://cdn.azul.com/zulu/bin/\",\n\t\t\"type\":\"tar.gz\",\n\t\t\"name\":\"zulu25.32.21-ca-fx-jre25.0.2-linux_x64\",\n\t\t\"jvmargs\":[\n\t\t\t\"-Dprism.dirtyopts=false\",\n\t\t\t\"-Dprism.forceGPU=true\",\n\t\t\t\"-XX:MaxRAMPercentage=95\",\n\t\t\t\"--add-exports javafx.graphics/com.sun.javafx.css=ALL-UNNAMED\",\n\t\t\t\"--add-exports javafx.controls/com.sun.javafx.scene.control.behavior=ALL-UNNAMED\",\n\t\t\t\"--add-exports javafx.controls/com.sun.javafx.scene.control=ALL-UNNAMED\",\n\t\t\t\"--add-exports javafx.base/com.sun.javafx.event=ALL-UNNAMED\",\n\t\t\t\"--add-exports javafx.controls/com.sun.javafx.scene.control.skin.resources=ALL-UNNAMED\",\n\t\t\t\"--add-exports javafx.graphics/com.sun.javafx.util=ALL-UNNAMED\",\n\t\t\t\"--add-exports javafx.graphics/com.sun.javafx.scene.input=ALL-UNNAMED\",\n\t\t\t\"--add-opens javafx.graphics/javafx.scene=ALL-UNNAMED\"\n\t\t]\n\t},\n\n\t\"Linux-aarch64\":{\n\t\t\"url\":\"https://cdn.azul.com/zulu/bin/\",\n\t\t\"type\":\"tar.gz\",\n\t\t\"name\":\"zulu25.32.21-ca-fx-jre25.0.2-linux_aarch64\",\n\t\t\"jvmargs\":[\n\t\t\t\t\t\"-Dprism.dirtyopts=false\",\n\t\t\t\"-Dprism.order=es2\",\n\t\t\t\"-Dprism.verbose=true\", \n\t\t\t\"-Dprism.forceGPU=true\",\n\t\t\t\"-XX:MaxRAMPercentage=95\",\n\t\t\t\"--add-exports javafx.graphics/com.sun.javafx.css=ALL-UNNAMED\",\n\t\t\t\"--add-exports javafx.controls/com.sun.javafx.scene.control.behavior=ALL-UNNAMED\",\n\t\t\t\"--add-exports javafx.controls/com.sun.javafx.scene.control=ALL-UNNAMED\",\n\t\t\t\"--add-exports javafx.base/com.sun.javafx.event=ALL-UNNAMED\",\n\t\t\t\"--add-exports javafx.controls/com.sun.javafx.scene.control.skin.resources=ALL-UNNAMED\",\n\t\t\t\"--add-exports javafx.graphics/com.sun.javafx.util=ALL-UNNAMED\",\n\t\t\t\"--add-exports javafx.graphics/com.sun.javafx.scene.input=ALL-UNNAMED\",\n\t\t\t\"--add-opens javafx.graphics/javafx.scene=ALL-UNNAMED\"\n\t\t]\n\t},\n\n\t\"Windows-x64\":{\n\t\t\"url\":\"https://cdn.azul.com/zulu/bin/\",\n\t\t\"type\":\"zip\",\n\t\t\"name\":\"zulu25.32.21-ca-fx-jre25.0.2-win_x64\",\n\t\t\"jvmargs\":[\n\t\t\t\t\t\"-Dprism.dirtyopts=false\",\n\t\t\t\"-XX:MaxRAMPercentage=95\",\n\t\t\t\"--add-exports javafx.graphics/com.sun.javafx.css=ALL-UNNAMED\",\n\t\t\t\"--add-exports javafx.controls/com.sun.javafx.scene.control.behavior=ALL-UNNAMED\",\n\t\t\t\"--add-exports javafx.controls/com.sun.javafx.scene.control=ALL-UNNAMED\",\n\t\t\t\"--add-exports javafx.base/com.sun.javafx.event=ALL-UNNAMED\",\n\t\t\t\"--add-exports javafx.controls/com.sun.javafx.scene.control.skin.resources=ALL-UNNAMED\",\n\t\t\t\"--add-exports javafx.graphics/com.sun.javafx.util=ALL-UNNAMED\",\n\t\t\t\"--add-exports javafx.graphics/com.sun.javafx.scene.input=ALL-UNNAMED\",\n\t\t\t\"--add-opens javafx.graphics/javafx.scene=ALL-UNNAMED\"\n\t\t]\n\t},\n\n\t\"Mac-x64\":{\n\t\t\"url\":\"https://cdn.azul.com/zulu/bin/\",\n\t\t\"type\":\"zip\",\n\t\t\"name\":\"zulu25.32.21-ca-fx-jre25.0.2-macosx_x64\",\n\t\t\"jvmargs\":[\n\t\t\t\t\t\"-Dprism.dirtyopts=false\",\n\t\t\t\"-Dprism.forceGPU=true\",\n\t\t\t\"-XX:MaxRAMPercentage=95\",\n\t\t\t\"--add-exports javafx.graphics/com.sun.javafx.css=ALL-UNNAMED\",\n\t\t\t\"--add-exports javafx.controls/com.sun.javafx.scene.control.behavior=ALL-UNNAMED\",\n\t\t\t\"--add-exports javafx.controls/com.sun.javafx.scene.control=ALL-UNNAMED\",\n\t\t\t\"--add-exports javafx.base/com.sun.javafx.event=ALL-UNNAMED\",\n\t\t\t\"--add-exports javafx.controls/com.sun.javafx.scene.control.skin.resources=ALL-UNNAMED\",\n\t\t\t\"--add-exports javafx.graphics/com.sun.javafx.util=ALL-UNNAMED\",\n\t\t\t\"--add-exports javafx.graphics/com.sun.javafx.scene.input=ALL-UNNAMED\",\n\t\t\t\"--add-opens javafx.graphics/javafx.scene=ALL-UNNAMED\"\n\t\t]\n\t},\n\n\t\"Mac-aarch64\":{\n\t\t\"url\":\"https://cdn.azul.com/zulu/bin/\",\n\t\t\"type\":\"zip\",\n\t\t\"name\":\"zulu25.32.21-ca-fx-jre25.0.2-macosx_aarch64\",\n\t\t\"jvmargs\":[\n\t\t\t\t\t\"-Dprism.dirtyopts=false\",\n\t\t\t\"-Dprism.forceGPU=true\",\n\t\t\t\"-XX:MaxRAMPercentage=95\",\n\t\t\t\"--add-exports javafx.graphics/com.sun.javafx.css=ALL-UNNAMED\",\n\t\t\t\"--add-exports javafx.controls/com.sun.javafx.scene.control.behavior=ALL-UNNAMED\",\n\t\t\t\"--add-exports javafx.controls/com.sun.javafx.scene.control=ALL-UNNAMED\",\n\t\t\t\"--add-exports javafx.base/com.sun.javafx.event=ALL-UNNAMED\",\n\t\t\t\"--add-exports javafx.controls/com.sun.javafx.scene.control.skin.resources=ALL-UNNAMED\",\n\t\t\t\"--add-exports javafx.graphics/com.sun.javafx.util=ALL-UNNAMED\",\n\t\t\t\"--add-exports javafx.graphics/com.sun.javafx.scene.input=ALL-UNNAMED\",\n\t\t\t\"--add-opens javafx.graphics/javafx.scene=ALL-UNNAMED\"\n\t\t]\n\t}\n}\n"
  },
  {
    "path": "libraries/.gitignore",
    "content": "/.project\n/.settings/\n/.classpath\n"
  },
  {
    "path": "log/.gitignore",
    "content": "/server.log\n"
  },
  {
    "path": "makeJar.sh",
    "content": "SCRIPT_DIR=$( cd -- \"$( dirname -- \"${BASH_SOURCE[0]}\" )\" &> /dev/null && pwd )\n\nexport ARCH=x86_64\nJVM=zulu25.32.21-ca-fx-jdk25.0.2-linux_x64\nset -e\nZIP=$JVM.tar.gz\nexport JAVA_HOME=$HOME/bin/java21/\nif test -d $JAVA_HOME/$JVM/; then\n  echo \"$JAVA_HOME exists.\"\nelse\n\trm -rf $JAVA_HOME\n\tmkdir -p $JAVA_HOME\n\twget https://cdn.azul.com/zulu/bin/$ZIP \n\ttar -xvzf $ZIP -C $JAVA_HOME\n\tmv $JAVA_HOME/$JVM/* $JAVA_HOME/\nfi\necho \"Java home set to $JAVA_HOME\"\n\n./gradlew spotlessCheck shadowJar\n"
  },
  {
    "path": "runMac.sh",
    "content": "SCRIPT_DIR=$( cd -- \"$( dirname -- \"${BASH_SOURCE[0]}\" )\" &> /dev/null && pwd )\n\nexport ARCH=x86_64\nJVM=zulu25.32.21-ca-fx-jdk25.0.2-macosx_x64\nset -e\nZIP=$JVM.tar.gz\nexport JAVA_HOME=$HOME/bin/java21/\nif test -d $JAVA_HOME/$JVM/; then\n  echo \"$JAVA_HOME exists.\"\nelse\n\trm -rf $JAVA_HOME\n\tmkdir -p $JAVA_HOME\n\tcurl -L https://cdn.azul.com/zulu/bin/$ZIP -o $ZIP\n\ttar -xvzf $ZIP -C $JAVA_HOME\n\tmv $JAVA_HOME/$JVM/* $JAVA_HOME/\nfi\necho \"Java home set to $JAVA_HOME\"\n ./gradlew --stop\n#rm -rf ~/.gradle/caches/\n#rm -rf ~/.gradle/daemon/\n./gradlew clean build --refresh-dependencies\n./gradlew shadowJar\n\n$JAVA_HOME/bin/java \\\n\t-Dprism.forceGPU=true \\\n    -XX:MaxRAMPercentage=95 \\\n    --add-exports javafx.graphics/com.sun.javafx.css=ALL-UNNAMED \\\n\t--add-exports javafx.controls/com.sun.javafx.scene.control.behavior=ALL-UNNAMED \\\n\t--add-exports javafx.controls/com.sun.javafx.scene.control=ALL-UNNAMED \\\n\t--add-exports javafx.base/com.sun.javafx.event=ALL-UNNAMED \\\n\t--add-exports javafx.controls/com.sun.javafx.scene.control.skin.resources=ALL-UNNAMED \\\n\t--add-exports javafx.graphics/com.sun.javafx.util=ALL-UNNAMED \\\n\t--add-exports javafx.graphics/com.sun.javafx.scene.input=ALL-UNNAMED \\\n\t--add-opens javafx.graphics/javafx.scene=ALL-UNNAMED \\\n\t-jar build/libs/BowlerStudio.jar"
  },
  {
    "path": "searchLicense.sh",
    "content": "#!/bin/bash\nfor VARIABLE in $(cat DEPENDENCIES_unknown.md)\ndo\n\tLOCATION=$(locate -l 1 $VARIABLE)\n\techo $VARIABLE\n\tunzip -l $LOCATION | grep -i \"license\"\ndone"
  },
  {
    "path": "settings.gradle",
    "content": "include ':libraries:bowler-script-kernel'\ninclude ':libraries:bowler-script-kernel:java-bowler'\ninclude ':libraries:bowler-script-kernel:JCSG'\ninclude ':libraries:bowler-script-kernel:GithubPasswordManager:GithubPasswordManager'\nrootProject.name = 'BowlerStudio'\n\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/AddFileToGistController.java",
    "content": "package com.neuronrobotics.bowlerstudio;\n\nimport com.neuronrobotics.bowlerstudio.assets.AssetFactory;\nimport com.neuronrobotics.bowlerstudio.assets.FontSizeManager;\nimport com.neuronrobotics.bowlerstudio.scripting.ArduinoLoader;\nimport com.neuronrobotics.bowlerstudio.scripting.IScriptingLanguage;\nimport com.neuronrobotics.bowlerstudio.scripting.ScriptingEngine;\n\nimport eu.mihosoft.vrl.v3d.JavaFXInitializer;\nimport javafx.application.Application;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\nimport javafx.event.ActionEvent;\nimport javafx.fxml.FXML;\nimport javafx.fxml.FXMLLoader;\nimport javafx.scene.Node;\nimport javafx.scene.Parent;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Alert;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.ComboBox;\nimport javafx.scene.control.TextArea;\nimport javafx.scene.control.TextField;\nimport javafx.scene.image.Image;\nimport javafx.scene.image.ImageView;\nimport javafx.scene.layout.AnchorPane;\nimport javafx.stage.Modality;\nimport javafx.stage.Stage;\n\nimport java.io.File;\nimport java.text.Normalizer;\nimport java.text.Normalizer.Form;\nimport java.util.List;\nimport java.util.regex.Pattern;\n\nimport org.kohsuke.github.GHRepository;\n\n/**\n * Created by Ryan Benasutti on 2/6/2016.\n */\n@SuppressWarnings(\"restriction\")\npublic class AddFileToGistController extends Application {\n\t@FXML\n\tpublic TextField filenameField;\n\t@FXML\n\tpublic TextField repoName;\n\t@FXML\n\tprivate ComboBox<String> projects;\n\t@FXML\n\tpublic Button addFileButton, cancelButton;\n\t@FXML\n\tprivate ComboBox<String> extension;\n\t@FXML // fx:id=\"langaugeIcon\"\n\tprivate ImageView langaugeIcon; // Value injected by FXMLLoader\n\tprivate String extensionStr = \".groovy\";\n\tprivate String gitRepo;\n\t@FXML\n\tprivate TextArea description;\n\t@FXML\n\tprivate AnchorPane newProject;\n\n\t@FXML\n\tprivate AnchorPane addFile;\n\n\tprivate MenuRefreshEvent refreshevent;\n\tprivate boolean isArduino;\n\tprivate static final Pattern NONLATIN = Pattern.compile(\"[^\\\\w-]\");\n\tprivate static final Pattern WHITESPACE = Pattern.compile(\"[\\\\s]\");\n\tprivate String forcedType = null;\n\tpublic static String toSlug(String input) {\n\t\tString nowhitespace = WHITESPACE.matcher(input).replaceAll(\"-\");\n\t\tString normalized = Normalizer.normalize(nowhitespace, Form.NFD);\n\t\tString slug = NONLATIN.matcher(normalized).replaceAll(\"\");\n\t\treturn slug.replaceAll(\"[^a-zA-Z0-9]\", \"\");\n\t}\n\t// private GHGist gistID;\n\n\tpublic AddFileToGistController(String gitRepo, MenuRefreshEvent event) {\n\t\tthis.setGitRepo(gitRepo);\n\t\t// this.gistID = id;\n\t\tthis.refreshevent = event;\n\n\t}\n\n\t@SuppressWarnings(\"restriction\")\n\t@Override\n\tpublic void start(Stage primaryStage) throws Exception {\n\t\tisArduino = false;\n\t\tFXMLLoader loader = AssetFactory.loadLayout(\"layout/addFileToGist.fxml\", true);\n\t\tParent root;\n\t\tloader.setController(this);\n\t\t// This is needed when loading on MAC\n\t\tloader.setClassLoader(getClass().getClassLoader());\n\t\troot = loader.load();\n\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\tint tmp = fontNum - 10;\n\t\t\tif (tmp < 12)\n\t\t\t\ttmp = 12;\n\t\t\troot.setStyle(\"-fx-font-size: \" + tmp + \"pt\");\n\t\t});\n\t\textension.getItems().clear();\n\t\tif (getGitRepo() != null) {\n\t\t\tnewProject.getChildren().clear();\n\t\t} else {\n\t\t\taddFile.setDisable(true);\n\t\t}\n\t\tList<String> langs = ScriptingEngine.getAllLangauges();\n\t\tObservableList<String> options = FXCollections.observableArrayList(langs);\n\t\t//\n\t\tfor (String s : options) {\n\t\t\textension.getItems().add(s);\n\t\t}\n\t\textension.getSelectionModel().select(\"Groovy\");\n\t\tImage icon;\n\t\tString asset = \"Script-Tab-\" + extension.getSelectionModel().getSelectedItem() + \".png\";\n\n\t\ttry {\n\n\t\t\ticon = AssetFactory.loadAsset(asset);\n\t\t\tlangaugeIcon.setImage(icon);\n\t\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\t\tlangaugeIcon.setScaleX(FontSizeManager.getImageScale());\n\t\t\t\tlangaugeIcon.setScaleY(FontSizeManager.getImageScale());\n\t\t\t});\n\t\t} catch (Exception e2) {\n\t\t\t// Auto-generated catch block\n\t\t\te2.printStackTrace();\n\t\t}\n\n\t\textension.setOnAction(event -> {\n\t\t\ttry {\n\n\t\t\t\tString selectedItem = extension.getSelectionModel().getSelectedItem();\n\t\t\t\tsetSelected(selectedItem);\n\t\t\t} catch (Exception e1) {\n\t\t\t\t// Auto-generated catch block\n\t\t\t\te1.printStackTrace();\n\t\t\t}\n\n\t\t});\n\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tprimaryStage.setTitle(\"Add File to Git Repo \" + getGitRepo());\n\n\t\t\tScene scene = new Scene(root);\n\t\t\tprimaryStage.setScene(scene);\n\t\t\tprimaryStage.initModality(Modality.WINDOW_MODAL);\n\t\t\tprimaryStage.setResizable(true);\n\t\t\tprimaryStage.show();\n\t\t});\n\t}\n\n\tprivate void setSelected(String selectedItem) throws Exception {\n\t\tString file = \"Script-Tab-\" + selectedItem + \".png\";\n\t\tImage loadAsset = AssetFactory.loadAsset(file);\n\t\ttry {\n\t\t\tlangaugeIcon.setImage(loadAsset);\n\n\t\t} catch (Throwable t) {\n\t\t\tcom.neuronrobotics.sdk.common.Log.error(t);\n\t\t}\n\t\tString key = selectedItem;\n\t\tIScriptingLanguage l = ScriptingEngine.getLangaugesMap().get(key);\n\n\t\tif (l != null) {\n\t\t\textensionStr = l.getFileExtension().get(0);\n\t\t} else\n\t\t\textensionStr = \".groovy\";\n\t\tif (!extensionStr.startsWith(\".\")) {\n\t\t\textensionStr = \".\" + extensionStr;\n\t\t}\n\t\tisArduino = ArduinoLoader.class.isInstance(l);\n\n\t\tsetGitRepo(gitRepo);\n\t}\n\n\t@FXML\n\tpublic void onAddFile(ActionEvent event) {\n\t\tnew Thread(() -> {\n\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\tStage stage = (Stage) addFileButton.getScene().getWindow();\n\t\t\t\tstage.close();\n\t\t\t});\n\t\t\tString filename = filenameField.getText();\n\n\t\t\tif (!filename.endsWith(extensionStr)) {\n\t\t\t\tfilename = filename + extensionStr;\n\t\t\t}\n\t\t\tString fileSlug = filename.replace(extensionStr, \"\");\n\t\t\tString message = description.getText();\n\t\t\tif (message == null || message.length() == 0) {\n\t\t\t\tmessage = filename;\n\t\t\t}\n\n\t\t\tif (getGitRepo() == null) {\n\t\t\t\tsetGitRepo(GistHelper.createNewGist(filename, message, true));\n\t\t\t}\n\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Adding new file\" + filename + \" to \" + getGitRepo());\n\t\t\ttry {\n\t\t\t\tScriptingEngine.pull(getGitRepo());\n\t\t\t\t// String defaultContents =\n\t\t\t\tString fullBranch = ScriptingEngine.getFullBranch(getGitRepo());\n\t\t\t\tif (fullBranch == null)\n\t\t\t\t\tfullBranch = ScriptingEngine.newBranch(getGitRepo(), \"main\");\n\t\t\t\tScriptingEngine.getLangaugeByExtension(extensionStr).getDefaultContents(getGitRepo(), filename);\n\t\t\t\tScriptingEngine.pushCodeToGit(getGitRepo(), fullBranch, filename, null, message);\n\t\t\t\tFile nf = ScriptingEngine.fileFromGit(getGitRepo(), filename);\n\t\t\t\ttry {\n\t\t\t\t\tBowlerStudio.createFileTab(nf);\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\tex.printStackTrace();\n\t\t\t\t}\n\t\t\t\trefreshevent.setToLoggedIn();\n\t\t\t} catch (Exception e) {\n\t\t\t\tnew IssueReportingExceptionHandler().except(e);\n\t\t\t}\n\n\t\t}).start();\n\t}\n\n\t@FXML\n\tpublic void onCancel(ActionEvent event) {\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tStage stage = (Stage) cancelButton.getScene().getWindow();\n\t\t\tstage.close();\n\t\t});\n\t}\n\n\tpublic static void main(String[] args) {\n\t\tJavaFXInitializer.go();\n\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tStage s = new Stage();\n\t\t\tnew Thread(() -> {\n\t\t\t\tString url = \"https://github.com/madhephaestus/TestRepo.git\";\n\t\t\t\t// url = null;\n\t\t\t\tAddFileToGistController controller = new AddFileToGistController(url, new MenuRefreshEvent() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void setToLoggedIn() {\n\t\t\t\t\t\t// Auto-generated method stub\n\n\t\t\t\t\t}\n\n\t\t\t\t});\n\n\t\t\t\ttry {\n\t\t\t\t\tcontroller.start(s);\n\t\t\t\t\t// setToLoggedIn(\"\");\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(e);\n\t\t\t\t}\n\t\t\t}).start();\n\t\t});\n\t}\n\n\t@FXML\n\tvoid createProject(ActionEvent event) {\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tnewProject.setDisable(true);\n\t\t});\n\t\tnew Thread(() -> {\n\t\t\ttry {\n\t\t\t\tString text = description.getText();\n\t\t\t\tif (text == null || text.length() < 5) {\n\t\t\t\t\ttext = \"Project \" + repoName.getText();\n\t\t\t\t}\n\t\t\t\tString txt = repoName.getText();\n\t\t\t\tString slugVer = toSlug(txt);\n\t\t\t\tif (!txt.contentEquals(slugVer)) {\n\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\trepoName.setText(slugVer);\n\t\t\t\t\t\tAlert alert = new Alert(javafx.scene.control.Alert.AlertType.INFORMATION);\n\t\t\t\t\t\talert.setContentText(\"Repository Name must Valid: \" + slugVer);\n\t\t\t\t\t\tNode root = alert.getDialogPane();\n\t\t\t\t\t\tStage stage = (Stage) alert.getDialogPane().getScene().getWindow();\n\t\t\t\t\t\tstage.setOnCloseRequest(ev -> alert.hide());\n\t\t\t\t\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\t\t\t\t\tint tmp = fontNum - 10;\n\t\t\t\t\t\t\tif (tmp < 12)\n\t\t\t\t\t\t\t\ttmp = 12;\n\t\t\t\t\t\t\troot.setStyle(\"-fx-font-size: \" + tmp + \"pt\");\n\t\t\t\t\t\t\talert.getDialogPane().applyCss();\n\t\t\t\t\t\t\talert.getDialogPane().layout();\n\t\t\t\t\t\t\tstage.sizeToScene();\n\t\t\t\t\t\t});\n\t\t\t\t\t\talert.showAndWait();\n\t\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\t\tnewProject.setDisable(false);\n\t\t\t\t\t\t});\n\t\t\t\t\t});\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tGHRepository repository = ScriptingEngine.makeNewRepo(toSlug(repoName.getText()), text);\n\t\t\t\tsetGitRepo(repository.getHttpTransportUrl());\n\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\taddFile.setDisable(false);\n\t\t\t\t});\n\n\t\t\t} catch (Throwable e) {\n\t\t\t\t// Auto-generated catch block\n\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(e);\n\t\t\t}\n\t\t}).start();\n\n\t}\n\n\tpublic String getGitRepo() {\n\t\treturn gitRepo;\n\t}\n\n\tpublic void setGitRepo(String gitRepo) {\n\t\tthis.gitRepo = gitRepo;\n\t\tif (gitRepo != null) {\n\t\t\tString dirName = ScriptingEngine.getRepositoryCloneDirectory(gitRepo).getName();\n\t\t\tif (filenameField != null)\n\t\t\t\tBowlerStudio.runLater(() -> {\n\n\t\t\t\t\tfilenameField.setDisable(isArduino);\n\t\t\t\t\tfilenameField.setText(dirName);\n\t\t\t\t});\n\t\t}\n\t}\n\n\tpublic void setFileExtensionType(IScriptingLanguage lang) {\n\t\tString string = lang.getShellType();\n\t\ttry {\n\t\t\tsetSelected(string);\n\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\textension.setValue(string);\n\t\t\t\textension.setDisable(true);\n\t\t\t});\n\t\t} catch (Exception e) {\n\t\t\t// Auto-generated catch block\n\t\t\tcom.neuronrobotics.sdk.common.Log.error(e);\n\t\t}\n\n\t}\n\n\tpublic void start(Stage s, IScriptingLanguage iScriptingLanguage) throws Exception {\n\t\tstart(s);\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tsetFileExtensionType(iScriptingLanguage);\n\t\t});\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/BowlerStudio.java",
    "content": "package com.neuronrobotics.bowlerstudio;\n\nimport javafx.scene.control.Button;\nimport javafx.scene.control.ButtonType;\n\n//import com.neuronrobotics.kinematicschef.InverseKinematicsEngine;\nimport com.neuronrobotics.bowlerkernel.BowlerKernelBuildInfo;\nimport com.neuronrobotics.bowlerkernel.Bezier3d.IInteractiveUIElementProvider;\nimport com.neuronrobotics.bowlerkernel.Bezier3d.Manipulation;\nimport com.neuronrobotics.bowlerstudio.assets.AssetFactory;\nimport com.neuronrobotics.bowlerstudio.assets.BowlerStudioResourceFactory;\nimport com.neuronrobotics.bowlerstudio.assets.ConfigurationDatabase;\nimport com.neuronrobotics.bowlerstudio.assets.FontSizeManager;\nimport com.neuronrobotics.bowlerstudio.assets.StudioBuildInfo;\nimport com.neuronrobotics.bowlerstudio.creature.MobileBaseCadManager;\nimport com.neuronrobotics.bowlerstudio.creature.MobileBaseLoader;\nimport com.neuronrobotics.bowlerstudio.scripting.DownloadManager;\nimport com.neuronrobotics.bowlerstudio.scripting.GitHubWebFlow;\nimport com.neuronrobotics.bowlerstudio.scripting.IApprovalForDownload;\nimport com.neuronrobotics.bowlerstudio.scripting.IDownloadManagerEvents;\nimport com.neuronrobotics.bowlerstudio.scripting.IURLOpen;\nimport com.neuronrobotics.bowlerstudio.scripting.PasswordManager;\nimport com.neuronrobotics.bowlerstudio.scripting.ScriptingEngine;\nimport com.neuronrobotics.bowlerstudio.scripting.ScriptingFileWidget;\nimport com.neuronrobotics.bowlerstudio.scripting.external.GroovyEclipseExternalEditor;\nimport com.neuronrobotics.bowlerstudio.util.FileChangeWatcher;\nimport com.neuronrobotics.bowlerstudio.vitamins.Vitamins;\n//import com.neuronrobotics.imageprovider.OpenCVJNILoader;\nimport com.neuronrobotics.javacad.JavaCadBuildInfo;\nimport com.neuronrobotics.sdk.addons.kinematics.DHParameterKinematics;\nimport com.neuronrobotics.sdk.addons.kinematics.FirmataLink;\nimport com.neuronrobotics.sdk.addons.kinematics.LinkConfiguration;\nimport com.neuronrobotics.sdk.addons.kinematics.MobileBase;\nimport com.neuronrobotics.sdk.addons.kinematics.math.TransformNR;\nimport com.neuronrobotics.sdk.common.BowlerAbstractDevice;\nimport com.neuronrobotics.sdk.common.ByteList;\nimport com.neuronrobotics.sdk.common.DeviceManager;\nimport com.neuronrobotics.sdk.common.IDeviceAddedListener;\nimport com.neuronrobotics.sdk.common.Log;\nimport com.neuronrobotics.sdk.config.SDKBuildInfo;\nimport com.neuronrobotics.sdk.util.ThreadUtil;\nimport eu.mihosoft.vrl.v3d.CSG;\nimport eu.mihosoft.vrl.v3d.CSG.OptType;\nimport eu.mihosoft.vrl.v3d.parametrics.CSGDatabase;\nimport eu.mihosoft.vrl.v3d.parametrics.CSGDatabaseInstance;\nimport javafx.application.Application;\nimport javafx.application.Platform;\nimport javafx.fxml.FXMLLoader;\nimport javafx.geometry.Rectangle2D;\nimport javafx.scene.Node;\nimport javafx.scene.Parent;\nimport javafx.scene.PerspectiveCamera;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Alert;\nimport javafx.scene.control.Alert.AlertType;\nimport javafx.scene.control.TextArea;\nimport javafx.scene.image.Image;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.Priority;\nimport javafx.scene.transform.Affine;\nimport javafx.stage.Stage;\n\nimport static com.neuronrobotics.bowlerstudio.scripting.DownloadManager.delim;\n\nimport java.awt.Desktop;\nimport java.awt.GraphicsEnvironment;\nimport java.awt.SplashScreen;\nimport java.io.BufferedReader;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.io.OutputStream;\nimport java.io.PrintStream;\nimport java.io.PrintWriter;\nimport java.io.StringWriter;\nimport java.lang.Thread.UncaughtExceptionHandler;\nimport java.net.URI;\nimport java.net.URL;\nimport java.net.URLConnection;\nimport java.net.MalformedURLException;\nimport java.net.URISyntaxException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Optional;\n\nimport javax.swing.SwingUtilities;\nimport javax.swing.UIManager;\n\n@SuppressWarnings(\"restriction\")\npublic class BowlerStudio extends Application {\n\tfinal static SplashScreen splash = null;// = SplashScreen.getSplashScreen();\n\tprivate static Scene scene;\n\tprivate static boolean hasnetwork;\n\tprivate static _Console out;\n\tprivate static TextArea logViewRefStatic = null;\n\tprivate static String firstVer = \"\";\n\n\tprivate static Stage primaryStage2;\n\tprivate static File layoutFile;\n\tprivate static boolean deleteFlag = false;\n\tprivate static IssueReportingExceptionHandler reporter = new IssueReportingExceptionHandler();\n\t// private static String lastVersion;\n\tprivate static UncaughtExceptionHandler hand;\n\n\t@SuppressWarnings({\"unchecked\", \"restriction\"})\n\tpublic static void main(String[] args) throws Exception {\n\t\tinitializeBowlerStudio(args);\n\t\tlaunch();\n\n\t}\n\n\tpublic static void go() {\n\t\ttry {\n\t\t\tembeddedLaunch(new String[0]);\n\t\t} catch (Exception e) {\n\t\t\t// TODO Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t}\n\t}\n\n\tpublic static void embeddedLaunch(String[] args) throws Exception {\n\t\tinitializeBowlerStudio(args);\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tStage stage = new Stage();\n\t\t\tnew BowlerStudio().start(stage);\n\t\t});\n\t}\n\n\tpublic static void initializeBowlerStudio(String[] args) throws Exception {\n\t\tString relative = ScriptingEngine.getWorkingDirectory().getAbsolutePath();\n\t\tFile file = new File(relative + delim() + \"bowler-workspace\" + delim());\n\t\tfile.mkdirs();\n\t\tScriptingEngine.setWorkspace(file);\n\t\tFile logfile = new File(file.getAbsolutePath() + delim() + \"bowlerStudioLog.txt\");\n\t\tif (logfile.exists())\n\t\t\tlogfile.delete();\n\t\ttry {\n\t\t\tlogfile.createNewFile();\n\t\t\tLog.enableDebugPrint(true);\n\t\t\tLog.enableDebugPrint();\n\t\t\tLog.setFile(logfile);\n\t\t\tLog.debug(\"Log file set to \" + logfile.getAbsolutePath());\n\t\t\tLog.warning(\"BowlerStudio Version \" + StudioBuildInfo.getVersion());\n\t\t\tRuntime.getRuntime().addShutdownHook(new Thread(() -> {\n\t\t\t\tLog.flush();\n\t\t\t}));\n\t\t} catch (IOException e) {\n\t\t\t// TODO Auto-generated catch block\n\t\t\tLog.error(e);\n\t\t}\n\t\tCSG.setDefaultOptType(OptType.Manifold3d);\n\t\tDownloadManager.setSTUDIO_INSTALL(\"BowlerStudioInstall\");\n\n\t\tif (args.length != 0) {\n\t\t\t// Log.error(\"Arguments detected, starting Kernel mode.\");\n\t\t\t// SplashManager.closeSplash();\n\t\t\tBowlerKernel.runArgumentsAfterStartup(args, System.currentTimeMillis());\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tmakeSymLinkOfCurrentVersion();\n\t\t} catch (Throwable t) {\n\t\t\t// t.printStackTrace();\n\t\t\tLog.error(\"Symlink not creaded\");\n\t\t}\n\t\tnet.java.games.input.ControllerEnvironment.getDefaultEnvironment();\n\n\t\tThread.currentThread().setUncaughtExceptionHandler(new IssueReportingExceptionHandler());\n\t\tif (!StudioBuildInfo.isOS64bit()) {\n\n\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\tAlert alert = new Alert(AlertType.ERROR);\n\t\t\t\talert.setTitle(\"32 Bit Java Detected\");\n\t\t\t\talert.setHeaderText(\"Insuffient Ram Capibilities in 32 bit mode\");\n\t\t\t\talert.setContentText(\"This applications uses more that 4gb of ram\\nA 32 bit JVM mode detected: \"\n\t\t\t\t\t\t+ System.getProperty(\"os.arch\"));\n\t\t\t\tNode root = alert.getDialogPane();\n\t\t\t\tStage stage = (Stage) alert.getDialogPane().getScene().getWindow();\n\t\t\t\tstage.setOnCloseRequest(ev -> alert.hide());\n\t\t\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\t\t\tint tmp = fontNum - 10;\n\t\t\t\t\tif (tmp < 12)\n\t\t\t\t\t\ttmp = 12;\n\t\t\t\t\troot.setStyle(\"-fx-font-size: \" + tmp + \"pt\");\n\t\t\t\t\talert.getDialogPane().applyCss();\n\t\t\t\t\talert.getDialogPane().layout();\n\t\t\t\t\tstage.sizeToScene();\n\t\t\t\t});\n\t\t\t\talert.showAndWait();\n\t\t\t\tSystem.exit(1);\n\t\t\t});\n\t\t}\n\n\t\trenderSplashFrame(2, \"Testing Internet\");\n\n\t\ttry {\n\t\t\tfinal URL url = new URI(\"http://github.com\").toURL();\n\t\t\tfinal URLConnection conn = url.openConnection();\n\t\t\tconn.connect();\n\t\t\tconn.getInputStream();\n\t\t\tsetHasnetwork(true);\n\n\t\t} catch (Exception e) {\n\t\t\t// we assuming we have no access to the server and run off of the\n\t\t\t// cached gists.\n\t\t\tsetHasnetwork(false);\n\t\t\te.printStackTrace();\n\n\t\t}\n\n\t\tStudioBuildInfo.setBaseBuildInfoClass(BowlerStudio.class);\n\t\tManipulation.setUi(new IInteractiveUIElementProvider() {\n\t\t\tpublic void runLater(Runnable r) {\n\t\t\t\tBowlerStudio.runLater(r);\n\t\t\t}\n\n\t\t\tpublic TransformNR getCamerFrame() {\n\t\t\t\treturn BowlerStudio.getCamerFrame();\n\t\t\t}\n\n\t\t\tpublic double getCamerDepth() {\n\t\t\t\treturn BowlerStudio.getCamerDepth();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic PerspectiveCamera getCamera() {\n\t\t\t\treturn CreatureLab3dController.getEngine().getFlyingCamera().getCamera();\n\t\t\t}\n\t\t});\n\n\t\trenderSplashFrame(5, \"Loging In...\");\n\t\t// ScriptingEngine.logout();\n\t\t// switching to Web Flow auth\n\t\tList<String> listOfScopes = Arrays.asList(\"repo\", \"gist\", \"user\", \"admin:org\", \"admin:org_hook\", \"workflow\");\n\n\t\tGitHubWebFlow.setOpen(new IURLOpen() {\n\t\t\tpublic void open(URI toOpe) {\n\t\t\t\ttry {\n\t\t\t\t\tBowlerStudio.openExternalWebpage(toOpe.toURL());\n\t\t\t\t} catch (MalformedURLException e) {\n\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tPasswordManager.setListOfScopes(listOfScopes);\n\t\tGitHubWebFlow.setMyAPI(() -> {\n\t\t\tString line = System.getProperty(\"API-ID\");\n\t\t\tif (line != null)\n\t\t\t\treturn line;\n\t\t\treturn \"1edf79fae494c232d4d2\";\n\t\t});\n\t\tNameGetter mykey = new NameGetter();\n\t\tGitHubWebFlow.setName(mykey);\n\t\tString myAssets = AssetFactory.getGitSource();\n\t\tif (PasswordManager.hasNetwork()) {\n\t\t\tLog.error(\"Attempt to log in with disk credentials\");\n\t\t\tScriptingEngine.waitForLogin();\n\t\t\tif (ScriptingEngine.isLoginSuccess()) {\n\n\t\t\t\t// if (BowlerStudio.hasNetwork()) {\n\t\t\t\t// ScriptingEngine.setAutoupdate(true);\n\t\t\t\t//\n\t\t\t\t// }\n\t\t\t\trenderSplashFrame(15, \"Load Configs\");\n\t\t\t\ttry {\n\t\t\t\t\tfirstVer = (String) ConfigurationDatabase.getObject(\"BowlerStudioConfigs\", \"firstVersion\",\n\t\t\t\t\t\t\tStudioBuildInfo.getVersion());\n\t\t\t\t} catch (Throwable t) {\n\t\t\t\t\tLog.error(\"Resetting the configs repo...\");\n\t\t\t\t\t// clear the configs repo\n\t\t\t\t\tfirstVer = (String) ConfigurationDatabase.getObject(\"BowlerStudioConfigs\", \"firstVersion\",\n\t\t\t\t\t\t\tStudioBuildInfo.getVersion());\n\t\t\t\t}\n\t\t\t\tConfigurationDatabase.setObject(\"BowlerStudioConfigs\", \"currentVersion\", StudioBuildInfo.getVersion());\n\t\t\t\trenderSplashFrame(16, \"Done Load Configs\");\n\t\t\t\t// myAssets = (String) ConfigurationDatabase.getObject(\"BowlerStudioConfigs\",\n\t\t\t\t// \"assetRepo\",\n\t\t\t\t// myAssets);\n\t\t\t\trenderSplashFrame(20, \"DL'ing Image Assets\");\n\t\t\t\tLog.error(\"Asset Repo \" + myAssets);\n\n\t\t\t\tLog.error(\"Asset intended ver \" + StudioBuildInfo.getVersion());\n\t\t\t\tScriptingEngine.cloneRepo(myAssets, null);\n\t\t\t\ttry {\n\t\t\t\t\tScriptingEngine.pull(myAssets, \"main\");\n\t\t\t\t\tLog.error(\"Studio version is the same\");\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t\tScriptingEngine.deleteRepo(myAssets);\n\t\t\t\t\tScriptingEngine.cloneRepo(myAssets, null);\n\t\t\t\t}\n\n\t\t\t\tif (ScriptingEngine.checkOwner(myAssets)) {\n\t\t\t\t\tif (!ScriptingEngine.tagExists(myAssets, StudioBuildInfo.getVersion())) {\n\t\t\t\t\t\tLog.error(\"Tagging Assets at \" + StudioBuildInfo.getVersion());\n\t\t\t\t\t\tScriptingEngine.tagRepo(myAssets, StudioBuildInfo.getVersion());\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (BowlerStudio.hasNetwork()) {\n\t\t\t\t\trenderSplashFrame(25, \"Populating Menu\");\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\trenderSplashFrame(20, \"DL'ing Image Assets\");\n\t\t\t\tScriptingEngine.cloneRepo(myAssets, null);\n\t\t\t\tScriptingEngine.pull(myAssets, \"main\");\n\t\t\t}\n\t\t}\n\t\tlayoutFile = AssetFactory.loadFile(\"layout/default.css\");\n\t\tif (layoutFile == null || !layoutFile.exists()) {\n\t\t\tScriptingEngine.deleteRepo(myAssets);\n\t\t\tScriptingEngine.cloneRepo(myAssets, null);\n\t\t\tlayoutFile = AssetFactory.loadFile(\"layout/default.css\");\n\t\t}\n\t\tif ((Boolean) ConfigurationDatabase.get(\"BowlerStudioUI\", \"DarkMode\", true)) {\n\t\t\tlayoutFile = AssetFactory.loadFile(\"layout/darkmode.css\");\n\t\t}\n\t\t// SplashManager.setIcon(AssetFactory.loadAsset(\"BowlerStudioTrayIcon.png\"));\n\t\trenderSplashFrame(50, \"DL'ing Tutorials...\");\n\t\t// load tutorials repo\n\n\t\tTutorial.getHomeUrl(); // Dowload and launch the Tutorial server\n\t\t// force the current version in to the version number\n\t\t// Download and Load all of the assets\n\n\t\trenderSplashFrame(60, \"Vitamins...\");\n\t\t// load the vitimins repo so the demo is always snappy\n\t\tScriptingEngine.cloneRepo(\"https://github.com/CommonWealthRobotics/BowlerStudioVitamins.git\", null);\n\n\t\trenderSplashFrame(80, \"Example Robots\");\n\t\tScriptingEngine.cloneRepo(\"https://github.com/CommonWealthRobotics/BowlerStudioExampleRobots.git\", null);\n\t\tScriptingEngine.pull(\"https://github.com/CommonWealthRobotics/BowlerStudioExampleRobots.git\");\n\t\trenderSplashFrame(81, \"CSG database\");\n\n\t\tCSGDatabase.setInstance(new CSGDatabaseInstance(\n\t\t\t\tnew File(ScriptingEngine.getWorkspace().getAbsoluteFile() + \"/csgDatabase.json\")));\n\n\t\t// Log.error(\"Loading assets \");\n\n\t\t// Log.error(\"Loading Main.fxml\");\n\t\trenderSplashFrame(82, \"Set up UI\");\n\t\ttry {\n\t\t\tUIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());\n\t\t\t// This is a workaround for #8 and is only relavent on osx\n\t\t\t// it causes the SwingNodes not to load if not called way ahead\n\t\t\t// of time\n\t\t\tjavafx.scene.text.Font.getFamilies();\n\t\t} catch (Exception e) {\n\t\t\treporter.uncaughtException(Thread.currentThread(), e);\n\n\t\t}\n\t\t// add a new link provider to the link factory\n\t\tFirmataLink.addLinkFactory();\n\t\t// Log.enableInfoPrint();\n\t\trenderSplashFrame(91, \"DL'ing Devices...\");\n\t\t// ThreadUtil.wait(100);\n\n\t\ttry {\n\t\t\tensureUpdated(false, \"https://github.com/CommonWealthRobotics/DHParametersCadDisplay.git\",\n\t\t\t\t\t\"https://github.com/CommonWealthRobotics/HotfixBowlerStudio.git\",\n\t\t\t\t\t\"https://github.com/CommonWealthRobotics/DeviceProviders.git\",\n\t\t\t\t\t\"https://github.com/OperationSmallKat/Katapult.git\",\n\t\t\t\t\t\"https://github.com/CommonWealthRobotics/ExternalEditorsBowlerStudio.git\",\n\t\t\t\t\t\"https://github.com/CommonWealthRobotics/freecad-bowler-cli.git\",\n\t\t\t\t\t\"https://github.com/CommonWealthRobotics/blender-bowler-cli.git\");\n\t\t\tScriptingEngine.gitScriptRun(CSGDatabase.getInstance(),\n\t\t\t\t\t\"https://github.com/CommonWealthRobotics/HotfixBowlerStudio.git\", \"hotfix.groovy\", null);\n\t\t\tScriptingEngine.gitScriptRun(CSGDatabase.getInstance(),\n\t\t\t\t\t\"https://github.com/CommonWealthRobotics/DeviceProviders.git\", \"loadAll.groovy\", null);\n\t\t\trenderSplashFrame(92, \"Vitamin Scripts...\");\n\t\t\tHashSet<String> urls = new HashSet<>();\n\t\t\tfor (String type : Vitamins.listVitaminTypes()) {\n\t\t\t\tString url = Vitamins.getScriptGitURL(type);\n\t\t\t\turls.add(url);\n\t\t\t}\n\t\t\tnew Thread(() -> {\n\t\t\t\tboolean wasState = ScriptingEngine.isPrintProgress();\n\t\t\t\t// ScriptingEngine.setPrintProgress(false);\n\t\t\t\tfor (Iterator<String> iterator = urls.iterator(); iterator.hasNext();) {\n\t\t\t\t\tString url = iterator.next();\n\n\t\t\t\t\tensureUpdated(false, url);\n\n\t\t\t\t}\n\t\t\t\tScriptingEngine.setPrintProgress(wasState);\n\t\t\t}).start();\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t\treporter.uncaughtException(Thread.currentThread(), e);\n\n\t\t}\n\t\tDownloadManager.setDownloadEvents(new IDownloadManagerEvents() {\n\n\t\t\t@Override\n\t\t\tpublic void startDownload() {\n\t\t\t\tSplashManager.renderSplashFrame(0, \"Downloading...\");\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void finishDownload() {\n\t\t\t\tSplashManager.closeSplash();\n\t\t\t}\n\t\t});\n\t\tDownloadManager.setApproval(new IApprovalForDownload() {\n\t\t\tprivate ButtonType buttonType = null;\n\n\t\t\t@Override\n\t\t\tpublic boolean get(String name, String url) {\n\t\t\t\tbuttonType = null;\n\n\t\t\t\tBowlerKernel.runLater(() -> {\n\t\t\t\t\tAlert alert = new Alert(Alert.AlertType.CONFIRMATION);\n\t\t\t\t\talert.setTitle(\"Message\");\n\t\t\t\t\talert.setHeaderText(\"Would you like add the \" + name + \" plugin?\");\n\t\t\t\t\tNode root = alert.getDialogPane();\n\t\t\t\t\tStage stage = (Stage) alert.getDialogPane().getScene().getWindow();\n\t\t\t\t\tstage.setOnCloseRequest(ev -> alert.hide());\n\t\t\t\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\t\t\t\tint tmp = fontNum - 10;\n\t\t\t\t\t\tif (tmp < 12)\n\t\t\t\t\t\t\ttmp = 12;\n\t\t\t\t\t\troot.setStyle(\"-fx-font-size: \" + tmp + \"pt\");\n\t\t\t\t\t\talert.getDialogPane().applyCss();\n\t\t\t\t\t\talert.getDialogPane().layout();\n\t\t\t\t\t\tstage.sizeToScene();\n\t\t\t\t\t});\n\t\t\t\t\tOptional<ButtonType> result = alert.showAndWait();\n\t\t\t\t\tbuttonType = result.get();\n\t\t\t\t\talert.close();\n\t\t\t\t});\n\n\t\t\t\twhile (buttonType == null) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tThread.sleep(100);\n\t\t\t\t\t} catch (InterruptedException e) {\n\t\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\t\te.printStackTrace();\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\treturn buttonType.equals(ButtonType.OK);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onInstallFail(String url) {\n\t\t\t\ttry {\n\t\t\t\t\tURL urlObject = new URI(url).toURL();\n\t\t\t\t\tBowlerStudio.openExternalWebpage(urlObject);\n\t\t\t\t} catch (MalformedURLException e) {\n\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t} catch (URISyntaxException e) {\n\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void notifyOfFailure(String name) {\n\t\t\t\tBowlerKernel.runLater(() -> {\n\t\t\t\t\tAlert alert = new Alert(Alert.AlertType.CONFIRMATION);\n\t\t\t\t\talert.setTitle(\"Message\");\n\t\t\t\t\talert.setHeaderText(\"FAILED to install \" + name + \" plugin\");\n\t\t\t\t\tNode root = alert.getDialogPane();\n\t\t\t\t\tStage stage = (Stage) alert.getDialogPane().getScene().getWindow();\n\t\t\t\t\tstage.setOnCloseRequest(ev -> alert.hide());\n\t\t\t\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\t\t\t\tint tmp = fontNum - 10;\n\t\t\t\t\t\tif (tmp < 12)\n\t\t\t\t\t\t\ttmp = 12;\n\t\t\t\t\t\troot.setStyle(\"-fx-font-size: \" + tmp + \"pt\");\n\t\t\t\t\t\talert.getDialogPane().applyCss();\n\t\t\t\t\t\talert.getDialogPane().layout();\n\t\t\t\t\t\tstage.sizeToScene();\n\t\t\t\t\t});\n\t\t\t\t\tOptional<ButtonType> result = alert.showAndWait();\n\t\t\t\t\tbuttonType = result.get();\n\t\t\t\t\talert.close();\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\t\trenderSplashFrame(92, \"Launching UI\");\n\n\t}\n\n\tprivate static class _Console extends OutputStream {\n\t\tprivate static final int LengthOfOutputLog = 5000;\n\t\tByteList incoming = new ByteList();\n\t\tThread update = new Thread() {\n\t\t\tpublic void run() {\n\t\t\t\tThread.currentThread().setUncaughtExceptionHandler(new IssueReportingExceptionHandler());\n\n\t\t\t\twhile (true) {\n\t\t\t\t\tThreadUtil.wait(150);\n\t\t\t\t\tif (incoming.size() > 0)\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tString text = incoming.asString();\n\t\t\t\t\t\t\tincoming.clear();\n\t\t\t\t\t\t\tif (text != null && text.length() > 0)\n\t\t\t\t\t\t\t\tappendText(text);\n\t\t\t\t\t\t\ttext = null;\n\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\te.printStackTrace();\n\t\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\tpublic _Console() {\n\t\t\tupdate.start();\n\t\t}\n\n\t\t@SuppressWarnings(\"restriction\")\n\t\tpublic void appendText(String v) {\n\t\t\tif (v.length() > LengthOfOutputLog) {\n\t\t\t\tv = v.substring(v.length() - LengthOfOutputLog, v.length());\n\t\t\t}\n\t\t\tString valueOf = v;\n\n\t\t\tif (getLogViewRefStatic() != null) {\n\t\t\t\tString text = getLogViewRefStatic().getText();\n\t\t\t\tif (text.length() > LengthOfOutputLog) {\n\n\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tgetLogViewRefStatic().deleteText(0, text.length() - LengthOfOutputLog);\n\n\t\t\t\t\t\t\tgetLogViewRefStatic().appendText(valueOf);\n\t\t\t\t\t\t} catch (Throwable t) {\n\t\t\t\t\t\t}\n\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\tgetLogViewRefStatic().appendText(valueOf);\n\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t\t// System.err.print(valueOf);\n\t\t}\n\n\t\tpublic void write(int b) throws IOException {\n\t\t\tincoming.add(b);\n\t\t}\n\t}\n\n\tpublic static void runLater(long delay, Runnable action) {\n\t\trunLater(java.time.Duration.ofMillis(delay), action);\n\t}\n\n\tpublic static void runLater(java.time.Duration delay, Runnable action) {\n\t\tThrowable t = new Exception(\"Delayed UI Thread Exception here!\");\n\t\t// t.printStackTrace();\n\t\tnew Thread() {\n\t\t\tpublic void run() {\n\t\t\t\tsetName(\"UI Delay Thread \");\n\t\t\t\ttry {\n\t\t\t\t\tThread.sleep(delay.getSeconds() * 1000);\n\t\t\t\t} catch (InterruptedException e) {\n\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\n\t\t\t\trunLater(action, t);\n\t\t\t}\n\t\t}.start();\n\t}\n\n\tpublic static void runLater(Runnable r) {\n\t\tif (Platform.isFxApplicationThread())\n\t\t\ttry {\n\t\t\t\tr.run();\n\t\t\t} catch (Throwable t) {\n\t\t\t\tt.printStackTrace();\n\t\t\t}\n\t\telse\n\t\t\trunLater(r, new Exception(\"UI Thread Exception here!\"));\n\t}\n\n\tpublic static void runLater(Runnable r, Throwable ex) {\n\t\tif (Platform.isFxApplicationThread())\n\t\t\ttry {\n\t\t\t\tr.run();\n\t\t\t} catch (Throwable t) {\n\t\t\t\tt.printStackTrace();\n\t\t\t}\n\t\telse\n\t\t\tPlatform.runLater(() -> {\n\t\t\t\ttry {\n\t\t\t\t\tr.run();\n\t\t\t\t} catch (Throwable t) {\n\t\t\t\t\tt.printStackTrace();\n\t\t\t\t\tex.printStackTrace();\n\t\t\t\t}\n\n\t\t\t});\n\t}\n\n\tpublic static OutputStream getOut() {\n\t\tif (out == null)\n\t\t\tout = new _Console();\n\t\treturn out;\n\t}\n\n\tpublic static MobileBase loadMobileBaseFromGit(String id, String file) throws Exception {\n\t\treturn MobileBaseLoader.fromGit(CSGDatabase.getInstance(), id, file);\n\t}\n\n\tpublic static void select(MobileBase base) {\n\t\tif (CreatureLab3dController.getEngine().isAutoHightlight()) {\n\t\t\tMobileBaseCadManager.get(CSGDatabase.getInstance(), base).selectCsgByMobileBase(base);\n\t\t}\n\t\t/*\n\t\t * try {\n\t\t *\n\t\t * ArrayList<CSG> csg =\n\t\t * MobileBaseCadManager.get(base).getBasetoCadMap().get(base);\n\t\t * CreatureLab3dController.getEngine(). setSelectedCsg(csg.get(0));\n\t\t * CreatureLab3dController.getEngine(). setSelectedCsg(csg); } catch (Exception\n\t\t * ex) { Log.error(\"Base not loaded yet\"); }\n\t\t */\n\n\t}\n\n\tpublic static void select(MobileBase base, DHParameterKinematics limb) {\n\t\tif (CreatureLab3dController.getEngine().isAutoHightlight()) {\n\t\t\tMobileBaseCadManager.get(CSGDatabase.getInstance(), base).selectCsgByLimb(base, limb);\n\t\t}\n\t\t/*\n\t\t * try {\n\t\t *\n\t\t * ArrayList<CSG> limCad =\n\t\t * MobileBaseCadManager.get(base).getDHtoCadMap().get(limb); try {\n\t\t * CreatureLab3dController.getEngine() .setSelectedCsg(limCad.get(limCad.size()\n\t\t * - 1)); } catch (Exception ex) { // initialization has no csgs yet }\n\t\t * CreatureLab3dController.getEngine(). setSelectedCsg(limCad); } catch\n\t\t * (Exception ex) { Log.error(\"Limb not loaded yet\"); }\n\t\t */\n\t}\n\n\t/**\n\t * Select a provided affine that is in a given global pose\n\t *\n\t * @param startingLocation the starting pose\n\t * @param rootListener     what affine to attach to\n\t */\n\tpublic static void select(TransformNR startingLocation, Affine rootListener) {\n\t\tif (CreatureLab3dController.getEngine().isAutoHightlight()) {\n\t\t\tCreatureLab3dController.getEngine().setSelected(startingLocation, rootListener);\n\t\t}\n\t}\n\n\t/**\n\t * Select a provided affine that is in a given global pose\n\t *\n\t * @param rootListener what affine to attach to\n\t */\n\tpublic static void select(Affine rootListener) {\n\t\tif (CreatureLab3dController.getEngine().isAutoHightlight()) {\n\t\t\tCreatureLab3dController.getEngine().setSelected(new TransformNR(), rootListener);\n\t\t}\n\t}\n\n\tpublic static void select(MobileBase base, LinkConfiguration limb) {\n\t\tif (CreatureLab3dController.getEngine().isAutoHightlight()) {\n\t\t\tMobileBaseCadManager.get(CSGDatabase.getInstance(), base).selectCsgByLink(base, limb);\n\t\t}\n\t\t/*\n\t\t * try {\n\t\t *\n\t\t * ArrayList<CSG> limCad =\n\t\t * MobileBaseCadManager.get(base).getLinktoCadMap().get(limb);\n\t\t * CreatureLab3dController.getEngine() .setSelectedCsg(limCad.get(limCad.size()\n\t\t * - 1)); CreatureLab3dController.getEngine(). setSelectedCsg(limCad); } catch\n\t\t * (Exception ex) { Log.error(\"Limb not loaded yet\"); }\n\t\t */\n\t}\n\n\tpublic static void select(File script, int lineNumber) {\n\t\tif (CreatureLab3dController.getEngine().isAutoHightlight())\n\t\t\ttry {\n\t\t\t\tCreatureLab3dController.getEngine().setSelectedCsg(script, lineNumber);\n\t\t\t} catch (Exception ex) {\n\t\t\t\tLog.error(\"File not found\");\n\t\t\t}\n\t}\n\n\t/**\n\t * @param args the command line arguments\n\t * @throws Exception\n\t */\n\tpublic static String getBowlerStudioBinaryVersion() throws FileNotFoundException {\n\t\tString latestVersionString;\n\t\tFile currentVerFile = new File(System.getProperty(\"user.home\") + delim() + \"bin\" + delim() + getInstallDirStub()\n\t\t\t\t+ delim() + \"currentversion.txt\");\n\t\tString s = \"\";\n\t\tBufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(currentVerFile)));\n\t\tString line;\n\t\ttry {\n\t\t\twhile (null != (line = br.readLine())) {\n\t\t\t\ts += line;\n\t\t\t}\n\t\t} catch (IOException e) {\n\t\t}\n\t\tlatestVersionString = s.trim();\n\t\treturn latestVersionString;\n\t}\n\n\tprivate static void makeSymLinkOfCurrentVersion() throws Exception {\n\t\tString version = getBowlerStudioBinaryVersion();\n\t\tFile installDir = new File(\n\t\t\t\tSystem.getProperty(\"user.home\") + delim() + \"bin\" + delim() + getInstallDirStub() + delim());\n\t\tFile link = new File(installDir.getAbsolutePath() + delim() + \"latest\");\n\t\tFile latest = new File(installDir.getAbsolutePath() + delim() + version);\n\t\tif (link.exists())\n\t\t\tlink.delete();\n\t\ttry {\n\n\t\t\tFiles.createSymbolicLink(link.toPath(), latest.toPath());\n\t\t} catch (Throwable t) {\n\t\t\t// t.printStackTrace();\n\t\t\t// link = new File(\"\\\"\"+link.getAbsolutePath()+\"\\\"\");\n\t\t\tPath ret = Files.createSymbolicLink(link.toPath(), Paths.get(\".\", version));\n\t\t\tLog.error(\"Path created \" + ret);\n\t\t}\n\t}\n\n\tpublic static void ensureUpdated(boolean check, String... urls) {\n\t\tBowlerKernel.ensureUpdated(check, urls);\n\t}\n\n\t// private static void removeAssets(String myAssets)\n\t// throws InvalidRemoteException, TransportException, GitAPIException,\n\t// IOException, Exception {\n\t// Log.error(\"Clearing assets\");\n\t// ScriptingEngine.deleteRepo(myAssets);\n\t// AssetFactory.setGitSource((String)\n\t// ConfigurationDatabase.getObject(\"BowlerStudioConfigs\", \"skinRepo\", myAssets),\n\t// StudioBuildInfo.getVersion());\n\t// }\n\n\tpublic static void closeSplash() {\n\t\tSplashManager.closeSplash();\n\t}\n\n\tpublic static void renderSplashFrame(int frame, String message) {\n\t\tSplashManager.renderSplashFrame(frame, message);\n\t}\n\n\t/**\n\t * open an external web page\n\t *\n\t * @param uri\n\t */\n\tpublic static void openExternalWebpage(URL uri) {\n\t\tDesktop desktop = Desktop.isDesktopSupported() ? Desktop.getDesktop() : null;\n\t\tif (desktop != null && desktop.isSupported(Desktop.Action.BROWSE)) {\n\t\t\ttry {\n\t\t\t\tdesktop.browse(uri.toURI());\n\t\t\t} catch (Exception e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * @author Sainath\n\t * @version 1.0\n\t * @param url - The URL of the tab that needs to be opened\n\t */\n\tpublic static void openUrlInNewTab(URL url) {\n\t\tBowlerStudioModularFrame.getBowlerStudioModularFrame().openUrlInNewTab(url);\n\t}\n\n\t/**\n\t * @author Sainath\n\t * @version 1.0\n\t * @param msg - message that needs to be spoken\n\t * @return an integer\n\t */\n\tpublic static int speak(String msg) {\n\n\t\treturn BowlerKernel.speak(msg);\n\t}\n\n\tpublic static ScriptingFileWidget createFileTab(File file) {\n\t\treturn BowlerStudioModularFrame.getBowlerStudioModularFrame().createFileTab(file);\n\t}\n\n\t@SuppressWarnings(\"restriction\")\n\tpublic static Scene getScene() {\n\t\treturn scene;\n\t}\n\n\t@SuppressWarnings(\"restriction\")\n\tpublic static void setScene(Scene s) {\n\t\tscene = s;\n\t}\n\n\t@SuppressWarnings(\"restriction\")\n\tpublic static void clearConsole() {\n\n\t\trunLater(() -> {\n\t\t\tif (getLogViewRefStatic() != null)\n\t\t\t\tgetLogViewRefStatic().setText(\"\");\n\t\t});\n\t}\n\n\tpublic static boolean hasNetwork() {\n\t\treturn hasnetwork;\n\t}\n\n\tpublic static void setHasnetwork(boolean hasnetwork) {\n\t\tBowlerStudio.hasnetwork = hasnetwork;\n\t}\n\n\t@SuppressWarnings(\"restriction\")\n\tpublic static TextArea getLogViewRefStatic() {\n\t\treturn logViewRefStatic;\n\t}\n\n\tpublic static void setLogViewRefStatic(@SuppressWarnings(\"restriction\") TextArea logViewRefStatic) {\n\t\tBowlerStudio.logViewRefStatic = logViewRefStatic;\n\t}\n\n\tpublic static void setCreatureLab3d(CreatureLab3dController creatureLab3dController) {\n\t}\n\n\t@SuppressWarnings(\"restriction\")\n\t@Override\n\tpublic void start(Stage primaryStage) {\n\t\ttry { // do this ...\n\t\t\tThread thread = Thread.currentThread();\n\t\t\tif (thread.getContextClassLoader() == null) {\n\t\t\t\t// seriously Apple??\n\t\t\t\tLog.error(\"ContextClassLoader Is Missing! (OSX) \");\n\t\t\t\tthread.setContextClassLoader(getClass().getClassLoader()); // a\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// valid\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// ClassLoader\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// from\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// somewhere\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// else\n\t\t\t}\n\t\t} catch (SecurityException e) {\n\t\t\treporter.uncaughtException(Thread.currentThread(), e);\n\n\t\t}\n\t\tPsudoSplash.setParentWindow(primaryStage);\n\t\tLog.error(\"Class loader: \" + Thread.currentThread().getContextClassLoader());\n\t\t// new Thread(() -> {\n\t\tThread.currentThread().setUncaughtExceptionHandler(new IssueReportingExceptionHandler());\n\n\t\ttry {\n\n\t\t\tString stylesheet = Application.STYLESHEET_MODENA;// \"MODENA\" or\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// \"CASPIAN\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// System.setProperty(\"javax.userAgentStylesheetUrl\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// stylesheet);\n\t\t\tsetUserAgentStylesheet(stylesheet);\n\t\t} catch (Exception | Error e) {\n\t\t\treporter.uncaughtException(Thread.currentThread(), e);\n\n\t\t}\n\t\t// These must be changed before anything starts\n\t\tPrintStream ps = new PrintStream(getOut());\n\t\tSystem.setErr(ps);\n\t\tLog.setMirrorStream(ps);\n\t\trenderSplashFrame(93, \"Loading resources\");\n\t\ttry {\n\t\t\tBowlerStudioResourceFactory.load();\n\t\t} catch (Exception e1) {\n\t\t\treporter.uncaughtException(Thread.currentThread(), e1);\n\n\t\t}\n\n\t\tprimaryStage2 = primaryStage;\n\t\tBowlerStudioModularFrame.setPrimaryStage(primaryStage);\n\t\t// Initialize your logic here: all @FXML variables will have been\n\t\t// injected\n\t\tFXMLLoader mainControllerPanel;\n\n\t\ttry {\n\t\t\tmainControllerPanel = AssetFactory.loadLayout(\"layout/BowlerStudioModularFrame.fxml\");\n\n\t\t\trenderSplashFrame(96, \"Setting controller\");\n\t\t\tmainControllerPanel.setController(new BowlerStudioModularFrame());\n\t\t\t// renderSplashFrame(96, \"Class loader\");\n\t\t\t// mainControllerPanel.setClassLoader(BowlerStudioModularFrame.class.getClassLoader());\n\t\t\ttry {\n\t\t\t\trenderSplashFrame(96, \"Controller load\");\n\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tmainControllerPanel.load();\n\t\t\t\t\t} catch (IOException e) {\n\t\t\t\t\t\tthrow new RuntimeException(e);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t} catch (Exception e) {\n\t\t\t\treporter.uncaughtException(Thread.currentThread(), e);\n\t\t\t\tSystem.exit(5);\n\n\t\t\t}\n\t\t\trenderSplashFrame(96, \"UI Launch...\");\n\n\t\t\tParent root = mainControllerPanel.getRoot();\n\n\t\t\tdouble sw = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDisplayMode()\n\t\t\t\t\t.getWidth();\n\t\t\tdouble sh = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDisplayMode()\n\t\t\t\t\t.getHeight();\n\t\t\tRectangle2D primaryScreenBounds = javafx.stage.Screen.getPrimary().getVisualBounds();\n\t\t\tdouble scalew = primaryScreenBounds.getWidth();\n\t\t\tdouble screenZoom = sw / scalew;\n\n\t\t\tif (FontSizeManager.getDefaultSize() == FontSizeManager.systemDefaultFontSize) {\n\t\t\t\tdouble newSize = sw / 2256.0 * (2 * FontSizeManager.systemDefaultFontSize) / screenZoom;\n\t\t\t\tif (newSize < FontSizeManager.systemDefaultFontSize)\n\t\t\t\t\tnewSize = FontSizeManager.systemDefaultFontSize;\n\t\t\t\tFontSizeManager.setFontSize((int) Math.round(newSize));\n\t\t\t\tLog.error(\"Screen \" + sw + \"x\" + sh);\n\t\t\t}\n\t\t\tsw = primaryScreenBounds.getWidth();\n\t\t\tsh = primaryScreenBounds.getHeight();\n\t\t\tdouble w;\n\t\t\tdouble h;\n\t\t\tw = sw - 40;\n\t\t\th = sh - 40;\n\n\t\t\tScene scene = new Scene(root, w, h, true);\n\t\t\tsetBowlerStudioCSS(scene);\n\t\t\tBowlerStudio.runLater(() -> {\n\n\t\t\t\tprimaryStage.setScene(scene);\n\t\t\t\tLog.error(\"Showing main applicaiton\");\n\t\t\t\tprimaryStage.show();\n\t\t\t\t// initialize the default styles for the dock pane and\n\t\t\t\t// undocked\n\t\t\t\t// nodes using the\n\t\t\t\t// library's internal Default.css stylesheet\n\t\t\t\t// unlike other custom control libraries this allows the\n\t\t\t\t// user to\n\t\t\t\t// override them globally\n\t\t\t\t// using the style manager just as they can with internal\n\t\t\t\t// JavaFX\n\t\t\t\t// controls\n\t\t\t\t// this must be called after the primary stage is shown\n\t\t\t\t// https://bugs.openjdk.java.net/browse/JDK-8132900\n\n\t\t\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\t\t\tBowlerStudioController bowlerStudio = BowlerStudioController.getBowlerStudio();\n\t\t\t\t\tbowlerStudio.setFontSize(fontNum);\n\t\t\t\t\tdouble tmp = FontSizeManager.getImageScale() * 9;\n\n\t\t\t\t\troot.setStyle(\"-fx-font-size: \" + ((int) tmp) + \"pt\");\n\t\t\t\t});\n\t\t\t});\n\n\t\t\tprimaryStage.setOnCloseRequest(arg0 -> {\n\t\t\t\t// ThreadUtil.wait(100);\n\t\t\t\tcloseBowlerStudio();\n\n\t\t\t});\n\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\tsetTitle(null);\n\n\t\t\t\ttry {\n\n\t\t\t\t\tImage loadAsset = new Image(PsudoSplash.getResource().toString());\n\t\t\t\t\tprimaryStage.getIcons().add(loadAsset);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\treporter.uncaughtException(Thread.currentThread(), e);\n\n\t\t\t\t}\n\t\t\t});;\n\n\t\t\tprimaryStage.setResizable(true);\n\n\t\t\tDeviceManager.addDeviceAddedListener(new IDeviceAddedListener() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void onNewDeviceAdded(BowlerAbstractDevice arg0) {\n\t\t\t\t\tLog.error(\"Device connected: \" + arg0);\n\t\t\t\t\tBowlerStudioModularFrame.getBowlerStudioModularFrame().showConectionManager();\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic void onDeviceRemoved(BowlerAbstractDevice arg0) {\n\t\t\t\t}\n\t\t\t});\n\t\t\tBowlerStudio.runLater(java.time.Duration.ofMillis((int) 2000), () -> {\n\t\t\t\tString javaVersion = System.getProperty(\"java.version\");\n\t\t\t\tString javafxVersion = System.getProperty(\"javafx.version\");\n\t\t\t\tLog.debug(\"Java Version : \" + javaVersion);\n\t\t\t\tLog.debug(\"JavaFX Version : \" + javafxVersion);\n\t\t\t\tLog.debug(\"BowlerStudio First Version: \" + firstVer);\n\t\t\t\tLog.debug(\"Java-Bowler Version: \" + SDKBuildInfo.getVersion());\n\t\t\t\tLog.debug(\"Bowler-Scripting-Kernel Version: \" + BowlerKernelBuildInfo.getVersion());\n\t\t\t\tLog.debug(\"JavaCad Version: \" + JavaCadBuildInfo.getVersion());\n\t\t\t\tLog.debug(\"Welcome to BowlerStudio!\");\n\n\t\t\t\ttry {\n\t\t\t\t\tFile jarFile = new File(GroovyEclipseExternalEditor.getApplicationJarPath());\n\t\t\t\t\tLog.debug(\"Application at \" + jarFile + \" is \" + (jarFile.exists() ? \"Found\" : \"Missing!\"));\n\t\t\t\t} catch (FileNotFoundException e) {\n\t\t\t\t\t// TODO Auto-generated catch block\n\t\t\t\t\te.printStackTrace();\n\n\t\t\t\t}\n\n\t\t\t});\n\t\t\tcloseSplash();\n\t\t\tif (!ScriptingEngine.isLoginSuccess() || PasswordManager.isAnonMode())\n\t\t\t\tBowlerStudioModularFrame.getBowlerStudioModularFrame().menueController.onLogin(null);\n\n\t\t} catch (Throwable e) {\n\t\t\treporter.uncaughtException(Thread.currentThread(), e);\n\n\t\t}\n\t\t// }).start();\n\n\t}\n\n\tpublic static void setTitle(String title) {\n\t\tif (title == null)\n\t\t\ttitle = StudioBuildInfo.getAppName() + \" v \" + StudioBuildInfo.getVersion();\n\t\tif (primaryStage2 != null)\n\t\t\tprimaryStage2.setTitle(title);\n\t}\n\n\tpublic static void setBowlerStudioCSS(Scene scene) {\n\t\tString nwfile = layoutFile.toURI().toString().replace(\"file:/\", \"file:///\");\n\n\t\tscene.getStylesheets().clear();\n\t\tscene.getStylesheets().add(nwfile);\n\n\t\tLog.error(\"Loading CSS from \" + nwfile);\n\t}\n\n\tpublic static void setToRunButton(Button b) {\n\t\tb.setText(\"Run\");\n\t\tb.setGraphic(AssetFactory.loadIcon(\"Run.png\"));\n\t\tb.getStyleClass().clear();\n\t\tb.getStyleClass().add(\"button-run\");\n\t\tb.getStyleClass().add(\"button\");\n\t\tb.setMinWidth(80);\n\t}\n\n\tpublic static void setToStopButton(Button b) {\n\t\tb.setText(\"Stop\");\n\t\tb.setGraphic(AssetFactory.loadIcon(\"Stop.png\"));\n\t\tb.getStyleClass().clear();\n\t\tb.getStyleClass().add(\"button\");\n\t\tb.getStyleClass().add(\"button-stop\");\n\t\tb.setMinWidth(80);\n\t}\n\n\t@SuppressWarnings(\"restriction\")\n\tpublic static void closeBowlerStudio() {\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tprimaryStage2.hide();\n\t\t});\n\t\tnew Thread() {\n\n\t\t\tpublic void run() {\n\t\t\t\tFileChangeWatcher.clearAll();\n\t\t\t\tThread.currentThread().setUncaughtExceptionHandler(new IssueReportingExceptionHandler());\n\n\t\t\t\trenderSplashFrame(100, \"Saving state..\");\n\t\t\t\tConnectionManager.disconnectAll();\n\t\t\t\tif (PasswordManager.hasNetwork()) {\n\t\t\t\t\tif (ScriptingEngine.isLoginSuccess() && !PasswordManager.isAnonMode())\n\t\t\t\t\t\tConfigurationDatabase.save();\n\t\t\t\t}\n\t\t\t\tif (isDeleteFlag())\n\t\t\t\t\tScriptingEngine.deleteCache();\n\t\t\t\tSystem.exit(0);\n\t\t\t}\n\t\t}.start();\n\n\t}\n\n\tpublic static void printStackTrace(Throwable e) {\n\t\tprintStackTrace(e, null);\n\t}\n\n\tpublic static void printStackTrace(Throwable e, File sourceFile) {\n\t\tBowlerStudioController.highlightException(sourceFile, e);\n\t}\n\n\tpublic static void println(CSG... toDisplay) {\n\t\tBowlerStudioController.setCsg(Arrays.asList(toDisplay));\n\t}\n\n\tpublic static void println(ArrayList<CSG> toDisplay) {\n\t\tBowlerStudioController.setCsg(toDisplay);\n\t}\n\n\tpublic static void print(CSG... toDisplay) {\n\t\tfor (CSG c : Arrays.asList(toDisplay))\n\t\t\tBowlerStudioController.addCsg(c);\n\t}\n\n\tpublic static void print(ArrayList<CSG> toDisplay) {\n\t\tfor (CSG c : toDisplay)\n\t\t\tBowlerStudioController.addCsg(c);\n\t}\n\n\tpublic static boolean isDeleteFlag() {\n\t\treturn deleteFlag;\n\t}\n\n\tpublic static void setDeleteFlag(boolean deleteFlag) {\n\t\tBowlerStudio.deleteFlag = deleteFlag;\n\t}\n\n\tpublic static void exit() {\n\t\tcloseBowlerStudio();\n\t}\n\n\tpublic static void invokeLater(Runnable object) {\n\t\tRuntimeException ex = new RuntimeException(\"SwingUtilities called from here\");\n\t\tSwingUtilities.invokeLater(() -> {\n\t\t\ttry {\n\t\t\t\tobject.run();\n\t\t\t} catch (Throwable t) {\n\t\t\t\tt.printStackTrace();\n\t\t\t\tLog.error(\"Swing method that failed called from: \");\n\t\t\t\tex.printStackTrace();\n\t\t\t}\n\t\t});\n\t}\n\n\tpublic static void moveCamera(TransformNR tf) {\n\t\trunLater(() -> {\n\t\t\tCreatureLab3dController.getEngine().moveCamera(tf);\n\t\t});\n\t}\n\n\tpublic static void setCamera(TransformNR tf) {\n\t\tTransformNR current = getCamerFrame();\n\t\tTransformNR tfupde = current.inverse().times(tf);\n\t\trunLater(() -> {\n\t\t\tCreatureLab3dController.getEngine().moveCamera(tfupde);\n\t\t});\n\t}\n\n\tpublic static TransformNR getCamerFrame() {\n\t\treturn CreatureLab3dController.getEngine().getFlyingCamera().getCamerFrame();\n\t}\n\n\tpublic static double getCamerDepth() {\n\t\treturn CreatureLab3dController.getEngine().getFlyingCamera().getZoomDepth();\n\t}\n\n\tpublic static void zoomCamera(double increment) {\n\t\trunLater(() -> {\n\t\t\tCreatureLab3dController.getEngine().zoomIncrement(increment);\n\t\t});\n\t}\n\n\tpublic static TransformNR getTargetFrame() {\n\t\treturn CreatureLab3dController.getEngine().getTargetNR();\n\t}\n\n\tpublic static void loadMobilBaseIntoUI(MobileBase base) {\n\t\tBowlerStudioController.getBowlerStudio().onScriptFinished(base, base, null);\n\t}\n\n\tpublic static void showExceptionAlert(Exception ex, String message) {\n\t\tAlert alert = new Alert(Alert.AlertType.ERROR);\n\t\talert.setTitle(\"Error\");\n\t\talert.setHeaderText(message);\n\t\talert.setContentText(ex.getMessage());\n\n\t\tStringWriter sw = new StringWriter();\n\t\tPrintWriter pw = new PrintWriter(sw);\n\t\tex.printStackTrace(pw);\n\t\tString stackTrace = sw.toString();\n\n\t\tTextArea textArea = new TextArea(stackTrace);\n\t\ttextArea.setEditable(false);\n\t\ttextArea.setWrapText(true);\n\n\t\ttextArea.setMaxWidth(Double.MAX_VALUE);\n\t\ttextArea.setMaxHeight(Double.MAX_VALUE);\n\t\tGridPane.setVgrow(textArea, Priority.ALWAYS);\n\t\tGridPane.setHgrow(textArea, Priority.ALWAYS);\n\n\t\tGridPane expContent = new GridPane();\n\t\texpContent.setMaxWidth(Double.MAX_VALUE);\n\t\texpContent.add(textArea, 0, 0);\n\n\t\talert.getDialogPane().setExpandableContent(expContent);\n\n\t\talert.showAndWait();\n\t}\n\n\tpublic static boolean checkValidURL(String url) {\n\t\ttry {\n\t\t\tif ((url == null) || (url.length() < 5))\n\t\t\t\tthrow new NullPointerException();\n\n\t\t\tif (url.startsWith(\"http\"))\n\t\t\t\tnew URI(url).toURL();// check that the URL string contains a valid URL\n\t\t\telse if (url.startsWith(\"git@\")) {\n\t\t\t\t// assume this is a URL\n\t\t\t}\n\t\t} catch (MalformedURLException e) {\n\t\t\t// not a url\n\t\t\t// Log.debug(\"Invalid URL \"+url);\n\t\t\t// e.printStackTrace();\n\t\t\treturn false;\n\t\t} catch (URISyntaxException e) {\n\t\t\t// not a url\n\t\t\t// Log.debug(\"Invalid URL \"+url);\n\t\t\t// e.printStackTrace();\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\tpublic static String getInstallDirStub() {\n\t\treturn DownloadManager.getSTUDIO_INSTALL();\n\t}\n\n\tpublic static void setInstallDirStub(String installDirStub) {\n\t\tDownloadManager.setSTUDIO_INSTALL(installDirStub);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/BowlerStudioController.java",
    "content": "package com.neuronrobotics.bowlerstudio;\n\nimport com.neuronrobotics.bowlerstudio.assets.AssetFactory;\nimport com.neuronrobotics.bowlerstudio.assets.ConfigurationDatabase;\nimport com.neuronrobotics.bowlerstudio.creature.IMobileBaseUI;\nimport com.neuronrobotics.bowlerstudio.creature.MobileBaseCadManager;\nimport com.neuronrobotics.bowlerstudio.scripting.IScriptEventListener;\nimport com.neuronrobotics.bowlerstudio.scripting.ScriptingEngine;\nimport com.neuronrobotics.bowlerstudio.scripting.ScriptingFileWidget;\nimport com.neuronrobotics.bowlerstudio.tabs.LocalFileScriptTab;\nimport com.neuronrobotics.bowlerstudio.util.FileChangeWatcher;\nimport com.neuronrobotics.bowlerstudio.util.IFileChangeListener;\nimport com.neuronrobotics.imageprovider.AbstractImageProvider;\nimport com.neuronrobotics.sdk.addons.kinematics.math.RotationNR;\nimport com.neuronrobotics.sdk.addons.kinematics.math.TransformNR;\nimport com.neuronrobotics.sdk.common.Log;\nimport com.neuronrobotics.sdk.util.ThreadUtil;\nimport eu.mihosoft.vrl.v3d.CSG;\nimport eu.mihosoft.vrl.v3d.Polygon;\nimport eu.mihosoft.vrl.v3d.Vector3d;\nimport eu.mihosoft.vrl.v3d.Vertex;\nimport eu.mihosoft.vrl.v3d.parametrics.CSGDatabase;\nimport javafx.scene.Node;\nimport javafx.scene.control.Tab;\nimport javafx.scene.image.ImageView;\nimport javafx.scene.shape.MeshView;\nimport javafx.scene.shape.TriangleMesh;\nimport javafx.scene.transform.Affine;\nimport javafx.stage.Stage;\n\n//import java.awt.Color;\n//import java.awt.*;\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.PrintWriter;\nimport java.io.StringWriter;\nimport java.nio.file.WatchEvent;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map.Entry;\nimport java.util.Set;\n\n@SuppressWarnings(\"restriction\")\npublic class BowlerStudioController implements IScriptEventListener {\n\n\t/**\n\t *\n\t */\n\tprivate ConnectionManager connectionManager;\n\tprivate AbstractImageProvider vrCamera;\n\tprivate static BowlerStudioController bowlerStudioControllerStaticReference = null;\n\tprivate boolean doneLoadingTutorials = false;\n\tprivate boolean runningExceptionHighlight = false;\n\n\tpublic BowlerStudioController() {\n\t\tif (getBowlerStudio() != null)\n\t\t\tthrow new RuntimeException(\"There can be only one Bowler Studio controller\");\n\t\tbowlerStudioControllerStaticReference = this;\n\t\tsize = ((Number) ConfigurationDatabase.getObject(\"BowlerStudioConfigs\", \"fontsize\", 12)).intValue();\n\n\t}\n\n\tprivate HashMap<String, Tab> openFiles = new HashMap<>();\n\tprivate HashMap<String, LocalFileScriptTab> widgets = new HashMap<>();\n\tprivate int size;\n\n\tprivate static IMobileBaseUI mbui = new IMobileBaseUI() {\n\n\t\t@Override\n\t\tpublic void highlightException(File fileEngineRunByName, Throwable ex) {\n\t\t\tBowlerStudioController.highlightException(fileEngineRunByName, ex);\n\t\t}\n\n\t\t@Override\n\t\tpublic void setAllCSG(Collection<CSG> toAdd, File source) {\n\t\t\ttry {\n\t\t\t\tif (toAdd != null)\n\t\t\t\t\tBowlerStudioController.setCsg(new ArrayList<>(toAdd));\n\t\t\t} catch (Throwable t) {\n\t\t\t\tt.printStackTrace();\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void addCSG(Collection<CSG> toAdd, File source) {\n\t\t\t// Auto-generated method stub\n\t\t\tfor (CSG b : toAdd)\n\t\t\t\tBowlerStudioController.addCsg(b);\n\t\t}\n\n\t\t@Override\n\t\tpublic Set<CSG> getVisibleCSGs() {\n\t\t\treturn CreatureLab3dController.getEngine().getCsgMap().keySet();\n\t\t}\n\n\t\t@Override\n\t\tpublic void setSelectedCsg(Collection<CSG> selectedCsg) {\n\t\t\tCreatureLab3dController.getEngine().setSelectedCsg(new ArrayList<>(selectedCsg));\n\n\t\t}\n\n\t\t@Override\n\t\tpublic void setSelected(Affine rootListener) {\n\t\t\tCreatureLab3dController.getEngine().setSelected(rootListener);\n\t\t}\n\t};\n\n\tpublic void setFontSize(int size) {\n\t\tthis.size = size;\n\t\tfor (String key : widgets.keySet()) {\n\t\t\twidgets.get(key).setFontSize(size);\n\t\t}\n\t}\n\n\t// Custom function for creation of New Tabs.\n\tpublic ScriptingFileWidget createFileTab(File file) {\n\t\tif (openFiles.get(file.getAbsolutePath()) != null && widgets.get(file.getAbsolutePath()) != null) {\n\t\t\tBowlerStudioModularFrame.getBowlerStudioModularFrame()\n\t\t\t\t\t.setSelectedTab(openFiles.get(file.getAbsolutePath()));\n\t\t\treturn widgets.get(file.getAbsolutePath()).getScripting();\n\t\t}\n\n\t\tTab fileTab = new Tab(file.getName());\n\t\topenFiles.put(file.getAbsolutePath(), fileTab);\n\n\t\ttry {\n\t\t\tLocalFileScriptTab t = new LocalFileScriptTab(file);\n\n\t\t\tnew Thread() {\n\t\t\t\tpublic void run() {\n\t\t\t\t\tString gitRepo = t.getScripting().getGitRepo();\n\t\t\t\t\tif (gitRepo != null) {\n\t\t\t\t\t\tString message = BowlerStudioMenu.gitURLtoMessage(gitRepo);\n\t\t\t\t\t\tif (gitRepo.length() < 5 || (message == null))\n\t\t\t\t\t\t\tmessage = \"Project \" + gitRepo;\n\t\t\t\t\t\tif (BowlerStudio.checkValidURL(gitRepo)) {\n\t\t\t\t\t\t\tBowlerStudioMenuWorkspace.add(gitRepo, message);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}.start();\n\n\t\t\tString gitRepoStr = t.getScripting().getGitRepo();\n\t\t\tString[] split = gitRepoStr.split(\"\\\\.\");\n\t\t\tString string = split[split.length - 2];\n\t\t\tString[] url = string.split(\"/\");\n\t\t\tString slug = url[url.length - 2] + \"/\" + url[url.length - 1];\n\t\t\tString key = slug + \":\" + t.getScripting().getGitFile();\n\t\t\tcom.neuronrobotics.sdk.common.Log.debug(\"Loading local file from: \" + file.getAbsolutePath() + \"\\n\" + key);\n\n\t\t\tif (key.length() == 1)\n\t\t\t\tthrow new RuntimeException(\"Failed to create a file key\");\n\t\t\tArrayList<String> files = new ArrayList<>();\n\t\t\tfiles.add(gitRepoStr);\n\t\t\tfiles.add(t.getScripting().getGitFile());\n\t\t\ttry {\n\t\t\t\tif (key.length() > 3 && files.get(0).length() > 0 && files.get(1).length() > 0) {// catch degenerates\n\t\t\t\t\tConfigurationDatabase.setObject(\"studio-open-file\", key, files);\n\t\t\t\t\tConfigurationDatabase.save();\n\t\t\t\t}\n\t\t\t} catch (java.lang.NullPointerException ex) {\n\t\t\t\t// file can not be opened\n\t\t\t}\n\n\t\t\tfileTab.setContent(t);\n\t\t\tImageView icon = AssetFactory\n\t\t\t\t\t.loadIcon(\"Script-Tab-\" + ScriptingEngine.getShellType(file.getName()) + \".png\");\n\t\t\ticon.setFitHeight(30);\n\t\t\ticon.setFitWidth(30);\n\n\t\t\tfileTab.setGraphic(icon);\n\t\t\tfileTab.selectedProperty().addListener((obs, wasSelected, isSelected) -> {\n\t\t\t\tif (isSelected) {\n\t\t\t\t\tt.requestTextAreaFocus();\n\t\t\t\t}\n\t\t\t});\n\t\t\taddTab(fileTab, true);\n\t\t\twidgets.put(file.getAbsolutePath(), t);\n\t\t\tSystem.err.println(\"Open Tab \" + file.getAbsolutePath());\n\n\t\t\tfileTab.setOnCloseRequest(event -> {\n\t\t\t\twidgets.remove(file.getAbsolutePath());\n\t\t\t\topenFiles.remove(file.getAbsolutePath());\n\t\t\t\tConfigurationDatabase.removeObject(\"studio-open-file\", key);\n\t\t\t\tConfigurationDatabase.save();\n\t\t\t\tt.getScripting().close();\n\t\t\t\tLog.debug(\"Closing Tab Here \" + file.getAbsolutePath());\n\t\t\t});\n\t\t\tFileChangeWatcher watcher = FileChangeWatcher.watch(file);\n\t\t\twatcher.addIFileChangeListener(new IFileChangeListener() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void onFileDelete(File fileThatIsDeleted) {\n\t\t\t\t\tBowlerStudioModularFrame.getBowlerStudioModularFrame().closeTab(fileTab);\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic void onFileChange(File fileThatChanged, WatchEvent event) {\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tt.setFontSize(size);\n\t\t\treturn t.getScripting();\n\t\t} catch (IOException e) {\n\t\t\t// Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic void clearHighlits() {\n\t\tfor (Entry<String, LocalFileScriptTab> set : widgets.entrySet()) {\n\t\t\tset.getValue().clearHighlits();\n\t\t}\n\t}\n\n\tpublic void setHighlight(File fileEngineRunByName, int lineNumber, java.awt.Color color) {\n\t\t// com.neuronrobotics.sdk.common.Log.error(\"Highlighting line \"+lineNumber+\" in\n\t\t// \"+fileEngineRunByName);\n\t\tif (openFiles.get(fileEngineRunByName.getAbsolutePath()) == null) {\n\t\t\tcreateFileTab(fileEngineRunByName);\n\t\t\tThreadUtil.wait(100);\n\t\t}\n\n\t\t// BowlerStudioModularFrame.getBowlerStudioModularFrame().setSelectedTab(openFiles.get(fileEngineRunByName.getAbsolutePath()));\n\t\t// com.neuronrobotics.sdk.common.Log.error(\"Highlighting \"+fileEngineRunByName+\"\n\t\t// at line\n\t\t// \"+lineNumber+\" to color \"+color);\n\t\ttry {\n\t\t\twidgets.get(fileEngineRunByName.getAbsolutePath()).setHighlight(lineNumber, color);\n\t\t} catch (Throwable e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t}\n\n\tpublic static void highlightException(File fileEngineRunByName, Throwable ex) {\n\t\tif (bowlerStudioControllerStaticReference != null)\n\t\t\tbowlerStudioControllerStaticReference.highlightExceptionLocal(fileEngineRunByName, ex);\n\t}\n\n\tpublic static void clearHighlight() {\n\t\tbowlerStudioControllerStaticReference.clearHighlits();\n\t}\n\n\tprivate void highlightExceptionLocal(File fileEngineRunByName, Throwable ex) {\n\t\t// THis needs to gate on checking if this thread is running already\n\t\tif (runningExceptionHighlight) {\n\t\t\tex.printStackTrace();\n\n\t\t\tnew RuntimeException(\"Only one exception Highlight can be called at once!\").printStackTrace();\n\t\t\treturn;\n\t\t}\n\n\t\tnew Thread() {\n\t\t\tpublic void run() {\n\t\t\t\trunningExceptionHighlight = true;\n\t\t\t\tsetName(\"Highlighter thread\");\n\t\t\t\tif (fileEngineRunByName != null) {\n\t\t\t\t\tif (openFiles.get(fileEngineRunByName.getAbsolutePath()) == null) {\n\t\t\t\t\t\tcreateFileTab(fileEngineRunByName);\n\t\t\t\t\t}\n\t\t\t\t\tBowlerStudioModularFrame.getBowlerStudioModularFrame()\n\t\t\t\t\t\t\t.setSelectedTab(openFiles.get(fileEngineRunByName.getAbsolutePath()));\n\t\t\t\t\ttry {\n\t\t\t\t\t\twidgets.get(fileEngineRunByName.getAbsolutePath()).clearHighlits();\n\t\t\t\t\t} catch (java.lang.NullPointerException e) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"Highlighting \"+fileEngineRunByName+\"\n\t\t\t\t\t// at line\n\t\t\t\t\t// \"+lineNumber+\" to color \"+color);\n\t\t\t\t\tStackTraceElement[] stackTrace = ex.getStackTrace();\n\n\t\t\t\t\tfor (StackTraceElement el : stackTrace) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"Compairing\n\t\t\t\t\t\t\t// \"+fileEngineRunByName.getName()+\" to\n\t\t\t\t\t\t\t// \"+el.getFileName());\n\t\t\t\t\t\t\tif (el.getFileName().contentEquals(fileEngineRunByName.getName())) {\n\t\t\t\t\t\t\t\twidgets.get(fileEngineRunByName.getAbsolutePath()).setHighlight(el.getLineNumber(),\n\t\t\t\t\t\t\t\t\t\tjava.awt.Color.CYAN);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\t// StringWriter sw = new StringWriter();\n\t\t\t\t\t\t\t// PrintWriter pw = new PrintWriter(sw);\n\t\t\t\t\t\t\t// e.printStackTrace(pw);\n\t\t\t\t\t\t\t// com.neuronrobotics.sdk.common.Log.error(sw.toString());\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (ex.getCause() != null) {\n\t\t\t\t\t\tfor (StackTraceElement el : ex.getCause().getStackTrace()) {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"Compairing\n\t\t\t\t\t\t\t\t// \"+fileEngineRunByName.getName()+\" to\n\t\t\t\t\t\t\t\t// \"+el.getFileName());\n\t\t\t\t\t\t\t\tif (el.getFileName().contentEquals(fileEngineRunByName.getName())) {\n\t\t\t\t\t\t\t\t\twidgets.get(fileEngineRunByName.getAbsolutePath()).setHighlight(el.getLineNumber(),\n\t\t\t\t\t\t\t\t\t\t\tjava.awt.Color.CYAN);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\t\t// StringWriter sw = new StringWriter();\n\t\t\t\t\t\t\t\t// PrintWriter pw = new PrintWriter(sw);\n\t\t\t\t\t\t\t\t// e.printStackTrace(pw);\n\t\t\t\t\t\t\t\t// com.neuronrobotics.sdk.common.Log.error(sw.toString());\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tif (widgets.get(fileEngineRunByName.getAbsolutePath()) != null) {\n\t\t\t\t\t\tString message = ex.getMessage();\n\t\t\t\t\t\t// com.neuronrobotics.sdk.common.Log.error(message);\n\t\t\t\t\t\tif (message != null && message.contains(fileEngineRunByName.getName()))\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tint indexOfFile = message.lastIndexOf(fileEngineRunByName.getName());\n\t\t\t\t\t\t\t\tString fileSub = message.substring(indexOfFile);\n\t\t\t\t\t\t\t\tString[] fileAndNum = fileSub.split(\":\");\n\t\t\t\t\t\t\t\tString FileNum = fileAndNum[1];\n\t\t\t\t\t\t\t\tint linNum = Integer.parseInt(FileNum.trim());\n\t\t\t\t\t\t\t\twidgets.get(fileEngineRunByName.getAbsolutePath()).setHighlight(linNum,\n\t\t\t\t\t\t\t\t\t\tjava.awt.Color.CYAN);\n\t\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\t\tStringWriter sw = new StringWriter();\n\t\t\t\t\t\t\t\tPrintWriter pw = new PrintWriter(sw);\n\t\t\t\t\t\t\t\te.printStackTrace(pw);\n\t\t\t\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.debug(sw.toString());\n\t\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception ex1) {\n\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tString sw = org.apache.commons.lang.exception.ExceptionUtils.getStackTrace(ex);\n\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.debug(sw.toString());\n\t\t\t\t\t// space out the exception highlights, ensure any sub threads spawned here have\n\t\t\t\t\t// time to finish\n\t\t\t\t\tThread.sleep(100);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\n\t\t\t\trunningExceptionHighlight = false;\n\t\t\t}\n\t\t}.start();\n\n\t}\n\n\tpublic void addTab(Tab tab, boolean closable) {\n\n\t\tException ex = new RuntimeException();\n\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tBowlerStudioModularFrame.getBowlerStudioModularFrame().addTab(tab, closable);\n\t\t}, ex);\n\n\t}\n\n\tpublic static boolean removeObject(Object p) {\n\t\tif (CSG.class.isInstance(p)) {\n\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\tCreatureLab3dController.getEngine().removeObject((CSG) p);\n\t\t\t});\n\t\t\treturn true;\n\t\t}\n\t\tif (Node.class.isInstance(p) || Polygon.class.isInstance(p)) {\n\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\tCreatureLab3dController.getEngine().clearUserNode();\n\t\t\t});\n\t\t\treturn true;\n\t\t}\n\t\tif (Tab.class.isInstance(p)) {\n\t\t\tTab newTab = (Tab) p;\n\t\t\tBowlerStudioModularFrame.getBowlerStudioModularFrame().closeTab(newTab);\n\t\t\treturn true;\n\t\t}\n\t\t// ThreadUtil.wait(20);\n\t\treturn false;\n\t}\n\n\t// private boolean removeObject(Object p) {\n\t// if (CSG.class.isInstance(p) || Node.class.isInstance(p) ||\n\t// Polygon.class.isInstance(p)) {\n\t// BowlerStudio.runLater(() -> {\n\t// CreatureLab3dController.getEngine().removeObjects();\n\t// CreatureLab3dController.getEngine().clearUserNode();\n\t// });\n\t// return true;\n\t// }\n\t// // ThreadUtil.wait(20);\n\t// return false;\n\t// }\n\n\tpublic static void setCsg(List<CSG> toadd, File source) {\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tCreatureLab3dController.getEngine().removeObjects();\n\t\t\tif (toadd != null)\n\t\t\t\tfor (CSG c : toadd) {\n\t\t\t\t\tif (c != null)\n\t\t\t\t\t\tBowlerStudio.runLater(() -> CreatureLab3dController.getEngine().addObject(c, source,\n\t\t\t\t\t\t\t\tc.getColor().getOpacity(), CSGDatabase.getInstance()));\n\t\t\t\t}\n\t\t});\n\t}\n\n\tpublic static void setCsg(List<CSG> toadd) {\n\t\tsetCsg(toadd, null);\n\t}\n\n\tpublic static void addCsg(CSG toadd) {\n\t\taddCsg(toadd, null);\n\t}\n\n\tpublic static void setUserNode(List<Node> toadd) {\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tCreatureLab3dController.getEngine().clearUserNode();\n\t\t\tif (toadd != null)\n\t\t\t\tfor (Node c : toadd) {\n\t\t\t\t\tCreatureLab3dController.getEngine().addUserNode(c);\n\t\t\t\t}\n\t\t});\n\t}\n\n\tpublic static void addUserNode(Node toadd) {\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tif (toadd != null)\n\t\t\t\tCreatureLab3dController.getEngine().addUserNode(toadd);\n\n\t\t});\n\t}\n\n\tpublic static void setSelectedCsg(CSG obj) {\n\t\tCreatureLab3dController.getEngine().setSelectedCsg(obj);\n\t}\n\n\tpublic static void highlightCsg(CSG obj) {\n\t\tCreatureLab3dController.getEngine().setSelectedCsg(obj, true);\n\t}\n\n\tpublic static void setSelectedCsg(Vector3d v) {\n\t\tAffine manipulator2 = new Affine();\n\t\tTransformNR poseToMove = new TransformNR(v.x, v.y, v.z, new RotationNR());\n\t\tCreatureLab3dController.getEngine().focusToAffine(poseToMove, manipulator2);\n\t}\n\n\tpublic static void setSelectedCsg(TransformNR poseToMove) {\n\t\tAffine manipulator2 = new Affine();\n\t\tCreatureLab3dController.getEngine().focusToAffine(poseToMove, manipulator2);\n\t}\n\n\tpublic static void setSelectedAffine(TransformNR poseToMove, Affine manipulator2) {\n\t\tCreatureLab3dController.getEngine().focusToAffine(poseToMove, manipulator2);\n\t}\n\n\tpublic static void targetAndFollow(TransformNR poseToMove, Affine manipulator2) {\n\t\tCreatureLab3dController.getEngine().targetAndFollow(poseToMove, manipulator2);\n\t}\n\n\tpublic static void setSelectedAffine(Affine af) {\n\t\tCreatureLab3dController.getEngine().focusToAffine(af);\n\t}\n\n\tpublic static void addCsg(CSG toadd, File source) {\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tif (toadd != null)\n\t\t\t\tCreatureLab3dController.getEngine().addObject(toadd, source, toadd.getColor().getOpacity(),\n\t\t\t\t\t\tCSGDatabase.getInstance());\n\n\t\t});\n\t}\n\n\tpublic static void addObject(Object o, File source) {\n\t\taddObject(o, source, null);\n\t}\n\n\tpublic static void addObject(Object o, File source, ArrayList<CSG> cache) {\n\n\t\tCreatureLab3dController.getEngine().addObject(o, source, cache);\n\n\t}\n\n\tpublic static MeshView createPolygonOutlineMesh(List<Vertex> vertices) {\n\t\tTriangleMesh mesh = new TriangleMesh();\n\n\t\t// This is a simplified approach - for true line rendering in 3D,\n\t\t// you might want to use a more sophisticated mesh generation\n\t\t// or consider using external 3D libraries like FXYZ3D\n\n\t\t// Add vertices to mesh (this creates a basic wireframe approximation)\n\t\tfor (Vertex v : vertices) {\n\t\t\tmesh.getPoints().addAll((float) v.getX(), (float) v.getY(), (float) v.getZ());\n\t\t}\n\n\t\t// Add texture coordinates (required but minimal for wireframe)\n\t\tmesh.getTexCoords().addAll(0, 0);\n\n\t\t// Add faces (connecting consecutive vertices)\n\t\tfor (int i = 0; i < vertices.size(); i++) {\n\t\t\tint next = (i + 1) % vertices.size();\n\t\t\t// Create degenerate triangles for line effect\n\t\t\tmesh.getFaces().addAll(i, 0, next, 0, i, 0);\n\t\t}\n\n\t\tMeshView meshView = new MeshView(mesh);\n\t\tmeshView.setDrawMode(javafx.scene.shape.DrawMode.LINE); // Wireframe mode\n\n\t\treturn meshView;\n\t}\n\n\tpublic void addNode(Node o) {\n\t\tCreatureLab3dController.getEngine().addUserNode(o);\n\t}\n\n\t@SuppressWarnings({\"unchecked\"})\n\t@Override\n\tpublic void onScriptFinished(Object result, Object Previous, File source) {\n\t\tLog.warning(\"Loading script results \" + result + \" previous \" + Previous);\n\t\t// this is added in the script engine when the connection manager is\n\t\t// loaded\n\t\tclearObjects(Previous);\n\t\tclearObjects(result);\n\t\tThreadUtil.wait(40);\n\t\tArrayList<CSG> cache = new ArrayList<>();\n\t\tif (List.class.isInstance(result)) {\n\t\t\tList<Object> c = (List<Object>) result;\n\t\t\tfor (int i = 0; i < c.size(); i++) {\n\t\t\t\t// Log.warning(\"Loading array Lists with removals \" + c.get(i));\n\t\t\t\taddObject(c.get(i), source, cache);\n\t\t\t}\n\t\t} else {\n\t\t\taddObject(result, source, cache);\n\t\t}\n\t\tif (cache.size() > 0)\n\t\t\taddObject(cache, source, null);\n\t\t// String git;\n\t\t// try {\n\t\t// git = ScriptingEngine.locateGitUrl(source);\n\t\t// if(cache.size()>0) {\n\t\t// if(git!=null) {\n\t\t// PrintBedManager manager=new PrintBedManager(git,cache);\n\t\t// addObject(manager.get(), source);\n\t\t// }else {\n\t\t// addObject(cache, source,null);\n\t\t// }\n\t\t// }\n\t\t// } catch (Exception e) {\n\t\t// // Auto-generated catch block\n\t\t// e.printStackTrace();\n\t\t// addObject(cache, source,null);\n\t\t// }\n\n\t}\n\n\tprivate void clearObjects(Object o) {\n\t\tif (List.class.isInstance(o)) {\n\t\t\t@SuppressWarnings(\"unchecked\")\n\t\t\tList<Object> c = (List<Object>) o;\n\t\t\tfor (int i = 0; i < c.size(); i++) {\n\t\t\t\tclearObjects(c.get(i));\n\t\t\t}\n\t\t} else {\n\t\t\tremoveObject(o);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onScriptChanged(String previous, String current, File source) {\n\n\t}\n\n\t@Override\n\tpublic void onScriptError(Throwable except, File source) {\n\t\t// Auto-generated method stub\n\n\t}\n\n\tpublic void disconnect() {\n\t\tConnectionManager.disconnectAll();\n\t}\n\n\tpublic Stage getPrimaryStage() {\n\t\t// Auto-generated method stub\n\t\treturn BowlerStudioModularFrame.getPrimaryStage();\n\t}\n\n\tpublic AbstractImageProvider getVrCamera() {\n\t\treturn vrCamera;\n\t}\n\n\tpublic void setVrCamera(AbstractImageProvider vrCamera) {\n\t\tthis.vrCamera = vrCamera;\n\t}\n\n\tpublic static BowlerStudioController getBowlerStudio() {\n\t\treturn bowlerStudioControllerStaticReference;\n\t}\n\n\tpublic static void setup() {\n\t\t// Auto-generated method stub\n\n\t}\n\n\tpublic static void clearCSG() {\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tCreatureLab3dController.getEngine().removeObjects();\n\t\t});\n\t}\n\n\tpublic static void clearUserNodes() {\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tCreatureLab3dController.getEngine().clearUserNode();\n\t\t});\n\t}\n\n\tpublic static void setCsg(CSG legAssembly, File cadScript) {\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tCreatureLab3dController.getEngine().removeObjects();\n\t\t\tif (legAssembly != null)\n\n\t\t\t\tBowlerStudio.runLater(() -> CreatureLab3dController.getEngine().addObject(legAssembly, cadScript));\n\n\t\t});\n\t}\n\n\tpublic static void setCsg(MobileBaseCadManager thread, File cadScript) {\n\t\tsetCsg(thread.getAllCad(), cadScript);\n\t}\n\n\tpublic boolean isDoneLoadingTutorials() {\n\t\treturn doneLoadingTutorials;\n\t}\n\n\tpublic void setDoneLoadingTutorials(boolean doneLoadingTutorials) {\n\t\tthis.doneLoadingTutorials = doneLoadingTutorials;\n\t}\n\n\tpublic ConnectionManager getConnectionManager() {\n\t\treturn connectionManager;\n\t}\n\n\tpublic void setConnectionManager(ConnectionManager connectionManager) {\n\t\tthis.connectionManager = connectionManager;\n\t}\n\n\tpublic static IMobileBaseUI getMobileBaseUI() {\n\t\treturn mbui;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/BowlerStudioFXMLController.java",
    "content": "package com.neuronrobotics.bowlerstudio;\n\nimport java.net.URL;\nimport java.util.ResourceBundle;\nimport javafx.event.ActionEvent;\nimport javafx.fxml.FXML;\nimport javafx.scene.control.Menu;\nimport javafx.scene.control.MenuBar;\nimport javafx.scene.control.MenuItem;\nimport javafx.scene.control.ScrollPane;\nimport javafx.scene.control.SplitPane;\nimport javafx.scene.control.TextArea;\nimport javafx.scene.control.TitledPane;\nimport javafx.scene.layout.AnchorPane;\n\npublic class BowlerStudioFXMLController {\n\n\t@FXML // ResourceBundle that was given to the FXMLLoader\n\tprivate ResourceBundle resources;\n\n\t@FXML // URL location of the FXML file that was given to the FXMLLoader\n\tprivate URL location;\n\n\t@FXML // fx:id=\"BowlerStudioMenue\"\n\tprivate MenuBar BowlerStudioMenue; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"CadControlsAnchor\"\n\tprivate AnchorPane CadControlsAnchor; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"CadTextSplit\"\n\tprivate SplitPane CadTextSplit; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"CommandLine\"\n\tprivate AnchorPane CommandLine; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"CreaturesMenu\"\n\tprivate Menu CreaturesMenu; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"DriveControlsAnchor\"\n\tprivate AnchorPane DriveControlsAnchor; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"GitHubRoot\"\n\tprivate Menu GitHubRoot; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"TempControlsAnchor\"\n\tprivate AnchorPane TempControlsAnchor; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"clearCache\"\n\tprivate MenuItem clearCache; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"commandLineTitledPane\"\n\tprivate TitledPane commandLineTitledPane; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"createNewGist\"\n\tprivate MenuItem createNewGist; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"editorContainer\"\n\tprivate AnchorPane editorContainer; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"jfx3dControls\"\n\tprivate AnchorPane jfx3dControls; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"logView\"\n\tprivate AnchorPane logView; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"logViewRef\"\n\tprivate TextArea logViewRef; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"logoutGithub\"\n\tprivate MenuItem logoutGithub; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"myGists\"\n\tprivate Menu myGists; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"myOrganizations\"\n\tprivate Menu myOrganizations; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"myRepos\"\n\tprivate Menu myRepos; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"overlayScrollPanel\"\n\tprivate ScrollPane overlayScrollPanel; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"viewContainer\"\n\tprivate AnchorPane viewContainer; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"watchingRepos\"\n\tprivate Menu watchingRepos; // Value injected by FXMLLoader\n\n\t// Handler for MenuItem[fx:id=\"clearCache\"] onAction\n\t@FXML\n\tvoid clearScriptCache(ActionEvent event) {\n\t\t// handle the event here\n\t}\n\n\t// Handler for MenuItem[javafx.scene.control.MenuItem@3d05e65e] onAction\n\t@FXML\n\tvoid onClose(ActionEvent event) {\n\t\t// handle the event here\n\t}\n\n\t// Handler for MenuItem[javafx.scene.control.MenuItem@6127215f] onAction\n\t@FXML\n\tvoid onConnect(ActionEvent event) {\n\t\t// handle the event here\n\t}\n\n\t// Handler for MenuItem[javafx.scene.control.MenuItem@1d163e1a] onAction\n\t@FXML\n\tvoid onConnectCHDKCamera(ActionEvent event) {\n\t\t// handle the event here\n\t}\n\n\t// Handler for MenuItem[javafx.scene.control.MenuItem@3cad92b8] onAction\n\t@FXML\n\tvoid onConnectCVCamera(ActionEvent event) {\n\t\t// handle the event here\n\t}\n\n\t// Handler for MenuItem[javafx.scene.control.MenuItem@542487b1] onAction\n\t@FXML\n\tvoid onConnectFileSourceCamera(ActionEvent event) {\n\t\t// handle the event here\n\t}\n\n\t// Handler for MenuItem[javafx.scene.control.MenuItem@4845d9d8] onAction\n\t@FXML\n\tvoid onConnectGamePad(ActionEvent event) {\n\t\t// handle the event here\n\t}\n\n\t// Handler for MenuItem[javafx.scene.control.MenuItem@36100c4d] onAction\n\t@FXML\n\tvoid onConnectHokuyoURG(ActionEvent event) {\n\t\t// handle the event here\n\t}\n\n\t// Handler for MenuItem[javafx.scene.control.MenuItem@7a7cbb68] onAction\n\t@FXML\n\tvoid onConnectPidSim(ActionEvent event) {\n\t\t// handle the event here\n\t}\n\n\t// Handler for MenuItem[javafx.scene.control.MenuItem@7d3d14a7] onAction\n\t@FXML\n\tvoid onConnectURLSourceCamera(ActionEvent event) {\n\t\t// handle the event here\n\t}\n\n\t// Handler for MenuItem[javafx.scene.control.MenuItem@68e8dfb1] onAction\n\t@FXML\n\tvoid onConnectVirtual(ActionEvent event) {\n\t\t// handle the event here\n\t}\n\n\t// Handler for MenuItem[fx:id=\"createNewGist\"] onAction\n\t@FXML\n\tvoid onCreatenewGist(ActionEvent event) {\n\t\t// handle the event here\n\t}\n\n\t// Handler for MenuItem[javafx.scene.control.MenuItem@6bfd67ba] onAction\n\t@FXML\n\tvoid onLoadFile(ActionEvent event) {\n\t\t// handle the event here\n\t}\n\n\t// Handler for MenuItem[javafx.scene.control.MenuItem@41a2e40f] onAction\n\t@FXML\n\tvoid onLogin(ActionEvent event) {\n\t\t// handle the event here\n\t}\n\n\t// Handler for MenuItem[fx:id=\"logoutGithub\"] onAction\n\t@FXML\n\tvoid onLogout(ActionEvent event) {\n\t\t// handle the event here\n\t}\n\n\t// Handler for MenuItem[javafx.scene.control.MenuItem@7e3e1a61] onAction\n\t@FXML\n\tvoid onMobileBaseFromFile(ActionEvent event) {\n\t\t// handle the event here\n\t}\n\n\t// Handler for MenuItem[javafx.scene.control.MenuItem@57184345] onAction\n\t@FXML\n\tvoid onMobileBaseFromGit(ActionEvent event) {\n\t\t// handle the event here\n\t}\n\n\t// Handler for MenuItem[javafx.scene.control.MenuItem@d523af5] onAction\n\t@FXML\n\tvoid onOpenGitter(ActionEvent event) {\n\t\t// handle the event here\n\t}\n\n\t// Handler for MenuItem[javafx.scene.control.MenuItem@2a1c81e] onAction\n\t@FXML\n\tvoid onPrint(ActionEvent event) {\n\t\t// handle the event here\n\t}\n\n\t@FXML // This method is called by the FXMLLoader when initialization is complete\n\tvoid initialize() {\n\t\tassert BowlerStudioMenue != null\n\t\t\t\t: \"fx:id=\\\"BowlerStudioMenue\\\" was not injected: check your FXML file 'Main.fxml'.\";\n\t\tassert CadControlsAnchor != null\n\t\t\t\t: \"fx:id=\\\"CadControlsAnchor\\\" was not injected: check your FXML file 'Main.fxml'.\";\n\t\tassert CadTextSplit != null : \"fx:id=\\\"CadTextSplit\\\" was not injected: check your FXML file 'Main.fxml'.\";\n\t\tassert CommandLine != null : \"fx:id=\\\"CommandLine\\\" was not injected: check your FXML file 'Main.fxml'.\";\n\t\tassert CreaturesMenu != null : \"fx:id=\\\"CreaturesMenu\\\" was not injected: check your FXML file 'Main.fxml'.\";\n\t\tassert DriveControlsAnchor != null\n\t\t\t\t: \"fx:id=\\\"DriveControlsAnchor\\\" was not injected: check your FXML file 'Main.fxml'.\";\n\t\tassert GitHubRoot != null : \"fx:id=\\\"GitHubRoot\\\" was not injected: check your FXML file 'Main.fxml'.\";\n\t\tassert TempControlsAnchor != null\n\t\t\t\t: \"fx:id=\\\"TempControlsAnchor\\\" was not injected: check your FXML file 'Main.fxml'.\";\n\t\tassert clearCache != null : \"fx:id=\\\"clearCache\\\" was not injected: check your FXML file 'Main.fxml'.\";\n\t\tassert commandLineTitledPane != null\n\t\t\t\t: \"fx:id=\\\"commandLineTitledPane\\\" was not injected: check your FXML file 'Main.fxml'.\";\n\t\tassert createNewGist != null : \"fx:id=\\\"createNewGist\\\" was not injected: check your FXML file 'Main.fxml'.\";\n\t\tassert editorContainer != null\n\t\t\t\t: \"fx:id=\\\"editorContainer\\\" was not injected: check your FXML file 'Main.fxml'.\";\n\t\tassert jfx3dControls != null : \"fx:id=\\\"jfx3dControls\\\" was not injected: check your FXML file 'Main.fxml'.\";\n\t\tassert logView != null : \"fx:id=\\\"logView\\\" was not injected: check your FXML file 'Main.fxml'.\";\n\t\tassert logViewRef != null : \"fx:id=\\\"logViewRef\\\" was not injected: check your FXML file 'Main.fxml'.\";\n\t\tassert logoutGithub != null : \"fx:id=\\\"logoutGithub\\\" was not injected: check your FXML file 'Main.fxml'.\";\n\t\tassert myGists != null : \"fx:id=\\\"myGists\\\" was not injected: check your FXML file 'Main.fxml'.\";\n\t\tassert myOrganizations != null\n\t\t\t\t: \"fx:id=\\\"myOrganizations\\\" was not injected: check your FXML file 'Main.fxml'.\";\n\t\tassert myRepos != null : \"fx:id=\\\"myRepos\\\" was not injected: check your FXML file 'Main.fxml'.\";\n\t\tassert overlayScrollPanel != null\n\t\t\t\t: \"fx:id=\\\"overlayScrollPanel\\\" was not injected: check your FXML file 'Main.fxml'.\";\n\t\tassert viewContainer != null : \"fx:id=\\\"viewContainer\\\" was not injected: check your FXML file 'Main.fxml'.\";\n\t\tassert watchingRepos != null : \"fx:id=\\\"watchingRepos\\\" was not injected: check your FXML file 'Main.fxml'.\";\n\n\t\t// Initialize your logic here: all @FXML variables will have been injected\n\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/BowlerStudioMenu.java",
    "content": "package com.neuronrobotics.bowlerstudio;\n\n/**\n * Sample Skeleton for \"BowlerStudioMenuBar.fxml\" Controller Class\n * You can copy and paste this code into your favorite IDE\n **/\n\nimport com.google.common.collect.Lists;\nimport com.neuronrobotics.bowlerstudio.assets.AssetFactory;\nimport com.neuronrobotics.bowlerstudio.assets.ConfigurationDatabase;\nimport com.neuronrobotics.bowlerstudio.assets.FontSizeManager;\nimport com.neuronrobotics.bowlerstudio.scripting.IGithubLoginListener;\nimport com.neuronrobotics.bowlerstudio.scripting.PasswordManager;\nimport com.neuronrobotics.bowlerstudio.scripting.ScriptingEngine;\nimport com.neuronrobotics.bowlerstudio.tabs.LocalFileScriptTab;\nimport com.neuronrobotics.bowlerstudio.vitamins.Vitamins;\n//import com.neuronrobotics.imageprovider.CHDKImageProvider;\nimport com.neuronrobotics.nrconsole.util.FileSelectionFactory;\nimport com.neuronrobotics.nrconsole.util.PromptForGit;\nimport com.neuronrobotics.pidsim.LinearPhysicsEngine;\nimport com.neuronrobotics.sdk.addons.kinematics.MobileBase;\nimport com.neuronrobotics.sdk.pid.VirtualGenericPIDDevice;\nimport com.neuronrobotics.sdk.util.ThreadUtil;\n\nimport eu.mihosoft.vrl.v3d.CSG;\nimport eu.mihosoft.vrl.v3d.parametrics.CSGDatabase;\n\nimport javafx.collections.ObservableList;\nimport javafx.event.ActionEvent;\nimport javafx.event.Event;\nimport javafx.event.EventHandler;\nimport javafx.fxml.FXML;\nimport javafx.scene.Node;\nimport javafx.scene.control.*;\nimport javafx.scene.control.Alert.AlertType;\nimport javafx.stage.FileChooser.ExtensionFilter;\nimport javafx.stage.Stage;\nimport javafx.scene.layout.*;\n\nimport org.eclipse.jgit.api.errors.GitAPIException;\nimport org.eclipse.jgit.api.errors.NoHeadException;\nimport org.eclipse.jgit.api.errors.WrongRepositoryStateException;\nimport org.eclipse.jgit.errors.RevisionSyntaxException;\nimport org.eclipse.jgit.lib.ObjectId;\nimport org.eclipse.jgit.lib.Ref;\nimport org.eclipse.jgit.lib.Repository;\nimport org.eclipse.jgit.revwalk.RevCommit;\nimport org.kohsuke.github.*;\n\nimport java.awt.Desktop;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.net.MalformedURLException;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.net.URL;\nimport java.text.Normalizer;\nimport java.text.Normalizer.Form;\nimport java.text.SimpleDateFormat;\nimport java.util.*;\nimport java.util.function.Consumer;\nimport java.util.regex.Pattern;\nimport java.util.stream.Collectors;\n\npublic class BowlerStudioMenu implements MenuRefreshEvent, INewVitaminCallback {\n\n\t@FXML // ResourceBundle that was given to the FXMLLoader\n\tprivate ResourceBundle resources;\n\n\t@FXML // URL location of the FXML file that was given to the FXMLLoader\n\tprivate URL location;\n\n\t@FXML // fx:id=\"CreaturesMenu\"\n\tprivate Menu CreaturesMenu; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"GitHubRoot\"\n\tprivate Menu GitHubRoot; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"workspacemenu\"\n\tprivate Menu workspacemenuHandle; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"MeneBarBowlerStudio\"\n\tprivate MenuBar MeneBarBowlerStudio; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"addMarlinGCODEDevice\"\n\tprivate MenuItem addMarlinGCODEDevice; // Value injected by FXMLLoader\n\t@FXML // fx:id=\"addMarlinGCODEDevice\"\n\tprivate MenuItem loadFirmata; // Value injected by FXMLLoader\n\t@FXML // fx:id=\"clearCache\"\n\tprivate MenuItem clearCache; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"createNewGist\"\n\tprivate MenuItem createNewGist; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"logoutGithub\"\n\tprivate MenuItem logoutGithub; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"myGists\"\n\tprivate Menu myGists; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"myOrganizations\"\n\tprivate Menu myOrganizations; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"myRepos\"\n\tprivate Menu myRepos; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"showDevicesPanel\"\n\tprivate MenuItem showDevicesPanel; // Value injected by FXMLLoader\n\t@FXML // fx:id=\"showCreatureLab\"\n\tprivate MenuItem showCreatureLab; // Value injected by FXMLLoader\n\t@FXML // fx:id=\"showTerminal\"\n\tprivate MenuItem showTerminal;\n\t@FXML // fx:id=\"showTerminal\"\n\tprivate Menu WindowMenu;\n\t@FXML // fx:id=\"watchingRepos\"\n\tprivate Menu watchingRepos; // Value injected by FXMLLoader\n\t@FXML\n\tprivate Menu vitaminsMenu;\n\n\t@FXML\n\tprivate MenuItem addNewVitamin;\n\n\tprivate BowlerStudioModularFrame bowlerStudioModularFrame;\n\n\tprivate String username;\n\tprivate static BowlerStudioMenu selfRef = null;\n\tprivate File openFile;\n\tprivate Map<String, GHRepository> myPublic;\n\t// PagedIterable<GHGist> gists ;\n\tprivate HashMap<String, String> messages = new HashMap<String, String>();\n\tprivate static SimpleDateFormat format = new SimpleDateFormat(\"E 'the' dd 'in' MMM-yyyy 'at' HH:mm\");\n\tprivate static SimpleDateFormat formatSimple = new SimpleDateFormat(\"MM-dd\");\n\tprivate static IssueReportingExceptionHandler exp = new IssueReportingExceptionHandler();\n\tprivate static final Pattern NONLATIN = Pattern.compile(\"[^\\\\w-]\");\n\tprivate static final Pattern WHITESPACE = Pattern.compile(\"[\\\\s]\");\n\tprivate HashMap<String, Menu> vitaminTypeMenus = new HashMap<String, Menu>();\n\n\tprivate CreatureLab3dController creatureLab3dController;\n\n\tpublic BowlerStudioMenu(BowlerStudioModularFrame tl, CreatureLab3dController creatureLab3dController) {\n\t\tbowlerStudioModularFrame = tl;\n\t\tthis.creatureLab3dController = creatureLab3dController;\n\n\t}\n\n\t@FXML\n\tpublic void onMobileBaseFromGist(ActionEvent event) {\n\t\tPromptForGit.prompt(\"Select a Creature From a Gist\", \"bcb4760a449190206170\", (gitsId, file) -> {\n\t\t\tloadMobilebaseFromGist(gitsId, file);\n\t\t});\n\t}\n\n\tpublic void loadMobilebaseFromGist(String id, String file) {\n\t\tloadMobilebaseFromGit(\"https://gist.github.com/\" + id + \".git\", file);\n\t}\n\n\tpublic MenuBar getMeneBarBowlerStudio() {\n\t\treturn MeneBarBowlerStudio;\n\t}\n\n\tpublic void setMeneBarBowlerStudio(MenuBar meneBarBowlerStudio) {\n\t\tMeneBarBowlerStudio = meneBarBowlerStudio;\n\t}\n\n\tpublic void loadMobilebaseFromGit(String id, String file) {\n\t\tnew Thread() {\n\t\t\tException ex = new Exception(\"Error Loading \" + id + \":\" + file);\n\n\t\t\t// String stacktraceFromCatch =\n\t\t\t// org.apache.commons.lang.exception.ExceptionUtils.getStackTrace(ex);\n\t\t\tpublic void run() {\n\t\t\t\tFile f = null;\n\t\t\t\ttry {\n\t\t\t\t\tf = ScriptingEngine.fileFromGit(id, file);\n\t\t\t\t\trunScriptFromGit(id, file);\n\t\t\t\t} catch (Throwable e) {\n\t\t\t\t\tSystem.err.println(\"Error Loading \" + id + \":\" + file);\n\t\t\t\t\tBowlerStudio.printStackTrace(e, f);\n\t\t\t\t\tBowlerStudio.printStackTrace(ex, f);\n\t\t\t\t\t// exp.except(ex,stacktraceFromCatch);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tprivate void runScriptFromGit(String id, String file) throws Exception {\n\t\t\t\tMobileBase mb;\n\t\t\t\tScriptingEngine.pull(id);\n\n\t\t\t\tmb = (MobileBase) ScriptingEngine.gitScriptRun(CSGDatabase.getInstance(), id, file, null);\n\n\t\t\t\tif (mb != null)\n\t\t\t\t\tConnectionManager.addConnection(mb, mb.getScriptingName());\n\t\t\t\telse\n\t\t\t\t\tSystem.err.println(\"\\r\\n\\r\\nNO MOBILE BASE found at \" + id + \"\\t\" + file);\n\t\t\t}\n\t\t}.start();\n\n\t}\n\n\tpublic void openUrlInNewTab(URL url) {\n\t\tbowlerStudioModularFrame.openUrlInNewTab(url);\n\t}\n\n\tpublic void setToLoggedOut() {\n\t\tthis.username = \"\";\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tmyGists.getItems().clear();\n\t\t\tlogoutGithub.disableProperty().set(true);\n\t\t\tlogoutGithub.setText(\"Anonymous\");\n\t\t\t// ConfigurationDatabase.loginEvent(null);\n\t\t\tthis.username = null;\n\t\t});\n\t\twhile (this.username != null)\n\t\t\tThreadUtil.wait(4);\n\n\t}\n\n\tpublic void setToLoggedIn() {\n\t\tsetToLoggedIn(username);\n\t}\n\n\tprivate void setToLoggedIn(final String n) {\n\t\t// new Exception().printStackTrace();\n\t\tif (n == null)\n\t\t\treturn;\n\t\tthis.username = n;\n\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tlogoutGithub.disableProperty().set(false);\n\t\t\tlogoutGithub.setText(\"Log out \" + username);\n\t\t\tnew Thread() {\n\t\t\t\tpublic void run() {\n\t\t\t\t\t// ConfigurationDatabase.loginEvent(username);\n\t\t\t\t\t// ConfigurationDatabase.getParamMap(\"workspace\");\n\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.debug(\"Login Success \" + n);\n\t\t\t\t\tBowlerStudioMenuWorkspace.loginEvent();\n\t\t\t\t\tif (!PasswordManager.hasNetwork())\n\t\t\t\t\t\treturn;\n\t\t\t\t\tGitHub gh = PasswordManager.getGithub();\n\t\t\t\t\twhile (gh == null || !PasswordManager.loggedIn()) {\n\t\t\t\t\t\tgh = PasswordManager.getGithub();\n\t\t\t\t\t\tThreadUtil.wait(200);\n\t\t\t\t\t}\n\t\t\t\t\tnew Thread(() -> {\n\t\t\t\t\t\topenFilesInUI();\n\t\t\t\t\t}).start();\n\t\t\t\t\tGitHub github = gh;\n\t\t\t\t\tloadOrganizations(github);\n\t\t\t\t\tloadMyRepos(github);\n\t\t\t\t\tloadWatchingRepos(github);\n\t\t\t\t\tLoadGistMenu(github);\n\n\t\t\t\t}\n\n\t\t\t}.start();\n\n\t\t});\n\n\t}\n\n\tprivate void openFilesInUI() {\n\t\tString key = \"studio-open-file\";\n\t\t// HashMap<String, Object> openGits =\n\t\t// ConfigurationDatabase.getParamMap(\"studio-open-file\");\n\t\tObject[] set = ConfigurationDatabase.keySet(\"studio-open-file\").toArray();\n\t\tfor (int i = 0; i < set.length; i++) {\n\t\t\ttry {\n\t\t\t\tThread.sleep(300);\n\t\t\t} catch (InterruptedException e1) {\n\t\t\t\te1.printStackTrace();\n\t\t\t}\n\t\t\tif (String.class.isInstance(set[i])) {\n\t\t\t\tString s = (String) set[i];\n\t\t\t\ttry {\n\t\t\t\t\t@SuppressWarnings(\"unchecked\")\n\t\t\t\t\tArrayList<String> repoFile = (ArrayList<String>) ConfigurationDatabase.getObject(key, s,\n\t\t\t\t\t\t\tnew ArrayList<>());\n\t\t\t\t\tFile f = ScriptingEngine.fileFromGit(repoFile.get(0), repoFile.get(1));\n\t\t\t\t\tif (!f.exists() || (BowlerStudio.createFileTab(f) == null)) {\n\t\t\t\t\t\tConfigurationDatabase.removeObject(key, s);\n\t\t\t\t\t\tSystem.err.println(\"Removing missing \" + s);\n\t\t\t\t\t}\n\n\t\t\t\t} catch (Throwable e) {\n\t\t\t\t\tConfigurationDatabase.removeObject(key, s);\n\t\t\t\t\tSystem.err.println(\"Error loading file \" + s);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// HashMap<String, Object> openWeb =\n\t\t// ConfigurationDatabase.getParamMap(\"studio-open-web\");\n\t\tString webKey = \"studio-open-web\";\n\t\tfor (String s : ConfigurationDatabase.keySet(webKey)) {\n\t\t\tString repoFile = (String) ConfigurationDatabase.getObject(webKey, s, null);\n\t\t\tif (repoFile != null)\n\t\t\t\ttry {\n\t\t\t\t\tbowlerStudioModularFrame.openUrlInNewTab(new URI(repoFile).toURL());\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\texp.uncaughtException(Thread.currentThread(), e);\n\t\t\t\t}\n\t\t}\n\t}\n\n\tprivate void loadWatchingRepos(GitHub github) {\n\t\tnew Thread(() -> {\n\t\t\tBowlerStudio.runLater(() -> watchingRepos.getItems().clear());\n\t\t\tThreadUtil.wait(20);\n\t\t\tGHMyself self;\n\t\t\ttry {\n\t\t\t\tself = github.getMyself();\n\t\t\t\t// Watched repos\n\t\t\t\tList<GHRepository> watching = self.listSubscriptions().asList();\n\t\t\t\tHashMap<String, Menu> ownerMenue = new HashMap<>();\n\t\t\t\tfor (GHRepository g : watching) {\n\t\t\t\t\tif (ownerMenue.get(g.getOwnerName()) == null) {\n\t\t\t\t\t\townerMenue.put(g.getOwnerName(), new Menu(g.getOwnerName()));\n\t\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\twatchingRepos.getItems().add(ownerMenue.get(g.getOwnerName()));\n\t\t\t\t\t\t\t} catch (Exception e) {\n\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tresetRepoMenue(ownerMenue.get(g.getOwnerName()), g);\n\t\t\t\t}\n\t\t\t} catch (IOException e1) {\n\t\t\t\tnew IssueReportingExceptionHandler().uncaughtException(Thread.currentThread(), e1);\n\n\t\t\t}\n\t\t}).start();\n\t}\n\n\tprivate void loadMyRepos(GitHub github) {\n\t\tnew Thread(() -> {\n\t\t\tBowlerStudio.runLater(() -> myRepos.getItems().clear());\n\t\t\tThreadUtil.wait(20);\n\t\t\t// Repos I own\n\t\t\ttry {\n\t\t\t\tGHMyself self = github.getMyself();\n\t\t\t\tmyPublic = self.getAllRepositories();\n\t\t\t\tHashMap<String, Menu> myownerMenue = new HashMap<>();\n\t\t\t\tfor (Map.Entry<String, GHRepository> entry : myPublic.entrySet()) {\n\t\t\t\t\tGHRepository g = entry.getValue();\n\t\t\t\t\tif (myownerMenue.get(g.getOwnerName()) == null) {\n\t\t\t\t\t\tmyownerMenue.put(g.getOwnerName(), new Menu(g.getOwnerName()));\n\t\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\t\tString ownerName = g.getOwnerName();\n\t\t\t\t\t\t\tif (ownerName == null)\n\t\t\t\t\t\t\t\tthrow new RuntimeException(\"ownerName can not be null\");\n\t\t\t\t\t\t\tMenu e = myownerMenue.get(ownerName);\n\t\t\t\t\t\t\tif (e == null)\n\t\t\t\t\t\t\t\tthrow new RuntimeException(\"Menu can not be null\");\n\t\t\t\t\t\t\tObservableList<MenuItem> items = myRepos.getItems();\n\t\t\t\t\t\t\tif (items == null)\n\t\t\t\t\t\t\t\tthrow new RuntimeException(\"Menue items can not be null\");\n\t\t\t\t\t\t\titems.add(e);\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\tresetRepoMenue(myownerMenue.get(g.getOwnerName()), g);\n\t\t\t\t}\n\t\t\t} catch (Exception ex) {\n\t\t\t\tnew IssueReportingExceptionHandler().uncaughtException(Thread.currentThread(), ex);\n\t\t\t\t// i have no public repso\n\t\t\t}\n\t\t}).start();\n\t}\n\n\tprivate void loadOrganizations(GitHub github) {\n\t\tnew Thread(() -> {\n\t\t\tBowlerStudio.runLater(() -> myOrganizations.getItems().clear());\n\t\t\tThreadUtil.wait(20);\n\n\t\t\tMap<String, GHOrganization> orgs;\n\t\t\ttry {\n\t\t\t\torgs = github.getMyOrganizations();\n\t\t\t\tfor (Map.Entry<String, GHOrganization> entry : orgs.entrySet()) {\n\t\t\t\t\t// System.err.println(\"Org: \"+org);\n\t\t\t\t\tMenu OrgItem = new Menu(entry.getKey());\n\t\t\t\t\tGHOrganization ghorg = entry.getValue();\n\t\t\t\t\tMap<String, GHRepository> repos = ghorg.getRepositories();\n\t\t\t\t\tfor (Map.Entry<String, GHRepository> entry1 : repos.entrySet()) {\n\t\t\t\t\t\tresetRepoMenue(OrgItem, entry1.getValue());\n\t\t\t\t\t}\n\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\tmyOrganizations.getItems().add(OrgItem);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tPasswordManager.checkInternet();\n\t\t\t\tif (PasswordManager.hasNetwork())\n\t\t\t\t\tnew IssueReportingExceptionHandler().uncaughtException(Thread.currentThread(), e);\n\n\t\t\t}\n\n\t\t}).start();\n\t}\n\n\tprivate void LoadGistMenu(GitHub github) {\n\t\tnew Thread(() -> {\n\t\t\tGHMyself myself;\n\t\t\ttry {\n\t\t\t\tmyself = github.getMyself();\n\t\t\t\tSystem.err.println(\"Loading all my Gists\");\n\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\tmyGists.getItems().clear();\n\t\t\t\t});\n\t\t\t\tList<GHGist> gists = myself.listGists().asList();\n\t\t\t\tfor (GHGist gist : gists) {\n\n\t\t\t\t\tString url = gist.getGitPushUrl();\n\n\t\t\t\t\tString desc = gist.getDescription();\n\t\t\t\t\tif (desc == null || desc.length() == 0 || desc.contentEquals(\"Adding new file from BowlerStudio\")) {\n\t\t\t\t\t\tdesc = gist.getFiles().keySet().toArray()[0].toString();\n\t\t\t\t\t}\n\t\t\t\t\tString descriptionString = desc;\n\t\t\t\t\tgetSelfRef().messages.put(url, \"GIST: \" + descriptionString);\n\t\t\t\t\t// Menu tmpGist = new Menu(desc);\n\t\t\t\t\t// setUpRepoMenue(ownerMenue.get(g.getOwnerName()), g);\n\t\t\t\t\tsetUpRepoMenue(myGists, url, true, true);\n\t\t\t\t}\n\t\t\t} catch (IOException e) {\n\t\t\t\tnew IssueReportingExceptionHandler().uncaughtException(Thread.currentThread(), e);\n\n\t\t\t}\n\t\t}).start();\n\t}\n\n\tpublic static String gitURLtoMessage(String url) {\n\t\tfor (int i = 0; i < 5; i++) {\n\t\t\ttry {\n\t\t\t\tif (getSelfRef().messages.get(url) != null)\n\t\t\t\t\tbreak;\n\t\t\t\tthrow new RuntimeException();\n\t\t\t} catch (Exception e) {\n\t\t\t\ttry {\n\t\t\t\t\tThread.sleep(500);\n\t\t\t\t} catch (InterruptedException e1) {\n\t\t\t\t\te1.printStackTrace();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tString string = getSelfRef().messages.get(url);\n\t\tif (string == null)\n\t\t\tstring = url;\n\t\treturn string;\n\t}\n\n\tpublic static void setUpRepoMenue(Menu repoMenue, String url, boolean useAddToWorkspaceItem, boolean threaded) {\n\t\tif (url.endsWith(\".git\"))\n\t\t\tsetUpRepoMenue(repoMenue, url, useAddToWorkspaceItem, threaded, gitURLtoMessage(url));\n\t}\n\n\tprivate static void resetRepoMenue(Menu repoMenue, GHRepository repo) {\n\t\tString url = repo.getGitTransportUrl().replace(\"git://\", \"https://\");\n\t\tgetSelfRef().messages.put(url, repo.getFullName());\n\t\tsetUpRepoMenue(repoMenue, url, true, true);\n\t}\n\n\tpublic static void setUpRepoMenue(Menu repoMenue, String url, boolean useAddToWorkspaceItem, boolean threaded,\n\t\t\tString message) {\n\n\t\tThread t = new Thread() {\n\t\t\tpublic void run() {\n\n\t\t\t\t// String menueMessage = repo.getFullName();\n\t\t\t\tMenu orgRepo = new Menu(message);\n\t\t\t\tMenu orgFiles = new Menu(\"Files\");\n\t\t\t\tMenu orgCommits = new Menu(\"Commits\");\n\t\t\t\tMenu orgBranches = new Menu(\"Branches\");\n\n\t\t\t\tMenuItem updateRepo = new MenuItem(\"Update Repo...\");\n\t\t\t\tMenuItem addToWs = new MenuItem(\"Add Repo to Workspace\");\n\t\t\t\taddToWs.setOnAction(event -> {\n\t\t\t\t\tnew Thread() {\n\t\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\t\tBowlerStudioMenuWorkspace.add(url);\n\t\t\t\t\t\t}\n\t\t\t\t\t}.start();\n\t\t\t\t});\n\t\t\t\t// String url = repo.getGitTransportUrl().replace(\"git://\", \"https://\");\n\n\t\t\t\tMenuResettingEventHandler loadCommitsEvent = createLoadCommitsEvent(url, orgCommits);\n\t\t\t\tMenuResettingEventHandler loadBranchesEvent = createLoadBranchesEvent(url, orgBranches);\n\t\t\t\tMenuResettingEventHandler loadFilesEvent = createLoadFileEvent(url, orgFiles);\n\t\t\t\tRunnable myEvent = new Runnable() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t// System.err.println(\"\\n\\nCommit event Detected \" + url + \" on branch \"\n\t\t\t\t\t\t\t// + ScriptingEngine.getBranch(url));\n\t\t\t\t\t\t\t// new RuntimeException().printStackTrace();\n\t\t\t\t\t\t\tBowlerStudio.runLater(() -> resetMenueForLoadingFiles(\"Files:\", orgFiles, loadFilesEvent));\n\t\t\t\t\t\t\tBowlerStudio.runLater(\n\t\t\t\t\t\t\t\t\t() -> resetMenueForLoadingFiles(\"Commits:\", orgCommits, loadCommitsEvent));\n\t\t\t\t\t\t\tBowlerStudio.runLater(\n\t\t\t\t\t\t\t\t\t() -> resetMenueForLoadingFiles(\"Branches:\", orgBranches, loadBranchesEvent));\n\n\t\t\t\t\t\t} catch (Throwable e) {\n\t\t\t\t\t\t\texp.uncaughtException(Thread.currentThread(), e);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\tloadCommitsEvent.setMenuReset(myEvent);\n\t\t\t\tloadBranchesEvent.setMenuReset(myEvent);\n\t\t\t\tloadFilesEvent.setMenuReset(myEvent);\n\n\t\t\t\tupdateRepo.setOnAction(event -> {\n\t\t\t\t\tnew Thread() {\n\t\t\t\t\t\t@SuppressWarnings(\"restriction\")\n\t\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tScriptingEngine.pull(url, ScriptingEngine.getBranch(url));\n\t\t\t\t\t\t\t} catch (WrongRepositoryStateException ex) {\n\t\t\t\t\t\t\t\t// ignore unsaved files\n\t\t\t\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\t\t\t\t@SuppressWarnings(\"restriction\")\n\t\t\t\t\t\t\t\t\tAlert alert = new Alert(AlertType.CONFIRMATION);\n\t\t\t\t\t\t\t\t\talert.setTitle(\"You have Un-Saved work, commit first\");\n\t\t\t\t\t\t\t\t\talert.setHeaderText(\"You have Un-Saved work, commit first\");\n\t\t\t\t\t\t\t\t\talert.setContentText(\"You have Un-Saved work, commit first\");\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\t\tBowlerStudioMenu.checkandDelete(url);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tmyEvent.run();\n\t\t\t\t\t\t\t// selfRef.onRefresh(null);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}.start();\n\t\t\t\t});\n\n\t\t\t\tMenuItem makeRelease = new MenuItem(\"Make Release...\");\n\t\t\t\tmakeRelease.setOnAction(event -> {\n\t\t\t\t\tSystem.err.println(\"Releasing \" + url);\n\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\tStage s = new Stage();\n\n\t\t\t\t\t\tMakeReleaseController controller = new MakeReleaseController(url);\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tcontroller.start(s);\n\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\te.printStackTrace();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tmyEvent.run();\n\t\t\t\t\t\t// selfRef.onRefresh(null);\n\t\t\t\t\t});\n\t\t\t\t});\n\n\t\t\t\tMenuItem addFile = new MenuItem(\"Add file to Git Repo...\");\n\t\t\t\taddFile.setOnAction(event -> {\n\t\t\t\t\tSystem.err.println(\"Adding file to : \" + url);\n\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\tStage s = new Stage();\n\n\t\t\t\t\t\tAddFileToGistController controller = new AddFileToGistController(url, getSelfRef());\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tcontroller.start(s);\n\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\te.printStackTrace();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tmyEvent.run();\n\t\t\t\t\t\t// selfRef.onRefresh(null);\n\t\t\t\t\t});\n\t\t\t\t});\n\n\t\t\t\tMenuItem delete = new MenuItem(\"Delete Local Copy...\");\n\t\t\t\tdelete.setOnAction(event -> {\n\t\t\t\t\tcheckandDelete(url);\n\t\t\t\t});\n\n\t\t\t\tScriptingEngine.addOnCommitEventListeners(url, myEvent);\n\t\t\t\torgRepo.setOnShowing(event -> {\n\t\t\t\t\t// On showing the menu, set up the rest of the handlers\n\t\t\t\t\tnew Thread(myEvent).start();\n\t\t\t\t});\n\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\tif (useAddToWorkspaceItem)\n\t\t\t\t\t\torgRepo.getItems().add(addToWs);\n\t\t\t\t\torgRepo.getItems().addAll(updateRepo, addFile, makeRelease, orgFiles, orgCommits, orgBranches,\n\t\t\t\t\t\t\tdelete);\n\t\t\t\t\t// BowlerStudio.runLater(() -> {\n\t\t\t\t\trepoMenue.getItems().add(orgRepo);\n\t\t\t\t\t// });\n\t\t\t\t});\n\n\t\t\t}\n\n\t\t};\n\t\tif (threaded)\n\t\t\tt.start();\n\t\telse\n\t\t\tt.run();\n\t}\n\n\tpublic static void checkandDelete(String url) {\n\t\tBowlerStudio.runLater(() -> {\n\n\t\t\tAlert alert = new Alert(AlertType.CONFIRMATION);\n\t\t\talert.setTitle(\"Are you sure you have published all your work?\");\n\t\t\talert.setHeaderText(\"This will wipe out the local cache for \" + url);\n\t\t\talert.setContentText(\"All files that are not published will be deleted\");\n\t\t\tNode root = alert.getDialogPane();\n\t\t\tStage stage = (Stage) alert.getDialogPane().getScene().getWindow();\n\t\t\tstage.setOnCloseRequest(ev -> alert.hide());\n\t\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\t\tint tmp = fontNum - 10;\n\t\t\t\tif (tmp < 12)\n\t\t\t\t\ttmp = 12;\n\t\t\t\troot.setStyle(\"-fx-font-size: \" + tmp + \"pt\");\n\t\t\t\talert.getDialogPane().applyCss();\n\t\t\t\talert.getDialogPane().layout();\n\t\t\t\tstage.sizeToScene();\n\t\t\t});\n\t\t\tOptional<ButtonType> result = alert.showAndWait();\n\t\t\tif (result.get() == ButtonType.OK) {\n\t\t\t\tnew Thread(() -> {\n\t\t\t\t\tScriptingEngine.deleteRepo(url);\n\t\t\t\t\tBowlerStudioMenuWorkspace.remove(url);\n\t\t\t\t}).start();\n\t\t\t} else {\n\t\t\t\tSystem.err.println(\"Nothing was deleted\");\n\t\t\t}\n\n\t\t});\n\t}\n\n\tprivate static MenuResettingEventHandler createLoadCommitsEvent(String url, Menu orgCommits) {\n\t\treturn new MenuResettingEventHandler() {\n\t\t\tpublic boolean gistFlag = false;\n\n\t\t\t@Override\n\t\t\tpublic void handle(Event event) {\n\t\t\t\tif (gistFlag) {\n\t\t\t\t\tSystem.err.println(\"Another thread is managing this event \" + url);\n\t\t\t\t\treturn;// another thread is\n\t\t\t\t\t\t\t// servicing this gist\n\t\t\t\t}\n\t\t\t\tgistFlag = true;\n\t\t\t\tString branchName;\n\t\t\t\ttry {\n\t\t\t\t\tbranchName = ScriptingEngine.getFullBranch(url);\n\t\t\t\t} catch (Exception e1) {\n\t\t\t\t\texp.uncaughtException(Thread.currentThread(), e1);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tSystem.err.println(\"Load Commits event \" + url + \" on branch \" + branchName);\n\t\t\t\tnew Thread(() -> {\n\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\t// removing this listener\n\t\t\t\t\t\t// after menue is activated\n\t\t\t\t\t\t// for the first time\n\t\t\t\t\t\torgCommits.setOnShowing(null);\n\t\t\t\t\t\tgistFlag = false;\n\t\t\t\t\t});\n\t\t\t\t\ttry {\n\t\t\t\t\t\tScriptingEngine.checkout(url, branchName);\n\n\t\t\t\t\t\tScriptingEngine.openGit(url, git -> {\n\t\t\t\t\t\t\tRepository repo = git.getRepository();\n\t\t\t\t\t\t\t// System.err.println(\"Commits of branch: \" + branchName);\n\t\t\t\t\t\t\t// System.err.println(\"-------------------------------------\");\n\n\t\t\t\t\t\t\tObjectId resolve = repo.resolve(branchName);\n\t\t\t\t\t\t\tif (resolve != null) {\n\t\t\t\t\t\t\t\tIterable<RevCommit> commits = git.log().add(resolve).call();\n\n\t\t\t\t\t\t\t\tList<RevCommit> commitsList = Lists.newArrayList(commits.iterator());\n\t\t\t\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\torgCommits.getItems()\n\t\t\t\t\t\t\t\t\t\t\t\t.add(new MenuItem(\"On Branch \" + ScriptingEngine.getBranch(url)));\n\t\t\t\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\t\t\t\texp.uncaughtException(Thread.currentThread(), e);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\torgCommits.getItems().add(new SeparatorMenuItem());\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t// RevCommit previous = null;\n\t\t\t\t\t\t\t\tfor (RevCommit commit : commitsList) {\n\t\t\t\t\t\t\t\t\tString date = format.format(new Date(commit.getCommitTime() * 1000L));\n\t\t\t\t\t\t\t\t\tString fullData = commit.getName() + \"\\r\\n\" + commit.getAuthorIdent().getName()\n\t\t\t\t\t\t\t\t\t\t\t+ \"\\r\\n\" + date + \"\\r\\n\" + commit.getFullMessage() + \"\\r\\n\"\n\t\t\t\t\t\t\t\t\t\t\t+ \"---------------------------------------------------\\r\\n\";// +\n\t\t\t\t\t\t\t\t\t// previous==null?\"\":getDiffOfCommit(previous,commit, repo, git);\n\n\t\t\t\t\t\t\t\t\t// previous = commit;\n\t\t\t\t\t\t\t\t\tString string = date + \" \" + commit.getAuthorIdent().getName() + \" \"\n\t\t\t\t\t\t\t\t\t\t\t+ commit.getShortMessage();\n\t\t\t\t\t\t\t\t\tif (string.length() > 80)\n\t\t\t\t\t\t\t\t\t\tstring = string.substring(0, 80);\n\t\t\t\t\t\t\t\t\t// MenuItem tmp = new MenuItem(string);\n\t\t\t\t\t\t\t\t\tCustomMenuItem tmp = new CustomMenuItem(new Label(string));\n\t\t\t\t\t\t\t\t\tTooltip tooltip = new Tooltip(fullData);\n\t\t\t\t\t\t\t\t\tTooltip.install(tmp.getContent(), tooltip);\n\t\t\t\t\t\t\t\t\ttmp.setOnAction(ev -> {\n\t\t\t\t\t\t\t\t\t\tnew Thread() {\n\t\t\t\t\t\t\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\t\t\t\t\t\t\tcom.neuronrobotics.sdk.common.Log\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t.error(\"Selecting \\r\\n\\r\\n\" + fullData);\n\n\t\t\t\t\t\t\t\t\t\t\t\tString branch;\n\t\t\t\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\t\t\t\tbranch = ScriptingEngine.getBranch(url);\n\t\t\t\t\t\t\t\t\t\t\t\t} catch (Exception e1) {\n\t\t\t\t\t\t\t\t\t\t\t\t\tbranch = \"newBranch\";\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t\t\tString dateString = formatSimple\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t.format(new Date(commit.getCommitTime() * 1000L));\n\t\t\t\t\t\t\t\t\t\t\t\tpromptForNewBranch(branch + \"-\" + dateString,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"Creating Branch From Commit:\\n\\n\" + fullData, newBranch -> {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnew Thread() {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tString slugify = slugify(newBranch);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcom.neuronrobotics.sdk.common.Log\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t.error(\"Creating \" + slugify);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tScriptingEngine.setCommitContentsAsCurrent(url,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tslugify, commit);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t} catch (IOException e) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\texp.uncaughtException(Thread.currentThread(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\te);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t} catch (GitAPIException e) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\texp.uncaughtException(Thread.currentThread(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\te);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}.start();\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t}.start();\n\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\t\t\t\t\torgCommits.getItems().add(tmp);\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\t\torgCommits.hide();\n\t\t\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\t\t\torgCommits.show();\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t});\n\t\t\t\t\t} catch (IOException e) {\n\t\t\t\t\t\texp.uncaughtException(Thread.currentThread(), e);\n\t\t\t\t\t} catch (RevisionSyntaxException e) {\n\t\t\t\t\t\texp.uncaughtException(Thread.currentThread(), e);\n\t\t\t\t\t} catch (NoHeadException e) {\n\t\t\t\t\t\texp.uncaughtException(Thread.currentThread(), e);\n\t\t\t\t\t} catch (GitAPIException e) {\n\t\t\t\t\t\texp.uncaughtException(Thread.currentThread(), e);\n\t\t\t\t\t} catch (Throwable e) {\n\t\t\t\t\t\texp.uncaughtException(Thread.currentThread(), e);\n\t\t\t\t\t}\n\t\t\t\t}).start();\n\t\t\t}\n\t\t};\n\t}\n\n\tpublic static String slugify(String input) {\n\t\tString nowhitespace = WHITESPACE.matcher(input).replaceAll(\"-\");\n\t\tString normalized = Normalizer.normalize(nowhitespace, Form.NFD);\n\t\tString slug = NONLATIN.matcher(normalized).replaceAll(\"\").replace('-', '_');\n\n\t\treturn slug;\n\t}\n\n\tprivate static void promptForNewBranch(String exampleName, String reasonForCreating, Consumer<String> resultEvent) {\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tTextInputDialog alert = new TextInputDialog(exampleName);\n\t\t\talert.setTitle(\"Create New Branch\");\n\t\t\talert.setHeaderText(reasonForCreating);\n\t\t\talert.setContentText(\"Enter a new branch name: \");\n\t\t\tNode root = alert.getDialogPane();\n\t\t\tStage stage = (Stage) alert.getDialogPane().getScene().getWindow();\n\t\t\tstage.setOnCloseRequest(ev -> alert.hide());\n\t\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\t\tint tmp = fontNum - 10;\n\t\t\t\tif (tmp < 12)\n\t\t\t\t\ttmp = 12;\n\t\t\t\troot.setStyle(\"-fx-font-size: \" + tmp + \"pt\");\n\t\t\t\talert.getDialogPane().applyCss();\n\t\t\t\talert.getDialogPane().layout();\n\t\t\t\tstage.sizeToScene();\n\t\t\t});\n\t\t\t// Traditional way to get the response value.\n\t\t\tOptional<String> result = alert.showAndWait();\n\t\t\t// The Java 8 way to get the response value (with lambda expression).\n\t\t\tresult.ifPresent(resultEvent);\n\t\t});\n\t}\n\n\tprivate static MenuResettingEventHandler createLoadBranchesEvent(String url, Menu orgBranches) {\n\t\treturn new MenuResettingEventHandler() {\n\t\t\tpublic boolean gistFlag = false;\n\t\t\t// EventHandler<Event> thisEvent = this;\n\n\t\t\t@Override\n\t\t\tpublic void handle(Event event) {\n\t\t\t\tif (gistFlag) {\n\t\t\t\t\tSystem.err.println(\"Another thread is managing this event \" + url);\n\t\t\t\t\treturn;// another thread is\n\t\t\t\t\t\t\t// servicing this gist\n\t\t\t\t}\n\t\t\t\tgistFlag = true;\n\t\t\t\tSystem.err.println(\"Load Branches event \" + url);\n\t\t\t\tfinal MenuItem onBranch;\n\t\t\t\ttry {\n\t\t\t\t\tonBranch = new MenuItem(\"On Branch \" + ScriptingEngine.getBranch(url));\n\t\t\t\t} catch (Exception e1) {\n\t\t\t\t\texp.uncaughtException(Thread.currentThread(), e1);\n\t\t\t\t\treturn;\n\t\t\t\t} ;\n\t\t\t\tnew Thread(() -> {\n\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\t// removing this listener\n\t\t\t\t\t\t// after menue is activated\n\t\t\t\t\t\t// for the first time\n\t\t\t\t\t\torgBranches.setOnShowing(null);\n\t\t\t\t\t\tgistFlag = false;\n\t\t\t\t\t});\n\t\t\t\t\tMenuItem newBranchItem = new MenuItem(\"New Branch...\");\n\t\t\t\t\tString newBranchName = \"\";\n\t\t\t\t\ttry {\n\t\t\t\t\t\tnewBranchName = ScriptingEngine.getBranch(url);\n\t\t\t\t\t} catch (Exception e1) {\n\t\t\t\t\t\texp.uncaughtException(Thread.currentThread(), e1);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tString newBranchName1 = newBranchName;\n\n\t\t\t\t\tString dateString = formatSimple.format(new Date());\n\n\t\t\t\t\tnewBranchItem.setOnAction(event1 -> {\n\t\t\t\t\t\tpromptForNewBranch(newBranchName1 + \"-\" + dateString,\n\t\t\t\t\t\t\t\t\"Create a new Branch from \" + newBranchName1, newBranch -> {\n\t\t\t\t\t\t\t\t\tnew Thread() {\n\t\t\t\t\t\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\t\t\tString slugify = slugify(newBranch);\n\t\t\t\t\t\t\t\t\t\t\t\tSystem.err.println(\"Creating Branch \" + slugify);\n\t\t\t\t\t\t\t\t\t\t\t\tScriptingEngine.newBranch(url, slugify);\n\t\t\t\t\t\t\t\t\t\t\t\tgetMenuReset().run();\n\t\t\t\t\t\t\t\t\t\t\t} catch (IOException e) {\n\t\t\t\t\t\t\t\t\t\t\t\texp.uncaughtException(Thread.currentThread(), e);\n\t\t\t\t\t\t\t\t\t\t\t} catch (GitAPIException e) {\n\t\t\t\t\t\t\t\t\t\t\t\texp.uncaughtException(Thread.currentThread(), e);\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}.start();\n\t\t\t\t\t\t\t\t});\n\n\t\t\t\t\t});\n\t\t\t\t\tBowlerStudio.runLater(() -> {\n\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tonBranch.setText(\"On Branch \" + ScriptingEngine.getBranch(url));\n\t\t\t\t\t\t\torgBranches.getItems().add(onBranch);\n\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\texp.uncaughtException(Thread.currentThread(), e);\n\n\t\t\t\t\t\t}\n\t\t\t\t\t\torgBranches.getItems().add(new SeparatorMenuItem());\n\t\t\t\t\t\torgBranches.getItems().add(newBranchItem);\n\t\t\t\t\t\torgBranches.getItems().add(new SeparatorMenuItem());\n\t\t\t\t\t});\n\t\t\t\t\tif (PasswordManager.hasNetwork())\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tCollection<Ref> branches = ScriptingEngine.getAllBranches(url);\n\t\t\t\t\t\t\tfor (Ref r : branches) {\n\t\t\t\t\t\t\t\tcreateRepoMenuItem(url, orgBranches, onBranch, r, getMenuReset());\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch (Throwable e) {\n\t\t\t\t\t\t\texp.uncaughtException(Thread.currentThread(), e);\n\t\t\t\t\t\t}\n\t\t\t\t\tSystem.err.println(\"Refreshing menu Branches\");\n\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\torgBranches.hide();\n\t\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\t\torgBranches.show();\n\t\t\t\t\t\t});\n\t\t\t\t\t});\n\t\t\t\t}).start();\n\t\t\t}\n\n\t\t};\n\t}\n\n\tprivate static void createRepoMenuItem(String url, Menu orgBranches, final MenuItem onBranch, Ref r,\n\t\t\tRunnable menureset) {\n\t\tString[] name2 = r.getName().split(\"/\");\n\t\tMenuItem tmp = new MenuItem(name2[name2.length - 1]);\n\t\tRef select = r;\n\t\tString[] name = select.getName().split(\"/\");\n\t\tString myName = name[name.length - 1];\n\t\t// System.err.println(\"Selecting Branch\\r\\n\"+url+\"\n\t\t// \\t\\t\"+myName);\n\t\ttmp.setOnAction(ev -> {\n\t\t\tnew Thread() {\n\t\t\t\tpublic void run() {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tswitchToThisNewBranch(url, onBranch, select, myName);\n\t\t\t\t\t\tmenureset.run();\n\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\texp.uncaughtException(Thread.currentThread(), e);\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tprivate void switchToThisNewBranch(String url, final MenuItem onBranch, Ref select, String myName)\n\t\t\t\t\t\tthrows Exception {\n\t\t\t\t\tString was = ScriptingEngine.getBranch(url);\n\t\t\t\t\ttry {\n\t\t\t\t\t\tScriptingEngine.checkout(url, select);\n\t\t\t\t\t} catch (org.eclipse.jgit.api.errors.CheckoutConflictException ex) {\n\t\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\t\tAlert alert = new Alert(AlertType.ERROR);// line 1\n\t\t\t\t\t\t\talert.setTitle(\"CheckoutConflictException\");// line 2\n\t\t\t\t\t\t\talert.setHeaderText(\"This repo is in an a dirty state\");// line 3\n\t\t\t\t\t\t\talert.setContentText(\n\t\t\t\t\t\t\t\t\t\"Please commit your changes before switching.\\nAlternatly you can revert your changes.\\nRepository must not have uncommitted changes before changing branches.\");// line\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// 4\n\t\t\t\t\t\t\tNode root = alert.getDialogPane();\n\t\t\t\t\t\t\tStage stage = (Stage) alert.getDialogPane().getScene().getWindow();\n\t\t\t\t\t\t\tstage.setOnCloseRequest(ev -> alert.hide());\n\t\t\t\t\t\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\t\t\t\t\t\tint tmp = fontNum - 10;\n\t\t\t\t\t\t\t\tif (tmp < 12)\n\t\t\t\t\t\t\t\t\ttmp = 12;\n\t\t\t\t\t\t\t\troot.setStyle(\"-fx-font-size: \" + tmp + \"pt\");\n\t\t\t\t\t\t\t\talert.getDialogPane().applyCss();\n\t\t\t\t\t\t\t\talert.getDialogPane().layout();\n\t\t\t\t\t\t\t\tstage.sizeToScene();\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\talert.showAndWait(); // line 5\n\t\t\t\t\t\t});\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tString s = ScriptingEngine.getBranch(url);\n\t\t\t\t\tif (myName.contentEquals(s))\n\t\t\t\t\t\tcom.neuronrobotics.sdk.common.Log\n\t\t\t\t\t\t\t\t.error(\"Changing from \" + was + \" to \" + myName + \" is now \" + s + \"... Success!\");\n\t\t\t\t\tonBranch.setText(\"On Branch \" + s);\n\t\t\t\t}\n\t\t\t}.start();\n\n\t\t});\n\t\tBowlerStudio.runLater(() -> {\n\t\t\torgBranches.getItems().add(tmp);\n\t\t});\n\t}\n\n\t@FXML\n\tpublic void onLoadFile(ActionEvent e) {\n\t\tnew Thread() {\n\n\t\t\tpublic void run() {\n\t\t\t\tsetName(\"Load File Thread\");\n\t\t\t\tif (openFile == null)\n\t\t\t\t\topenFile = ScriptingEngine.getLastFile();\n\t\t\t\topenFile = FileSelectionFactory.GetFile(openFile, new ExtensionFilter(\"All\", \"*.*\"),\n\t\t\t\t\t\tnew ExtensionFilter(\"Groovy Scripts\", \"*.groovy\", \"*.java\", \"*.txt\"),\n\t\t\t\t\t\tnew ExtensionFilter(\"Clojure\", \"*.cloj\", \"*.clj\", \"*.txt\", \"*.clojure\"),\n\t\t\t\t\t\tnew ExtensionFilter(\"Python\", \"*.py\", \"*.python\", \"*.txt\"),\n\t\t\t\t\t\tnew ExtensionFilter(\"DXF\", \"*.dxf\", \"*.DXF\"),\n\t\t\t\t\t\tnew ExtensionFilter(\"GCODE\", \"*.gcode\", \"*.nc\", \"*.ncg\", \"*.txt\"),\n\t\t\t\t\t\tnew ExtensionFilter(\"Image\", \"*.jpg\", \"*.jpeg\", \"*.JPG\", \"*.png\", \"*.PNG\"),\n\t\t\t\t\t\tnew ExtensionFilter(\"STL\", \"*.stl\", \"*.STL\", \"*.Stl\"));\n\t\t\t\tif (openFile == null) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tbowlerStudioModularFrame.createFileTab(openFile);\n\t\t\t}\n\t\t}.start();\n\t}\n\n\tprivate static void resetMenueForLoadingFiles(String string, Menu orgFiles, EventHandler<Event> loadFiles) {\n\t\tBowlerStudio.runLater(() -> {\n\t\t\ttry {\n\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\torgFiles.getItems().clear();\n\t\t\t\t\t// orgFiles.hide();\n\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\torgFiles.getItems().add(new MenuItem(string));\n\t\t\t\t\t\torgFiles.getItems().add(new SeparatorMenuItem());\n\t\t\t\t\t\torgFiles.setOnShowing(loadFiles);\n\t\t\t\t\t\t// BowlerStudio.runLater(() ->orgFiles.show());\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t} catch (Throwable t) {\n\t\t\t\tt.printStackTrace();\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate static MenuResettingEventHandler createLoadFileEvent(String url, Menu orgFiles) {\n\t\treturn new MenuResettingEventHandler() {\n\t\t\tpublic boolean gistFlag = false;\n\n\t\t\t@Override\n\t\t\tpublic void handle(Event ev) {\n\t\t\t\tif (gistFlag) {\n\t\t\t\t\tSystem.err.println(\"Another thread is managing this event\");\n\t\t\t\t\treturn;// another thread is\n\t\t\t\t\t\t\t// servicing this gist\n\t\t\t\t}\n\t\t\t\tgistFlag = true;\n\t\t\t\tSystem.err.println(\"Load file event \" + url);\n\t\t\t\tnew Thread() {\n\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\tsetName(\"Load file Thread \" + url);\n\n\t\t\t\t\t\tSystem.err.println(\"Loading files for \" + url + \" \");\n\t\t\t\t\t\tArrayList<String> listofFiles;\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tlistofFiles = ScriptingEngine.filesInGit(url, ScriptingEngine.getFullBranch(url), null);\n\t\t\t\t\t\t\tcom.neuronrobotics.sdk.common.Log\n\t\t\t\t\t\t\t\t\t.error(\"Clone Done for \" + url + listofFiles.size() + \" files\");\n\t\t\t\t\t\t} catch (Exception e1) {\n\t\t\t\t\t\t\te1.printStackTrace();\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// if (orgFiles.getItems().size() != 1) {\n\t\t\t\t\t\t// Log.warning(\"Bailing out of loading thread\");\n\t\t\t\t\t\t// return;// menue populated by\n\t\t\t\t\t\t// // another thread\n\t\t\t\t\t\t// }\n\t\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\t\t// removing this listener\n\t\t\t\t\t\t\t// after menue is activated\n\t\t\t\t\t\t\t// for the first time\n\t\t\t\t\t\t\torgFiles.setOnShowing(null);\n\t\t\t\t\t\t\tgistFlag = false;\n\t\t\t\t\t\t});\n\t\t\t\t\t\tfor (String s : listofFiles) {\n\t\t\t\t\t\t\tSystem.err.println(\"Adding file: \" + s);\n\t\t\t\t\t\t\tString string = s;\n\t\t\t\t\t\t\tif (s.length() > 80)\n\t\t\t\t\t\t\t\ts = s.substring(0, 10) + \"...\" + s.substring(s.length() - 70, s.length() - 1);\n\t\t\t\t\t\t\tMenuItem tmp = new MenuItem(s);\n\t\t\t\t\t\t\ttmp.setOnAction(event -> {\n\t\t\t\t\t\t\t\tnew Thread() {\n\t\t\t\t\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\t\tFile fileSelected = ScriptingEngine.fileFromGit(url, string);\n\t\t\t\t\t\t\t\t\t\t\tBowlerStudio.createFileTab(fileSelected);\n\t\t\t\t\t\t\t\t\t\t\tBowlerStudioMenuWorkspace.add(url);\n\t\t\t\t\t\t\t\t\t\t\tgetMenuReset().run();\n\t\t\t\t\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\t\t\t\t\texp.uncaughtException(Thread.currentThread(), e);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}.start();\n\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\t\t\torgFiles.getItems().add(tmp);\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t}\n\t\t\t\t\t\tSystem.err.println(\"Refreshing menu\");\n\t\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\t\torgFiles.hide();\n\t\t\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\t\t\torgFiles.show();\n\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}.start();\n\t\t\t}\n\t\t};\n\t}\n\n\t@FXML\n\tpublic void onConnect(ActionEvent e) {\n\n\t\tConnectionManager.addConnection();\n\n\t}\n\n\t@FXML\n\tpublic void onConnectVirtual(ActionEvent e) {\n\n\t\tConnectionManager.addConnection(new VirtualGenericPIDDevice(10000, \"virtual\"), \"virtual\");\n\t}\n\n\t@FXML\n\tpublic void onClose(ActionEvent e) {\n\t\tBowlerStudio.closeBowlerStudio();\n\t}\n\n\t@FXML\n\tpublic void onConnectCHDKCamera(ActionEvent event) {\n\t\t// BowlerStudio.runLater(() -> {\n\t\t// try {\n\t\t// ConnectionManager.addConnection(new CHDKImageProvider(), \"cameraCHDK\");\n\t\t// } catch (Exception e) {\n\t\t// exp.uncaughtException(Thread.currentThread(), e);\n\t\t// }\n\t\t// });\n\t}\n\n\t@FXML\n\tpublic void onConnectCVCamera(ActionEvent event) {\n\n\t\t// BowlerStudio.runLater(() -> ConnectionManager.onConnectCVCamera());\n\n\t}\n\n\t@FXML\n\tpublic void onConnectFileSourceCamera(ActionEvent event) {\n\t\tBowlerStudio.runLater(() -> ConnectionManager.onConnectFileSourceCamera());\n\n\t}\n\n\t@FXML\n\tpublic void onConnectURLSourceCamera(ActionEvent event) {\n\n\t\tBowlerStudio.runLater(() -> ConnectionManager.onConnectURLSourceCamera());\n\n\t}\n\n\t@FXML\n\tpublic void onConnectHokuyoURG(ActionEvent event) {\n\t\tBowlerStudio.runLater(() -> ConnectionManager.onConnectHokuyoURG());\n\n\t}\n\n\t@FXML\n\tpublic void onConnectGamePad(ActionEvent event) {\n\t\tBowlerStudio.runLater(() -> ConnectionManager.onConnectGamePad());\n\n\t}\n\n\t@FXML\n\tpublic void onLogin(ActionEvent event) {\n\t\t// new Exception().printStackTrace();\n\t\tnew Thread() {\n\t\t\tpublic void run() {\n\t\t\t\tPasswordManager.checkInternet();\n\t\t\t\tsetName(\"Login Gist Thread\");\n\n\t\t\t\ttry {\n\t\t\t\t\tScriptingEngine.logout();\n\t\t\t\t\twhile (!ScriptingEngine.isLoginSuccess() && !PasswordManager.isAnonMode()) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tThread.sleep(200);\n\t\t\t\t\t\t} catch (InterruptedException e) {\n\t\t\t\t\t\t\texp.uncaughtException(Thread.currentThread(), e);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tScriptingEngine.login();\n\t\t\t\t\t}\n\t\t\t\t} catch (IOException e) {\n\t\t\t\t\texp.uncaughtException(Thread.currentThread(), e);\n\t\t\t\t}\n\t\t\t}\n\t\t}.start();\n\n\t}\n\n\t@FXML\n\tpublic void onLogout(ActionEvent event) {\n\t\tnew Thread(() -> {\n\t\t\ttry {\n\t\t\t\tScriptingEngine.logout();\n\t\t\t} catch (IOException e) {\n\t\t\t\texp.uncaughtException(Thread.currentThread(), e);\n\t\t\t}\n\t\t}).start();\n\t}\n\n\t@FXML\n\tpublic void onConnectPidSim(ActionEvent event) {\n\t\tLinearPhysicsEngine eng = new LinearPhysicsEngine();\n\t\teng.connect();\n\t\tConnectionManager.addConnection(eng, \"engine\");\n\t}\n\n\t@FXML\n\tpublic void onPrint(ActionEvent event) {\n\n\t}\n\n\t@FXML\n\tpublic void onMobileBaseFromFile(ActionEvent event) {\n\t\tnew Thread() {\n\t\t\tpublic void run() {\n\t\t\t\tsetName(\"Load Mobile Base Thread\");\n\t\t\t\tFile openFile = FileSelectionFactory.GetFile(ScriptingEngine.getLastFile(),\n\t\t\t\t\t\tnew ExtensionFilter(\"MobileBase XML\", \"*.xml\", \"*.XML\"));\n\n\t\t\t\tif (openFile == null) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tMobileBase mb = new MobileBase(new FileInputStream(openFile));\n\t\t\t\t\t\tConnectionManager.addConnection(mb, mb.getScriptingName());\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\texp.uncaughtException(Thread.currentThread(), e);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t}.start();\n\n\t}\n\n\t@FXML\n\tpublic void onCreatenewGist(ActionEvent event) {\n\t\tStage s = new Stage();\n\t\tnew Thread(() -> {\n\t\t\tAddFileToGistController controller = new AddFileToGistController(null, getSelfRef());\n\n\t\t\ttry {\n\t\t\t\tcontroller.start(s);\n\t\t\t\tsetToLoggedIn(username);\n\t\t\t} catch (Exception e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t}).start();\n\t}\n\n\t@FXML\n\tpublic void onOpenGitter(ActionEvent event) {\n\t\tString url = \"https://gitter.im\";\n\t\ttry {\n\t\t\tBowlerStudio.openUrlInNewTab(new URI(url).toURL());\n\t\t} catch (MalformedURLException | URISyntaxException e) {\n\t\t\texp.uncaughtException(Thread.currentThread(), e);\n\t\t}\n\t}\n\n\t@FXML\n\tpublic void clearScriptCache(ActionEvent event) {\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tAlert alert = new Alert(AlertType.CONFIRMATION);\n\t\t\talert.setTitle(\"Are you sure you have published all your work?\");\n\t\t\talert.setHeaderText(\"This will wipe out the local cache\");\n\t\t\talert.setContentText(\"All files that are not published will be deleted\");\n\t\t\tNode root = alert.getDialogPane();\n\t\t\tStage stage = (Stage) alert.getDialogPane().getScene().getWindow();\n\t\t\tstage.setOnCloseRequest(ev -> alert.hide());\n\t\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\t\tint tmp = fontNum - 10;\n\t\t\t\tif (tmp < 12)\n\t\t\t\t\ttmp = 12;\n\t\t\t\troot.setStyle(\"-fx-font-size: \" + tmp + \"pt\");\n\t\t\t\talert.getDialogPane().applyCss();\n\t\t\t\talert.getDialogPane().layout();\n\t\t\t\tstage.sizeToScene();\n\t\t\t});\n\t\t\tOptional<ButtonType> result = alert.showAndWait();\n\t\t\tif (result.get() == ButtonType.OK) {\n\t\t\t\tnew Thread(() -> {\n\t\t\t\t\tBowlerStudio.setDeleteFlag(true);\n\t\t\t\t\tBowlerStudio.exit();\n\t\t\t\t}).start();\n\t\t\t} else {\n\t\t\t\tSystem.err.println(\"Nothing was deleted\");\n\t\t\t}\n\t\t});\n\n\t}\n\n\t@FXML\n\tpublic void changeAssetRepoButtonPressed(ActionEvent event) {\n\t\tStage s = new Stage();\n\t\tnew Thread(() -> {\n\t\t\tChangeAssetRepoController controller = new ChangeAssetRepoController();\n\n\t\t\ttry {\n\t\t\t\tcontroller.start(s);\n\t\t\t} catch (Exception e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t}).start();\n\t}\n\n\t@FXML\n\tpublic void onMobileBaseFromGit(ActionEvent event) {\n\t\tPromptForGit.prompt(\"Select a Creature From a Git\", \"https://github.com/madhephaestus/carl-the-hexapod.git\",\n\t\t\t\t(gitsId, file) -> {\n\t\t\t\t\tloadMobilebaseFromGit(gitsId, file);\n\t\t\t\t});\n\t}\n\n\t@FXML\n\tvoid onSaveConfiguration(ActionEvent event) {\n\t\tSystem.err.println(\"Saving database\");\n\t\tnew Thread() {\n\t\t\tpublic void run() {\n\n\t\t\t\tConfigurationDatabase.save();\n\t\t\t}\n\t\t}.start();\n\t}\n\n\tpublic void addVitaminType(String s) {\n\t\tgetTypeMenu(s);\n\t\tArrayList<String> sizes = Vitamins.listVitaminSizes(s);\n\t\tfor (String size : sizes) {\n\t\t\taddSizesToMenu(size, s);\n\t\t}\n\n\t}\n\n\tpublic Menu getTypeMenu(String type) {\n\t\tif (vitaminTypeMenus.get(type) == null) {\n\t\t\tMenu typeMenu = new Menu(type);\n\t\t\ttypeMenu.setMnemonicParsing(false);\n\t\t\tvitaminTypeMenus.put(type, typeMenu);\n\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\tvitaminsMenu.getItems().add(typeMenu);\n\t\t\t});\n\t\t\tsetUpSizes(typeMenu, type);\n\t\t}\n\t\treturn vitaminTypeMenus.get(type);\n\t}\n\n\tprivate void setUpSizes(Menu typeMenu, String type) {\n\n\t\tMenuItem editScript = new MenuItem(\"Edit \" + type + \" Cad Generator...\");\n\t\teditScript.setMnemonicParsing(false);\n\t\teditScript.setOnAction(event -> {\n\t\t\tnew Thread(() -> BowlerStudio.createFileTab(Vitamins.getScriptFile(type))).start();\n\t\t});\n\n\t\tBowlerStudio.runLater(() -> {\n\t\t\ttypeMenu.getItems().add(new MenuItem(\"Sizes:\"));\n\t\t\ttypeMenu.getItems().add(new SeparatorMenuItem());\n\t\t\ttypeMenu.getItems().add(editScript);\n\t\t\ttypeMenu.getItems().add(new SeparatorMenuItem());\n\t\t});\n\t}\n\n\tpublic void addSizesToMenu(String size, String type) {\n\t\tMenuItem sizeMenu = new MenuItem(size);\n\t\tsizeMenu.setMnemonicParsing(false);\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tgetTypeMenu(type).getItems().add(sizeMenu);\n\t\t});\n\t\tsizeMenu.setOnAction(new EventHandler<ActionEvent>() {\n\t\t\t@Override\n\t\t\tpublic void handle(ActionEvent event) {\n\t\t\t\tnew Thread() {\n\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\tLocalFileScriptTab tab = LocalFileScriptTab.getSelectedTab();\n\t\t\t\t\t\tif (tab != null)\n\t\t\t\t\t\t\ttab.insertString(\"CSG vitamin_\" + slugify(type) + \"_\" + slugify(size) + \" = Vitamins.get(\\\"\"\n\t\t\t\t\t\t\t\t\t+ type + \"\\\", \\\"\" + size + \"\\\")\\n\");\n\t\t\t\t\t}\n\t\t\t\t}.start();\n\t\t\t}\n\t\t});\n\t}\n\n\t@FXML\n\tvoid onRefresh(ActionEvent event) {\n\t\tString current = this.username;// =null;\n\t\tthis.username = null;\n\t\tif (PasswordManager.loggedIn())\n\t\t\tsetToLoggedIn(current);\n\n\t}\n\n\t@FXML\n\tvoid onCreateNewVitamin(ActionEvent event) {\n\t\ttry {\n\t\t\tNewVitaminWizardController.launchWizard(this);\n\t\t} catch (Exception e) {\n\t\t\tnew IssueReportingExceptionHandler().uncaughtException(Thread.currentThread(), e);\n\n\t\t}\n\t}\n\n\t@FXML\n\tvoid onLoadGit(ActionEvent event) {\n\t\ttry {\n\n\t\t\t// create a text input dialog\n\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\tTextInputDialog td = new TextInputDialog();\n\t\t\t\ttd.setHeaderText(\"Enter Git URL\");\n\t\t\t\ttd.setResizable(true);\n\t\t\t\tNode root = td.getDialogPane();\n\t\t\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\t\t\tint tmp = fontNum - 10;\n\t\t\t\t\tif (tmp < 12)\n\t\t\t\t\t\ttmp = 12;\n\t\t\t\t\troot.setStyle(\"-fx-font-size: \" + tmp + \"pt\");\n\t\t\t\t\ttd.getDialogPane().applyCss();\n\t\t\t\t\ttd.getDialogPane().layout();\n\t\t\t\t});\n\t\t\t\ttd.showAndWait();\n\n\t\t\t\t// set the text of the label\n\t\t\t\tString s = td.getEditor().getText();\n\n\t\t\t\tif (s == null || s.length() < 4) {\n\t\t\t\t\tSystem.err.println(\"Cancle detected\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (s.endsWith(\".git\")) {\n\t\t\t\t\tSystem.err.println(\"Loading file from git \" + s);\n\t\t\t\t\tnew Thread(() -> {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tArrayList<String> f = ScriptingEngine.filesInGit(s);\n\t\t\t\t\t\t\tif (f.size() > 0) {\n\t\t\t\t\t\t\t\tSystem.err.println(\"Valid URL Detected\");\n\t\t\t\t\t\t\t\tBowlerStudioMenuWorkspace.add(s);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\te.printStackTrace();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}).start();\n\t\t\t\t} else {\n\t\t\t\t\tSystem.err.println(\"Invalid entry \" + s);\n\t\t\t\t\tonLoadGit(event);\n\t\t\t\t}\n\n\t\t\t});\n\t\t} catch (Exception e) {\n\t\t\tnew IssueReportingExceptionHandler().uncaughtException(Thread.currentThread(), e);\n\n\t\t}\n\t}\n\n\t@FXML\n\tvoid onBowlerStudioHelp(ActionEvent event) {\n\t\tnew Thread(() -> {\n\t\t\tif (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) {\n\t\t\t\ttry {\n\t\t\t\t\tDesktop.getDesktop().browse(\n\t\t\t\t\t\t\tnew URI(\"https://hackaday.io/project/6423-bowlerstudio-a-robotics-development-platform\"));\n\t\t\t\t} catch (IOException e) {\n\t\t\t\t\tnew IssueReportingExceptionHandler().uncaughtException(Thread.currentThread(), e);\n\n\t\t\t\t} catch (URISyntaxException e) {\n\t\t\t\t\tnew IssueReportingExceptionHandler().uncaughtException(Thread.currentThread(), e);\n\n\t\t\t\t}\n\t\t\t}\n\t\t}).start();\n\t}\n\n\t@FXML // This method is called by the FXMLLoader when initialization is\n\t// complete\n\tvoid initialize() {\n\t\tassert CreaturesMenu != null\n\t\t\t\t: \"fx:id=\\\"CreaturesMenu\\\" was not injected: check your FXML file 'BowlerStudioMenuBar.fxml'.\";\n\t\tassert GitHubRoot != null\n\t\t\t\t: \"fx:id=\\\"GitHubRoot\\\" was not injected: check your FXML file 'BowlerStudioMenuBar.fxml'.\";\n\t\tassert getMeneBarBowlerStudio() != null\n\t\t\t\t: \"fx:id=\\\"MeneBarBowlerStudio\\\" was not injected: check your FXML file 'BowlerStudioMenuBar.fxml'.\";\n\t\tassert addMarlinGCODEDevice != null\n\t\t\t\t: \"fx:id=\\\"addMarlinGCODEDevice\\\" was not injected: check your FXML file 'BowlerStudioMenuBar.fxml'.\";\n\t\tassert clearCache != null\n\t\t\t\t: \"fx:id=\\\"clearCache\\\" was not injected: check your FXML file 'BowlerStudioMenuBar.fxml'.\";\n\t\tassert createNewGist != null\n\t\t\t\t: \"fx:id=\\\"createNewGist\\\" was not injected: check your FXML file 'BowlerStudioMenuBar.fxml'.\";\n\t\tassert logoutGithub != null\n\t\t\t\t: \"fx:id=\\\"logoutGithub\\\" was not injected: check your FXML file 'BowlerStudioMenuBar.fxml'.\";\n\t\tassert myGists != null : \"fx:id=\\\"myGists\\\" was not injected: check your FXML file 'BowlerStudioMenuBar.fxml'.\";\n\t\tassert myOrganizations != null\n\t\t\t\t: \"fx:id=\\\"myOrganizations\\\" was not injected: check your FXML file 'BowlerStudioMenuBar.fxml'.\";\n\t\tassert myRepos != null : \"fx:id=\\\"myRepos\\\" was not injected: check your FXML file 'BowlerStudioMenuBar.fxml'.\";\n\t\tassert watchingRepos != null\n\t\t\t\t: \"fx:id=\\\"watchingRepos\\\" was not injected: check your FXML file 'BowlerStudioMenuBar.fxml'.\";\n\t\tassert workspacemenuHandle != null\n\t\t\t\t: \"fx:id=\\\"workspacemenuHandle\\\" was not injected: check your FXML file 'BowlerStudioMenuBar.fxml'.\";\n\t\tassert vitaminsMenu != null\n\t\t\t\t: \"fx:id=\\\"vitaminsMenu\\\" was not injected: check your FXML file 'BowlerStudioMenuBar.fxml'.\";\n\t\tassert addNewVitamin != null\n\t\t\t\t: \"fx:id=\\\"addNewVitamin\\\" was not injected: check your FXML file 'BowlerStudioMenuBar.fxml'.\";\n\t\tGitHubRoot.setGraphic(AssetFactory.loadIcon(\"githubLogo\"));\n\n\t\tsetSelfRef(this);\n\t\tBowlerStudioMenuWorkspace.init(workspacemenuHandle);\n\n\t\tshowDevicesPanel.setOnAction(event -> {\n\t\t\tbowlerStudioModularFrame.showConectionManager();\n\t\t});\n\t\tshowCreatureLab.setOnAction(event -> {\n\t\t\tbowlerStudioModularFrame.showCreatureLab();;\n\t\t});\n\t\tshowTerminal.setOnAction(event -> {\n\t\t\tbowlerStudioModularFrame.showTerminal();\n\t\t});\n\t\tnew Thread() {\n\t\t\tpublic void run() {\n\t\t\t\ttry {\n\t\t\t\t\tFile f = ScriptingEngine.fileFromGit(\n\t\t\t\t\t\t\t\"https://github.com/CommonWealthRobotics/BowlerStudioExampleRobots.git\", // git\n\t\t\t\t\t\t\t// repo,\n\t\t\t\t\t\t\t// change\n\t\t\t\t\t\t\t// this\n\t\t\t\t\t\t\t// if\n\t\t\t\t\t\t\t// you\n\t\t\t\t\t\t\t// fork\n\t\t\t\t\t\t\t// this\n\t\t\t\t\t\t\t// demo\n\t\t\t\t\t\t\t\"exampleRobots.json\"// File from within the Git repo\n\t\t\t\t\t);\n\t\t\t\t\tMenuItem newCreatureWiz = new MenuItem(\"New Creature...\");\n\t\t\t\t\tnewCreatureWiz.setOnAction(event -> {\n\t\t\t\t\t\tNewCreatureWizard.run();\n\t\t\t\t\t});\n\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\tCreaturesMenu.getItems().add(new SeparatorMenuItem());\n\t\t\t\t\t\tCreaturesMenu.getItems().add(newCreatureWiz);\n\t\t\t\t\t\tCreaturesMenu.getItems().add(new SeparatorMenuItem());\n\t\t\t\t\t});\n\t\t\t\t\t@SuppressWarnings(\"unchecked\")\n\t\t\t\t\tHashMap<String, HashMap<String, Object>> map = (HashMap<String, HashMap<String, Object>>) ScriptingEngine\n\t\t\t\t\t\t\t.inlineFileScriptRun(CSGDatabase.getInstance(), f, null);\n\n\t\t\t\t\tList<String> entrySet = asSortedList(map.keySet());\n\n\t\t\t\t\tfor (String entry : entrySet) {\n\t\t\t\t\t\tHashMap<String, Object> script = map.get(entry);\n\t\t\t\t\t\tMenuItem item = new MenuItem(entry);\n\t\t\t\t\t\titem.setOnAction(event -> {\n\t\t\t\t\t\t\tString id = (String) script.get(\"scriptGit\");\n\t\t\t\t\t\t\tString file = (String) script.get(\"scriptFile\");\n\n\t\t\t\t\t\t\tloadMobilebaseFromGit(id, file);\n\t\t\t\t\t\t});\n\t\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\t\tCreaturesMenu.getItems().add(item);\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\texp.uncaughtException(Thread.currentThread(), e);\n\t\t\t\t}\n\t\t\t}\n\t\t}.start();\n\n\t\taddMarlinGCODEDevice.setOnAction(event -> {\n\t\t\tBowlerStudio.runLater(() -> ConnectionManager.onMarlinGCODE());\n\t\t});\n\t\tloadFirmata.setOnAction(event -> {\n\t\t\tBowlerStudio.runLater(() -> ConnectionManager.onFirmata());\n\t\t});\n\t\tScriptingEngine.addIGithubLoginListener(new IGithubLoginListener() {\n\t\t\t@Override\n\t\t\tpublic void onLogout(String arg0) {\n\t\t\t\tsetToLoggedOut();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onLogin(String arg0) {\n\t\t\t\tsetToLoggedIn(arg0);\n\t\t\t}\n\t\t});\n\n\t\tif (PasswordManager.getUsername() != null) {\n\t\t\tsetToLoggedIn(PasswordManager.getUsername());\n\t\t} else {\n\t\t\tsetToLoggedOut();\n\t\t}\n\n\t\t// WindowMenu\n\t\tint[] fonts = FontSizeManager.getFontOptions();\n\t\tMenu fontSelect = new Menu(\"Font Size\");\n\t\tToggleGroup toggleGroup = new ToggleGroup();\n\t\tint defSize = FontSizeManager.getDefaultSize();\n\t\tfor (int i = 0; i < fonts.length; i++) {\n\t\t\tint myFoneNum = fonts[i];\n\t\t\tRadioMenuItem ftmp = new RadioMenuItem(myFoneNum + \" pt\");\n\n\t\t\tif (defSize == myFoneNum) {\n\t\t\t\tftmp.setSelected(true);\n\t\t\t} else\n\t\t\t\tftmp.setSelected(false);\n\t\t\tEventHandler<ActionEvent> eventHandler = (event) -> {\n\t\t\t\tif (ftmp.isSelected()) {\n\t\t\t\t\tFontSizeManager.setFontSize(myFoneNum);\n\t\t\t\t}\n\t\t\t};\n\t\t\tftmp.setOnAction(eventHandler);\n\t\t\tftmp.setToggleGroup(toggleGroup);\n\t\t\tfontSelect.getItems().add(ftmp);\n\t\t\tFontSizeManager.addListener(nf -> {\n\t\t\t\tftmp.setOnAction(null);\n\t\t\t\tif (myFoneNum == nf) {\n\t\t\t\t\tftmp.setSelected(true);\n\t\t\t\t} else\n\t\t\t\t\tftmp.setSelected(false);\n\t\t\t\tftmp.setOnAction(eventHandler);\n\t\t\t});\n\t\t}\n\t\tWindowMenu.getItems().add(fontSelect);\n\t\tCheckMenuItem autohighlight = new CheckMenuItem(\"Auto Highlight 3d Items\");\n\t\tautohighlight.setSelected(true);\n\t\tCheckMenuItem idlespin = new CheckMenuItem(\"Idle Spin \");\n\t\tidlespin.setSelected(false);\n\t\tCheckMenuItem showRuler = new CheckMenuItem(\"Show Ruler \");\n\t\tshowRuler.setSelected(true);\n\t\tCheckMenuItem showCSGProgress = new CheckMenuItem(\"Show CSG Update\");\n\t\tCheckMenuItem useAdvancedSTL = new CheckMenuItem(\"Generate Advanced STL (Fully Manifold)\");\n\t\tCSG.setProgressMoniter((currentIndex, finalIndex, type, intermediateShape) -> {\n\t\t\ttry {\n\t\t\t\tint i = currentIndex + 1;\n\n\t\t\t\tdouble percent = ((double) i) / ((double) finalIndex) * 100;\n\t\t\t\tString name = intermediateShape == null ? \"\" : intermediateShape.getName();\n\t\t\t\tString x = name + \" \" + type.trim() + \" \" + String.format(\"%.1f\", percent) + \"% finished : \" + i\n\t\t\t\t\t\t+ \" of \" + finalIndex;\n\t\t\t\tif (showCSGProgress.isSelected()) {\n\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.debug(x);\n\t\t\t\t\tif (finalIndex > 50) {\n\t\t\t\t\t\tif (percent > 90) {\n\t\t\t\t\t\t\tSplashManager.closeSplash();\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tSplashManager.renderSplashFrame((int) percent, x);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tSplashManager.closeSplash();\n\t\t\t\t\t}\n\t\t\t\t} else\n\t\t\t\t\tSystem.err.println(x);\n\t\t\t} catch (Exception ex) {\n\t\t\t\tex.printStackTrace();\n\t\t\t}\n\t\t});\n\t\tuseAdvancedSTL.setOnAction(event -> {\n\t\t\tboolean selected = useAdvancedSTL.isSelected();\n\t\t\tConfigurationDatabase.setObject(\"MenueSettings\", \"CSG_Advanced_STL\", selected);\n\t\t\tCSG.setPreventNonManifoldTriangles(selected);\n\t\t});\n\t\tshowCSGProgress.setOnAction(event -> {\n\t\t\tConfigurationDatabase.setObject(\"MenueSettings\", \"printCSG\", showCSGProgress.isSelected());\n\t\t});\n\t\teu.mihosoft.vrl.v3d.svg.SVGLoad.getProgressDefault();\n\t\t// eu.mihosoft.vrl.v3d.svg.SVGLoad.setProgressDefault(new ISVGLoadProgress() {\n\t\t// @Override\n\t\t// public void onShape(CSG newShape) {\n\t\t// BowlerStudioController.addCsg(newShape);\n\t\t// }\n\t\t// });\n\t\tRunnable r = () -> {\n\t\t\tboolean parseBoolean = Boolean.parseBoolean(ConfigurationDatabase\n\t\t\t\t\t.getObject(\"MenueSettings\", \"CSG_Advanced_STL\", CSG.isPreventNonManifoldTriangles()).toString());\n\t\t\tCSG.setPreventNonManifoldTriangles(parseBoolean);\n\t\t\tuseAdvancedSTL.setSelected(parseBoolean);\n\t\t\tshowCSGProgress.setSelected(Boolean\n\t\t\t\t\t.parseBoolean(ConfigurationDatabase.getObject(\"MenueSettings\", \"printCSG\", true).toString()));\n\t\t};\n\t\tnew Thread(r).start();\n\n\t\tCreatureLab3dController.getEngine().setControls(showRuler, idlespin, autohighlight);\n\t\tCreatureLab3dController.getEngine().reattachMouseHandlers();\n\t\tWindowMenu.getItems().addAll(showRuler, idlespin, autohighlight, showCSGProgress, useAdvancedSTL);\n\n\t\tnew Thread() {\n\t\t\tpublic void run() {\n\t\t\t\tsetUncaughtExceptionHandler(new IssueReportingExceptionHandler());\n\t\t\t\ttry {\n\t\t\t\t\tif (vitaminsMenu == null)\n\t\t\t\t\t\tthrow new RuntimeException(\"Vitamins menu was not inserted\");\n\t\t\t\t\tif (vitaminsMenu.getItems() == null)\n\t\t\t\t\t\tthrow new RuntimeException(\"Vitamins menu items are null\");\n\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\tvitaminsMenu.getItems().add(new SeparatorMenuItem());\n\t\t\t\t\t});\n\t\t\t\t\tList<String> types = Vitamins.listVitaminTypes().stream().sorted().collect(Collectors.toList());\n\t\t\t\t\tfor (String s : types) {\n\t\t\t\t\t\taddVitaminType(s);\n\t\t\t\t\t\tVitamins.getVitaminFile(s, () -> {\n\t\t\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\t\t\tgetTypeMenu(s).getItems().clear();\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tsetUpSizes(getTypeMenu(s), s);\n\t\t\t\t\t\t\taddVitaminType(s);\n\t\t\t\t\t\t}, false);\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\texp.uncaughtException(Thread.currentThread(), e);\n\t\t\t\t}\n\t\t\t\tif (!PasswordManager.hasNetwork()) {\n\t\t\t\t\tnew Thread(() -> {\n\t\t\t\t\t\topenFilesInUI();\n\t\t\t\t\t}).start();\n\t\t\t\t}\n\t\t\t}\n\t\t}.start();\n\n\t}\n\n\tpublic static <T extends Comparable<? super T>> List<T> asSortedList(Set<T> c) {\n\t\tList<T> list = new ArrayList<T>(c);\n\t\tjava.util.Collections.sort(list);\n\t\treturn list;\n\t}\n\n\tpublic static BowlerStudioMenu getSelfRef() {\n\t\treturn selfRef;\n\t}\n\n\tpublic static void setSelfRef(BowlerStudioMenu selfRef) {\n\t\tBowlerStudioMenu.selfRef = selfRef;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/BowlerStudioMenuWorkspace.java",
    "content": "package com.neuronrobotics.bowlerstudio;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.HashMap;\n\nimport org.eclipse.jgit.api.errors.WrongRepositoryStateException;\n\nimport com.neuronrobotics.bowlerstudio.assets.ConfigurationDatabase;\nimport com.neuronrobotics.bowlerstudio.scripting.ScriptingEngine;\n\nimport javafx.scene.control.Menu;\n\n@SuppressWarnings(\"restriction\")\npublic class BowlerStudioMenuWorkspace {\n\tprivate static final String key = \"workspaceList\";\n\tprivate static String catagory = \"list\";\n\tprivate static Menu workspaceMenu;\n\tprivate static final int maxMenueSize = 40;\n\tprivate static boolean sorting = false;\n\tprivate static HashMap<String, Integer> rank = new HashMap<String, Integer>();\n\tprivate static boolean running = false;\n\tprivate static ArrayList<ArrayList<String>> wp = null;\n\n\tpublic static void init(Menu workspacemenu) {\n\t\tif (workspacemenu == null)\n\t\t\tthrow new RuntimeException();\n\t\tworkspaceMenu = workspacemenu;\n\t}\n\n\tpublic static void loginEvent() {\n\t\tif (running)\n\t\t\treturn;\n\t\trunning = true;\n\t\trank.clear();\n\t\tnew Thread(() -> {\n\t\t\tif (ScriptingEngine.hasNetwork()) {\n\t\t\t\twp = (ArrayList<ArrayList<String>>) ConfigurationDatabase.getObject(key, catagory,\n\t\t\t\t\t\tnew ArrayList<ArrayList<String>>());\n\n\t\t\t\tfor (ArrayList<String> entry : wp) {\n\t\t\t\t\tString o = entry.get(0);\n\t\t\t\t\tString message = entry.get(1);\n\t\t\t\t\ttry {\n\t\t\t\t\t\tScriptingEngine.pull(o);\n\t\t\t\t\t} catch (WrongRepositoryStateException ex) {\n\t\t\t\t\t\t// ignore, unsaved work\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\tBowlerStudioMenu.checkandDelete(o);\n\t\t\t\t\t} catch (Throwable ex) {\n\t\t\t\t\t\tex.printStackTrace();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tupdateMenu();\n\t\t\t//\n\t\t\t// for (int i = 0; i < ConfigurationDatabase.keySet(key).size(); i++) {\n\t\t\t// try {\n\t\t\t// String o = (String) ConfigurationDatabase.keySet(key).toArray()[i];\n\t\t\t// if (o.endsWith(\".git\")) {\n\t\t\t// boolean wasState = ScriptingEngine.isPrintProgress();\n\t\t\t// ScriptingEngine.setPrintProgress(false);\n\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"Pulling workspace \" + o);\n\t\t\t// try {\n\t\t\t// ScriptingEngine.pull(o);\n\t\t\t// } catch (WrongRepositoryStateException ex) {\n\t\t\t// // ignore, unsaved work\n\t\t\t// } catch (Exception e) {\n\t\t\t// BowlerStudioMenu.checkandDelete(o);\n\t\t\t// } catch (Throwable ex) {\n\t\t\t// ex.printStackTrace();\n\t\t\t// ConfigurationDatabase.removeObject(key, o);\n\t\t\t// // ScriptingEngine.deleteRepo(o);\n\t\t\t// // i--;\n\t\t\t// }\n\t\t\t// ScriptingEngine.setPrintProgress(wasState);\n\t\t\t//\n\t\t\t// } else {\n\t\t\t// ConfigurationDatabase.remove(key, o);\n\t\t\t// }\n\t\t\t// } catch (Exception e) {\n\t\t\t// e.printStackTrace();\n\t\t\t// }\n\t\t\t// }\n\t\t\t// running = false;\n\n\t\t}).start();\n\n\t}\n\n\tpublic static void add(String url) {\n\t\tadd(url, BowlerStudioMenu.gitURLtoMessage(url));\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tpublic static void add(String url, String menueMessage) {\n\t\tif (menueMessage == null)\n\t\t\tthrow new RuntimeException(\"Menu Message can not be \" + menueMessage);\n\t\tif (menueMessage.length() < 2) {\n\t\t\tmenueMessage = new Date().toString();\n\t\t}\n\t\ttry {\n\t\t\tif (!BowlerStudio.checkValidURL(url)) {\n\t\t\t\tBowlerStudio.runLater(\n\t\t\t\t\t\t() -> BowlerStudio.showExceptionAlert(new RuntimeException(), \"URL does not exist: \" + url));\n\t\t\t\treturn;\n\t\t\t}\n\t\t} catch (Exception ex) {\n\t\t\tex.printStackTrace();\n\t\t\treturn;\n\t\t}\n\t\tfor (ArrayList<String> entry : wp) {\n\t\t\tString o = entry.get(0);\n\t\t\tif (o.contentEquals(url)) {\n\t\t\t\tcom.neuronrobotics.sdk.common.Log.debug(\"Already in menu \" + url);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tArrayList<String> data = new ArrayList<String>();\n\t\tdata.add(url);\n\t\tdata.add(menueMessage);\n\t\twp.add(0, data);\n\t\tif (wp.size() >= maxMenueSize)\n\t\t\twp.remove(wp.size() - 1);\n\t\tConfigurationDatabase.save();\n\n\t\t//\n\t\t// Object object = ConfigurationDatabase.getObject(key, url, null);\n\t\t// if (object == null) {\n\t\t// data = new ArrayList<String>();\n\t\t// data.add(menueMessage);\n\t\t// data.add(new Long(System.currentTimeMillis()).toString());\n\t\t// System.err.println(\"Adding URL to workspace \" + url);\n\t\t// ConfigurationDatabase.put(key, url, data);\n\t\t// ConfigurationDatabase.save();\n\t\t// // com.neuronrobotics.sdk.common.Log.error(\"Workspace add: \" + url);\n\t\t// }\n\n\t\t// data = (ArrayList<String>) workspaceData.get(url);\n\t\t// data.set(1, new Long(System.currentTimeMillis()).toString());\n\t\tupdateMenu();\n\t\t//\n\n\t}\n\n\t// @SuppressWarnings(\"unchecked\")\n\t// public static void sort() {\n\t// if (sorting)\n\t// return;\n\t// sorting = true;\n\t//\n\t// boolean rankChanged = false;\n\t// try {\n\t// ArrayList<String> myOptions = new ArrayList<String>();\n\t//\n\t// for (String o : ConfigurationDatabase.keySet(key)) {\n\t// // com.neuronrobotics.sdk.common.Log.error(\"Opt: \"+o);\n\t// myOptions.add(o);\n\t// }\n\t//\n\t// ArrayList<String> menu = new ArrayList<>();\n\t// while (myOptions.size() > 0) {\n\t// int bestIndex = 0;\n\t// String besturl = (String) myOptions.get(bestIndex);\n\t// ArrayList<String> arrayList = (ArrayList<String>)\n\t// ConfigurationDatabase.get(key, besturl);\n\t// long newestTime = 0;\n\t// if (arrayList != null)\n\t// if (arrayList.size() > 1) {\n\t// newestTime = Long.parseLong(arrayList.get(1));\n\t// for (int i = 0; i < myOptions.size(); i++) {\n\t// String nowurl = (String) myOptions.get(i);\n\t// long myTime = Long.parseLong(arrayList.get(1));\n\t// if (myTime >= newestTime) {\n\t// newestTime = myTime;\n\t// besturl = nowurl;\n\t// bestIndex = i;\n\t// }\n\t// }\n\t// } else\n\t// continue;\n\t// String removedURL = (String) myOptions.remove(bestIndex);\n\t//\n\t// // clone all repos from git\n\t// try {\n\t// // ScriptingEngine.pull(removedURL);\n\t// menu.add(removedURL);\n\t// } catch (Exception e) {\n\t// // repo is broken or missing\n\t// e.printStackTrace();\n\t// System.err.println(\"Removing from workspace: \" + removedURL);\n\t// remove(removedURL);\n\t// }\n\t//\n\t// }\n\t//\n\t// for (int i = 0; i < menu.size(); i++) {\n\t// String url = menu.get(i);\n\t// if (rank.get(url) == null) {\n\t// rankChanged = true;\n\t// rank.put(url, i);\n\t// // com.neuronrobotics.sdk.common.Log.error(\"Rank firstNoted : \"+url+\" \"+i);\n\t// }\n\t// if (rank.get(url).intValue() != i) {\n\t// rankChanged = true;\n\t//\n\t// }\n\t// rank.put(url, i);\n\t// }\n\t// if (rankChanged) {\n\t// BowlerStudio.runLater(() -> {\n\t// if (workspaceMenu.getItems() != null)\n\t// workspaceMenu.getItems().clear();\n\t//\n\t// new Thread(() -> {\n\t// int numAdded = 0;\n\t// for (String url : menu) {\n\t// if(numAdded>=maxMenueSize) {\n\t// System.err.println(\"Pruning \"+url+\" because too big!\");\n\t// remove(url);\n\t// continue;\n\t//\n\t// }\n\t// // com.neuronrobotics.sdk.common.Log.error(\"Workspace : \" + url);\n\t// ArrayList<String> arrayList = (ArrayList<String>)\n\t// ConfigurationDatabase.getObject(key, url,\n\t// new ArrayList<>());\n\t// if (arrayList != null)\n\t// if (arrayList.size() >= 0)\n\t// try {\n\t// BowlerStudioMenu.setUpRepoMenue(workspaceMenu, url, false, false,\n\t// arrayList.get(0));\n\t// numAdded++;\n\t// } catch (Throwable t) {\n\t// com.neuronrobotics.sdk.common.Log\n\t// .error(\"Error with \" + url + \" \" + arrayList.toArray());\n\t// t.printStackTrace();\n\t// }\n\t//\n\t// }\n\t// sorting = false;\n\t// }).start();\n\t// });\n\t// } else {\n\t// sorting = false;\n\t// }\n\t//\n\t// } catch (Exception ex) {\n\t// ex.printStackTrace();\n\t// }\n\t// if (rankChanged) {\n\t// // com.neuronrobotics.sdk.common.Log.error(\"Sorting workspace...\");\n\t// new Thread(() -> {\n\t// ConfigurationDatabase.save();\n\t// }).start();\n\t// }\n\t// }\n\n\t// public static HashMap<String, Object> getWorkspaceData() {\n\t// return ConfigurationDatabase.getParamMap(\"workspace\");\n\t// }\n\tprivate static void updateMenu() {\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tif (workspaceMenu.getItems() != null)\n\t\t\t\tworkspaceMenu.getItems().clear();\n\t\t\tfor (int i = 0; i < wp.size(); i++) {\n\t\t\t\tArrayList<String> entry = wp.get(i);\n\t\t\t\tString url = entry.get(0);\n\t\t\t\tString message = entry.get(1);\n\t\t\t\tBowlerStudioMenu.setUpRepoMenue(workspaceMenu, url, false, false, message);\n\t\t\t}\n\t\t});\n\t}\n\n\tpublic static void remove(String url) {\n\t\tnew Exception(\"BowlerStudiotMenuWorkspace removing URL \" + url).printStackTrace();\n\t\tfor (int i = 0; i < wp.size(); i++) {\n\t\t\tArrayList<String> entry = wp.get(i);\n\t\t\tif (entry.get(0).contentEquals(url)) {\n\t\t\t\twp.remove(entry);\n\t\t\t\tConfigurationDatabase.save();\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tupdateMenu();\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/BowlerStudioModularFrame.java",
    "content": "package com.neuronrobotics.bowlerstudio;\n/**\n * Sample Skeleton for \"BowlerStudioModularFrame.fxml\" Controller Class\n * You can copy and paste this code into your favorite IDE\n **/\n\nimport com.neuronrobotics.bowlerstudio.assets.AssetFactory;\nimport com.neuronrobotics.bowlerstudio.scripting.PasswordManager;\nimport com.neuronrobotics.bowlerstudio.scripting.ScriptingFileWidget;\nimport com.neuronrobotics.bowlerstudio.tabs.WebTab;\nimport javafx.fxml.FXML;\nimport javafx.fxml.FXMLLoader;\nimport javafx.scene.Node;\nimport javafx.scene.control.Tab;\nimport javafx.scene.control.TabPane;\nimport javafx.scene.image.Image;\nimport javafx.scene.layout.AnchorPane;\nimport javafx.scene.layout.BorderPane;\nimport javafx.scene.layout.VBox;\nimport javafx.stage.Stage;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.URL;\nimport java.util.HashMap;\nimport java.util.ResourceBundle;\n@SuppressWarnings(\"restriction\")\npublic class BowlerStudioModularFrame {\n\n\t@FXML // ResourceBundle that was given to the FXMLLoader\n\tprivate ResourceBundle resources;\n\n\t@FXML // URL location of the FXML file that was given to the FXMLLoader\n\tprivate URL location;\n\n\t@FXML // fx:id=\"editorContainer\"\n\tprivate AnchorPane editorContainer; // Value injected by FXMLLoader\n\t@FXML // fx:id=\"editorContainer\"\n\tprivate AnchorPane threeDarea;\n\t@FXML // fx:id=\"editorContainer\"\n\tprivate AnchorPane textAreAnchor;\n\t@FXML // fx:id=\"editorContainer\"\n\tprivate AnchorPane terminalArea;\n\t@FXML // fx:id=\"editorContainer\"\n\tprivate AnchorPane connectionsTabAnchor;\n\t@FXML // fx:id=\"editorContainer\"\n\tprivate AnchorPane terminalTabAnchor;\n\t@FXML // fx:id=\"menurAnchor\"\n\tprivate AnchorPane menurAnchor; // Value injected by FXMLLoader\n\n\tprivate Image dockImage;\n\n\t// private DockNode tutorialDockNode;\n\n\t// private DockNode connectionManagerDockNode;\n\t@FXML\n\tprivate TabPane dockPane;\n\n\tpublic BowlerStudioMenu menueController;\n\n\t// private InvalidationListener connectionManagerRemover;\n\n\tprivate CreatureLab3dController creatureLab3dController;\n\n\t// private DockNode creatureLab3dDockNode=null;\n\n\t// private InvalidationListener creatureManagerRemover;\n\n\tprivate BowlerStudioController controller;\n\n\tprivate static Stage primaryStage;\n\n\tprivate static BowlerStudioModularFrame bowlerStudioModularFrame;\n\n\t// private HashMap<Tab, DockNode> webTabs = new HashMap<>();\n\tprivate HashMap<String, Boolean> isOpen = new HashMap<>();\n\n\tprivate Terminal terminal;\n\n\t// private DockNode terminalDockNode;\n\tprivate boolean startup = false;\n\n\tprivate WebTab webtab;\n\n\t@FXML // This method is called by the FXMLLoader when initialization is\n\t\t\t// complete\n\tvoid initialize() throws Exception {\n\t\tbowlerStudioModularFrame = this;\n\t\tassert editorContainer != null\n\t\t\t\t: \"fx:id=\\\"editorContainer\\\" was not injected: check your FXML file 'BowlerStudioModularFrame.fxml'.\";\n\t\tassert menurAnchor != null\n\t\t\t\t: \"fx:id=\\\"menurAnchor\\\" was not injected: check your FXML file 'BowlerStudioModularFrame.fxml'.\";\n\t\tassert dockPane != null : \"DockPain not injected\";\n\t\tdockImage = AssetFactory.loadAsset(\"BowlerStudioModularFrameIcon.png\");\n\t\t// final Tab newtab = new Tab();\n\t\t// newtab.setText(\"\");\n\t\t// newtab.setClosable(false);\n\t\t// newtab.setGraphic(AssetFactory.loadIcon(\"New-Web-Tab.png\"));\n\t\tString homeURL = Tutorial.getHomeUrl();\n\n\t\tcontroller = new BowlerStudioController();\n\t\tWebTab.setBSController(controller);\n\n\t\tdockPane.setTabClosingPolicy(TabPane.TabClosingPolicy.ALL_TABS);\n\t\twebtab = new WebTab(\"Documentation\", homeURL, true);\n\t\taddTab(webtab, false);\n\n\t\tNode content = ConnectionManager.getConnectionManager().getContent();\n\n\t\tconnectionsTabAnchor.getChildren().add(content);\n\t\tAnchorPane.setTopAnchor(content, 0.0);\n\t\tAnchorPane.setRightAnchor(content, 0.0);\n\t\tAnchorPane.setBottomAnchor(content, 0.0);\n\t\tAnchorPane.setLeftAnchor(content, 0.0);\n\n\t\t// Initial docked setup\n\t\taddTutorial();\n\n\t\tFXMLLoader WindowLoader3d;\n\t\tWindowLoader3d = AssetFactory.loadLayout(\"layout/CreatureLab.fxml\");\n\t\tcreatureLab3dController = new CreatureLab3dController();\n\t\tBowlerStudio.setCreatureLab3d(creatureLab3dController);\n\t\tWindowLoader3d.setController(creatureLab3dController);\n\t\tWindowLoader3d.setClassLoader(CreatureLab3dController.class.getClassLoader());\n\t\tFXMLLoader commandLine;\n\t\tcommandLine = AssetFactory.loadLayout(\"layout/Terminal.fxml\");\n\t\tterminal = new Terminal();\n\t\tcommandLine.setController(terminal);\n\t\tcommandLine.setClassLoader(Terminal.class.getClassLoader());\n\t\tFXMLLoader menueBar;\n\t\tmenueBar = AssetFactory.loadLayout(\"layout/BowlerStudioMenuBar.fxml\");\n\t\tmenueController = new BowlerStudioMenu(this, creatureLab3dController);\n\t\tmenueBar.setController(menueController);\n\t\tmenueBar.setClassLoader(BowlerStudioMenu.class.getClassLoader());\n\n\t\ttry {\n\t\t\tmenueBar.load();\n\t\t\tWindowLoader3d.load();\n\t\t\tcommandLine.load();\n\t\t} catch (Exception e) {\n\t\t\t// Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t\tthrow e;\n\t\t}\n\t\tBorderPane menue = (BorderPane) menueBar.getRoot();\n\t\tBorderPane threed = (BorderPane) WindowLoader3d.getRoot();\n\t\tVBox cmd = (VBox) commandLine.getRoot();\n\t\tthreeDarea.getChildren().add(threed);\n\t\tAnchorPane.setTopAnchor(threed, 0.0);\n\t\tAnchorPane.setRightAnchor(threed, 0.0);\n\t\tAnchorPane.setBottomAnchor(threed, 0.0);\n\t\tAnchorPane.setLeftAnchor(threed, 0.0);\n\t\t// do {\n\t\t// try {\n\t\t// creatureLab3dDockNode = new DockNode(threed, \"Creature Lab\",\n\t\t// AssetFactory.loadIcon(\"CreatureLab-Tab.png\"));\n\t\t// }catch(Exception e) {\n\t\t// e.printStackTrace();\n\t\t// ThreadUtil.wait(100);\n\t\t// }\n\t\t// } while (creatureLab3dDockNode==null);\n\t\t// creatureLab3dDockNode.setPrefSize(500, 500);\n\n\t\tterminalTabAnchor.getChildren().add(cmd);\n\t\tAnchorPane.setTopAnchor(cmd, 0.0);\n\t\tAnchorPane.setRightAnchor(cmd, 0.0);\n\t\tAnchorPane.setBottomAnchor(cmd, 0.0);\n\t\tAnchorPane.setLeftAnchor(cmd, 0.0);\n\t\t// terminalDockNode = new DockNode(cmd, \"Terminal\",\n\t\t// AssetFactory.loadIcon(\"Command-Line.png\"));\n\t\t// terminalDockNode.setPrefSize(400, 400);\n\n\t\t// Add the dock pane to the window\n\t\tmenurAnchor.getChildren().add(menue);\n\t\tAnchorPane.setTopAnchor(menue, 0.0);\n\t\tAnchorPane.setRightAnchor(menue, 0.0);\n\t\tAnchorPane.setLeftAnchor(menue, 0.0);\n\t\tAnchorPane.setBottomAnchor(menue, 0.0);\n\t\tisOpen.put(\"showCreatureLab\", false);\n\t\tisOpen.put(\"showTerminal\", false);\n\t\tisOpen.put(\"showDevices\", false);\n\n\t\t// focus on the tutorial to start\n\t\t// BowlerStudio.runLater(() -> getTutorialDockNode().requestFocus());\n\t\t// connectionManagerDockNode.onMouseClickedProperty().addListener((a, b, c) -> {\n\t\t// com.neuronrobotics.sdk.common.Log.error(\"Cloick\");\n\t\t// });\n\n\t}\n\n\tpublic void loadMobilebaseFromGit(String id, String file) {\n\t\tmenueController.loadMobilebaseFromGit(id, file);\n\t}\n\n\tprivate void addTutorial() {\n\t\t// BowlerStudio.runLater(() -> getTutorialDockNode().dock(dockPane,\n\t\t// DockPos.LEFT));\n\n\t}\n\n\tpublic void showConectionManager() {\n\n\t\t// String key = \"showDevices\";\n\t\t// if (isOpen.get(key) == null) {\n\t\t// isOpen.put(key, false);\n\t\t// }\n\t\t// if (isOpen.get(\"showTerminal\") == null) {\n\t\t// isOpen.put(\"showTerminal\", false);\n\t\t// }\n\t\t// BowlerStudio.runLater(() -> {\n\t\t// if (!isOpen.get(key)) {\n\t\t// isOpen.put(key, true);\n\t\t// if (isOpen.get(\"showTerminal\"))\n\t\t// connectionManagerDockNode.dock(dockPane, DockPos.RIGHT, terminalDockNode);\n\t\t// else\n\t\t// connectionManagerDockNode.dock(dockPane, DockPos.BOTTOM,\n\t\t// getTutorialDockNode());\n\t\t// connectionManagerDockNode.requestFocus();\n\t\t//\n\t\t// connectionManagerDockNode.closedProperty().addListener(new\n\t\t// InvalidationListener() {\n\t\t// @Override\n\t\t// public void invalidated(Observable event) {\n\t\t// connectionManagerDockNode.closedProperty().removeListener(this);\n\t\t// isOpen.put(key, false);\n\t\t// }\n\t\t// });\n\t\t//\n\t\t// }\n\t\t// BowlerStudio.runLater(() -> connectionManagerDockNode.requestFocus());\n\t\t// });\n\n\t}\n\n\tpublic void showTerminal() {\n\n\t\t// String key = \"showTerminal\";\n\t\t// if (isOpen.get(key) == null || getTutorialDockNode()==null\n\t\t// ||terminalDockNode==null) {\n\t\t// isOpen.put(key, false);\n\t\t// return;\n\t\t// }\n\t\t// if (isOpen.get(\"showDevices\") == null) {\n\t\t// isOpen.put(\"showDevices\", false);\n\t\t// }\n\t\t// if (!isOpen.get(key)) {\n\t\t// isOpen.put(key, true);\n\t\t// BowlerStudio.runLater(() -> {\n\t\t//\n\t\t// if (isOpen.get(\"showDevices\"))\n\t\t// terminalDockNode.dock(dockPane, DockPos.LEFT, connectionManagerDockNode);\n\t\t// else\n\t\t// terminalDockNode.dock(dockPane, DockPos.BOTTOM, getTutorialDockNode());\n\t\t// terminalDockNode.requestFocus();\n\t\t//\n\t\t// if (ScriptingEngine.isLoginSuccess()) {\n\t\t//\n\t\t// }\n\t\t//\n\t\t// terminalDockNode.closedProperty().addListener(new InvalidationListener() {\n\t\t// @Override\n\t\t// public void invalidated(Observable event) {\n\t\t// terminalDockNode.closedProperty().removeListener(this);\n\t\t// isOpen.put(key, false);\n\t\t// }\n\t\t// });\n\t\t//\n\t\t// BowlerStudio.runLater(() -> terminalDockNode.requestFocus());\n\t\t//\n\t\t// });\n\t\t//\n\t\t// }\n\t}\n\n\tpublic void showCreatureLab() {\n\t\tshowCreatureLab(0);\n\t}\n\n\tpublic void showCreatureLab(int depth) {\n\t\t// String key = \"showCreatureLab\";\n\t\t// Boolean boolean1 = isOpen.get(key);\n\t\t// if(boolean1!=null)\n\t\t// if (!boolean1) {\n\t\t// isOpen.put(key, true);\n\t\t// new Thread(() -> {\n\t\t// //ThreadUtil.wait(100);\n\t\t//\n\t\t// BowlerStudio.runLater(() -> {\n\t\t// try {\n\t\t// creatureLab3dDockNode.dock(dockPane, DockPos.RIGHT);\n\t\t// isOpen.put(key, true);\n\t\t// return;\n\t\t// } catch (NullPointerException e) {\n\t\t// // keep trying to open\n\t\t// // e.printStackTrace();\n\t\t// isOpen.put(key, false);\n\t\t// if (depth < 2) {\n\t\t// showCreatureLab(depth + 1);\n\t\t// } else\n\t\t// BowlerStudio.printStackTrace(e);// fail and show\n\t\t// // user\n\t\t// } catch (Exception e) {\n\t\t// isOpen.put(key, false);\n\t\t// BowlerStudio.printStackTrace(e);// fail and show user\n\t\t// }\n\t\t//\n\t\t// });\n\t\t//\n\t\t// BowlerStudio.runLater(() ->\n\t\t// creatureLab3dDockNode.closedProperty().addListener(new InvalidationListener()\n\t\t// {\n\t\t// @Override\n\t\t// public void invalidated(Observable event) {\n\t\t// creatureLab3dDockNode.closedProperty().removeListener(this);\n\t\t// isOpen.put(key, false);\n\t\t// }\n\t\t// }));\n\t\t//\n\t\t// }).start();\n\t\t// }\n\t\t// else\n\t\t// BowlerStudio.runLater(() -> creatureLab3dDockNode.requestFocus());\n\n\t}\n\n\tpublic static Stage getPrimaryStage() {\n\t\treturn primaryStage;\n\t}\n\n\tpublic static void setPrimaryStage(Stage primaryStage) {\n\t\tBowlerStudioModularFrame.primaryStage = primaryStage;\n\t\t// DockNode.addStageToDockingSystem(primaryStage);\n\t\t// DockNode.setModifyer(new IStageModifyer() {\n\t\t// @Override\n\t\t// public void onNewStage(Stage s) {\n\t\t// Parent r = s.getScene().getRoot();\n\t\t// FontSizeManager.addListener(fontNum->{\n\t\t// BowlerStudioController.getBowlerStudio().setFontSize(fontNum);\n\t\t// double tmp = FontSizeManager.getImageScale()*9;\n\t\t// r.setStyle(\"-fx-font-size: \"+((int)tmp)+\"pt\");\n\t\t// });\n\t\t// }\n\t\t// });\n\t}\n\n\tpublic ScriptingFileWidget createFileTab(File file) {\n\t\t// Auto-generated method stub\n\t\treturn controller.createFileTab(file);\n\t}\n\n\tpublic void openUrlInNewTab(URL url) {\n\t\tBowlerStudio.runLater(() -> {\n\t\t\ttry {\n\t\t\t\tif (PasswordManager.getUsername() != null) {\n\t\t\t\t\tWebTab newTab = new WebTab(\"Web\", url.toExternalForm(), false);\n\n\t\t\t\t\taddTab(newTab, true);\n\t\t\t\t}\n\t\t\t} catch (IOException | InterruptedException e) {\n\t\t\t\t// Auto-generated catch block\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t});\n\t}\n\n\tpublic void closeTab(Tab newTab) {\n\n\t\tdockPane.getTabs().remove(newTab);\n\t}\n\n\tpublic void addTab(Tab newTab, boolean b) {\n\t\tcom.neuronrobotics.sdk.common.Log.error(\"Loading a new tab: \" + newTab.getText());\n\t\tnewTab.setClosable(b);\n\t\tdockPane.getTabs().add(newTab);\n\t}\n\n\tpublic static BowlerStudioModularFrame getBowlerStudioModularFrame() {\n\t\treturn bowlerStudioModularFrame;\n\t}\n\n\tpublic void setSelectedTab(Tab tab) {\n\t\tcom.neuronrobotics.sdk.common.Log.error(\"Selecting a new tab: \" + tab.getText());\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tdockPane.getSelectionModel().select(tab);\n\t\t});\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/ChangeAssetRepoController.java",
    "content": "package com.neuronrobotics.bowlerstudio;\n\nimport com.neuronrobotics.bowlerstudio.assets.AssetFactory;\nimport com.neuronrobotics.bowlerstudio.assets.ConfigurationDatabase;\nimport com.neuronrobotics.bowlerstudio.assets.FontSizeManager;\n\nimport javafx.application.Application;\nimport javafx.event.ActionEvent;\nimport javafx.fxml.FXML;\nimport javafx.fxml.FXMLLoader;\nimport javafx.scene.Parent;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.TextField;\nimport javafx.stage.Modality;\nimport javafx.stage.Stage;\n\n/**\n * Created by Ryan Benasutti on 2/6/2016.\n */\n\npublic class ChangeAssetRepoController extends Application {\n\t@FXML\n\tprivate TextField repoField;\n\n\t@FXML\n\tprivate Button changeRepoButton, cancelButton;\n\n\tpublic ChangeAssetRepoController() {\n\t}\n\n\t@SuppressWarnings(\"restriction\")\n\t@Override\n\tpublic void start(Stage primaryStage) throws Exception {\n\t\tFXMLLoader loader = AssetFactory.loadLayout(\"layout/changeAssetRepo.fxml\", true);\n\t\tParent root;\n\t\tloader.setController(this);\n\t\t// This is needed when loading on MAC\n\t\tloader.setClassLoader(getClass().getClassLoader());\n\t\troot = loader.load();\n\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\tint tmp = fontNum - 10;\n\t\t\tif (tmp < 12)\n\t\t\t\ttmp = 12;\n\t\t\troot.setStyle(\"-fx-font-size: \" + tmp + \"pt\");\n\t\t});\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tprimaryStage.setTitle(\"Change Asset Repository\");\n\n\t\t\tScene scene = new Scene(root);\n\t\t\tprimaryStage.setScene(scene);\n\t\t\tprimaryStage.initModality(Modality.WINDOW_MODAL);\n\t\t\tprimaryStage.setResizable(true);\n\t\t\tprimaryStage.show();\n\t\t});\n\t}\n\n\t@FXML\n\tpublic void onChangeRepo(ActionEvent event) {\n\t\tString repo = repoField.getText().replaceAll(\"git://\", \"https://\");\n\t\tnew Thread(() -> {\n\t\t\tConfigurationDatabase.setObject(\"BowlerStudioConfigs\", \"skinRepo\", repo);\n\t\t\tConfigurationDatabase.save();\n\n\t\t}).start();\n\n\t\tStage stage = (Stage) changeRepoButton.getScene().getWindow();\n\t\tstage.close();\n\n\t}\n\n\t@FXML\n\tpublic void onCancel(ActionEvent event) {\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tStage stage = (Stage) cancelButton.getScene().getWindow();\n\t\t\tstage.close();\n\t\t});\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/ConnectionManager.java",
    "content": "package com.neuronrobotics.bowlerstudio;\n\nimport com.neuronrobotics.bowlerstudio.assets.AssetFactory;\nimport com.neuronrobotics.bowlerstudio.assets.FontSizeManager;\nimport com.neuronrobotics.bowlerstudio.utils.BowlerConnectionMenu;\nimport com.neuronrobotics.imageprovider.AbstractImageProvider;\n//import com.neuronrobotics.imageprovider.OpenCVImageProvider;\nimport com.neuronrobotics.imageprovider.StaticFileProvider;\nimport com.neuronrobotics.imageprovider.URLImageProvider;\nimport com.neuronrobotics.sdk.addons.gamepad.BowlerJInputDevice;\nimport com.neuronrobotics.sdk.addons.kinematics.FirmataBowler;\nimport com.neuronrobotics.sdk.addons.kinematics.gcodebridge.GcodeDevice;\nimport com.neuronrobotics.sdk.common.*;\nimport com.neuronrobotics.sdk.javaxusb.UsbCDCSerialConnection;\nimport com.neuronrobotics.sdk.network.BowlerTCPClient;\nimport com.neuronrobotics.sdk.network.UDPBowlerConnection;\nimport com.neuronrobotics.sdk.serial.SerialConnection;\nimport com.neuronrobotics.sdk.ui.AbstractConnectionPanel;\nimport com.neuronrobotics.sdk.wireless.bluetooth.BluetoothSerialConnection;\n\nimport gnu.io.NRSerialPort;\n\nimport javafx.event.ActionEvent;\nimport javafx.event.EventHandler;\nimport javafx.scene.Node;\nimport javafx.scene.control.*;\nimport javafx.scene.control.Alert.AlertType;\nimport javafx.scene.image.Image;\nimport javafx.scene.image.ImageView;\nimport javafx.scene.layout.HBox;\nimport javafx.scene.layout.VBox;\nimport javafx.scene.text.Text;\nimport javafx.stage.FileChooser;\nimport javafx.stage.Modality;\nimport javafx.stage.Stage;\n\nimport java.io.File;\n\nimport java.net.MalformedURLException;\nimport java.net.URISyntaxException;\nimport java.net.URI;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.Set;\n\n//import org.bytedeco.javacv.OpenCVFrameGrabber;\n\npublic class ConnectionManager extends Tab implements IDeviceAddedListener, EventHandler<ActionEvent> {\n\n\tprivate static VBox rootItem;\n\tprivate static final ArrayList<PluginManagerWidget> plugins = new ArrayList<PluginManagerWidget>();\n\t// private BowlerStudioController bowlerStudioController;\n\tString formatStr = \"%1$-40s %2$-60s  %3$-40s\";\n\tprivate static final ConnectionManager connectionManager;\n\tprivate static Button disconnectAll;\n\tprivate static HBox topLine;\n\tfinal static Accordion accordion = new Accordion();\n\tstatic {\n\t\tconnectionManager = new ConnectionManager();\n\t}\n\n\tprivate Node getIcon(String s) {\n\t\treturn new ImageView(new Image(AbstractConnectionPanel.class.getResourceAsStream(s)));\n\t}\n\n\tpublic ConnectionManager() {\n\t\tif (connectionManager != null) {\n\t\t\tthrow new RuntimeException(\n\t\t\t\t\t\"Connection manager is a static singleton, access it using ConnectionManager.getConnectionmanager()\");\n\t\t}\n\t\tsetText(\"My Devices\");\n\t\tsetGraphic(AssetFactory.loadIcon(\"My-Devices.png\"));\n\t\trootItem = new VBox(10);\n\n\t\t// rootItem.getColumnConstraints().add(new ColumnConstraints(30)); // column 1\n\t\t// is 75 wide\n\t\t// rootItem. getColumnConstraints().add(new ColumnConstraints(80)); // column 2\n\t\t// is 300 wide\n\t\t// rootItem.getColumnConstraints().add(new ColumnConstraints(100)); // column 2\n\t\t// is 100 wide\n\t\t// rootItem.getColumnConstraints().add(new ColumnConstraints(50)); // column 2\n\t\t// is 100 wide\n\t\t//\n\t\ttopLine = new HBox(20);\n\n\t\tdisconnectAll = new Button(\"Disconnect All\", AssetFactory.loadIcon(\"Disconnect-All.png\"));\n\t\tdisconnectAll.setOnAction(new EventHandler<ActionEvent>() {\n\t\t\t@Override\n\t\t\tpublic void handle(ActionEvent e) {\n\t\t\t\tdisconnectAll();\n\t\t\t}\n\t\t});\n\t\tdisconnectAll.setDisable(true);\n\t\ttopLine.getChildren().addAll(AssetFactory.loadIcon(\"Connected-Devices.png\"), new Text(\"Connected Devices\"),\n\t\t\t\tdisconnectAll);\n\t\trootItem.getChildren().add(topLine);\n\n\t\trootItem.getChildren().add(accordion);\n\t\t// rootItem = new CheckBoxTreeItem<String>( String.format(\" \"+formatStr,\n\t\t// \"SCRIPTING NAME\",\"DEVICE TYPE\",\"MAC ADDRESS\"),\n\t\t// getIcon(\"images/connection-icon.png\"\n\t\t// // \"images/usb-icon.png\"\n\t\t// ));\n\t\t// rootItem.setExpanded(true);\n\t\t// rootItem.setSelected(true);\n\t\t// rootItem.selectedProperty().addListener(b -> {\n\t\t// if (!rootItem.isSelected()) {\n\t\t// disconnectAll();\n\t\t// }\n\t\t// });\n\n\t\tsetContent(rootItem);\n\n\t\tDeviceManager.addDeviceAddedListener(this);\n\n\t}\n\n\tpublic static void addConnection(BowlerAbstractDevice newDevice, String name) {\n\t\tDeviceManager.addConnection(newDevice, name);\n\n\t}\n\n\t@Override\n\tpublic void handle(ActionEvent event) {\n\t\t// Auto-generated method stub\n\n\t}\n\n\tpublic static ArrayList<PluginManagerWidget> getPlugins() {\n\t\treturn plugins;\n\t}\n\t//\n\t// public BowlerStudioController getBowlerStudioController() {\n\t// return bowlerStudioController;\n\t// }\n\t//\n\t// public void setBowlerStudioController(\n\t// BowlerStudioController bowlerStudioController) {\n\t// this.bowlerStudioController = bowlerStudioController;\n\t// }\n\n\tpublic static BowlerAbstractDevice pickConnectedDevice(@SuppressWarnings(\"rawtypes\") Class class1) {\n\t\tList<String> choices = DeviceManager.listConnectedDevice(class1);\n\n\t\tif (!choices.isEmpty()) {\n\t\t\tChoiceDialog<String> alert = new ChoiceDialog<>(choices.get(0), choices);\n\t\t\talert.setTitle(\"Bowler Device Chooser\");\n\t\t\talert.setHeaderText(\"Choose connected bowler device\");\n\t\t\talert.setContentText(\"Device Name:\");\n\t\t\tNode root = alert.getDialogPane();\n\t\t\tStage stage = (Stage) alert.getDialogPane().getScene().getWindow();\n\t\t\tstage.setOnCloseRequest(ev -> alert.hide());\n\t\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\t\tint tmp = fontNum - 10;\n\t\t\t\tif (tmp < 12)\n\t\t\t\t\ttmp = 12;\n\t\t\t\troot.setStyle(\"-fx-font-size: \" + tmp + \"pt\");\n\t\t\t\talert.getDialogPane().applyCss();\n\t\t\t\talert.getDialogPane().layout();\n\t\t\t\tstage.sizeToScene();\n\t\t\t});\n\t\t\t// Traditional way to get the response value.\n\t\t\tOptional<String> result = alert.showAndWait();\n\t\t\tif (result.isPresent()) {\n\t\t\t\tfor (int i = 0; i < plugins.size(); i++) {\n\t\t\t\t\tif (plugins.get(i).getManager().getName().contains(result.get())) {\n\t\t\t\t\t\treturn plugins.get(i).getManager().getDevice();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tAlert alert = new Alert(AlertType.INFORMATION);\n\t\t\talert.setTitle(\"Device not available\");\n\t\t\talert.setHeaderText(\"Connect a \" + class1.getSimpleName());\n\t\t\talert.setContentText(\"A device of type \" + class1.getSimpleName() + \" is needed\");\n\t\t\talert.initModality(Modality.APPLICATION_MODAL);\n\t\t\talert.show();\n\t\t}\n\t\treturn null;\n\t}\n\n\t// public BowlerAbstractDevice pickConnectedDevice() {\n\t//\n\t// return pickConnectedDevice(null);\n\t// }\n\n\tpublic static void disconnectAll() {\n\n\t\t// extract list int thread safe object\n\t\tObject[] pms = plugins.toArray();\n\t\tfor (int i = 0; i < pms.length; i++) {\n\t\t\tdisconectAndRemoveDevice(((PluginManagerWidget) pms[i]).getManager());\n\t\t\t// ThreadUtil.wait(50);\n\t\t}\n\n\t}\n\n\t// public static OpenCVImageProvider onConnectCVCamera() {\n\t// List<String> choices = new ArrayList<>();\n\t// choices.add(\"0\");\n\t// choices.add(\"1\");\n\t// choices.add(\"2\");\n\t// choices.add(\"3\");\n\t// choices.add(\"4\");\n\t//\n\t// ChoiceDialog<String> dialog = new ChoiceDialog<>(\"0\", choices);\n\t// dialog.setTitle(\"OpenCV Camera Index Chooser\");\n\t// dialog.setHeaderText(\"Choose an OpenCV camera\");\n\t// dialog.setContentText(\"Camera Index:\");\n\t//\n\t// // Traditional way to get the response value.\n\t// Optional<String> result = dialog.showAndWait();\n\t//\n\t// // The Java 8 way to get the response value (with lambda expression).\n\t// if (result !=null) {\n\t// String letter = result.get();\n\t// OpenCVImageProvider p = new OpenCVImageProvider(Integer.parseInt(letter));\n\t// String name = \"camera\"+letter;\n\t// addConnection(p,name);\n\t// return p;\n\t// }\n\t// return null;\n\t//// OpenCVImageProvider p = new OpenCVImageProvider(0);\n\t//// String name = \"camera0\";\n\t//// application.addConnection(p,name);\n\t//\n\t// }\n\t//\n\n\tpublic static void onConnectJavaCVCamera() {\n\t\t// onConnectCVCamera();\n\t\t// List<String> choices = new ArrayList<>();\n\t\t// try {\n\t\t// String[] des = OpenCVFrameGrabber.getDeviceDescriptions();\n\t\t// if(des.length==0)\n\t\t// return;\n\t\t// for (String s: des){\n\t\t// choices.add(s);\n\t\t// }\n\t\t// } catch (org.bytedeco.javacv.FrameGrabber.Exception\n\t\t// |UnsupportedOperationException e1) {\n\t\t// choices.add(\"0\");\n\t\t// choices.add(\"1\");\n\t\t// choices.add(\"2\");\n\t\t// choices.add(\"3\");\n\t\t// choices.add(\"4\");\n\t\t// }\n\t\t//\n\t\t// ChoiceDialog<String> dialog = new ChoiceDialog<>(choices.get(0), choices);\n\t\t// dialog.setTitle(\"JavaCV Camera Index Chooser\");\n\t\t// dialog.setHeaderText(\"Choose an JavaCV camera\");\n\t\t// dialog.setContentText(\"Camera Index:\");\n\t\t//\n\t\t// // Traditional way to get the response value.\n\t\t// Optional<String> result = dialog.showAndWait();\n\t\t//\n\t\t// // The Java 8 way to get the response value (with lambda expression).\n\t\t// result.ifPresent(letter -> {\n\t\t// JavaCVImageProvider p;\n\t\t// try {\n\t\t// p = new JavaCVImageProvider(Integer.parseInt(letter));\n\t\t// String name = \"camera\"+letter;\n\t\t// addConnection(p,name);\n\t\t// } catch (Exception e) {\n\t\t// // Auto-generated catch block\n\t\t// e.printStackTrace();\n\t\t// }\n\t\t//\n\t\t// });\n\t\t//\n\n\t}\n\n\tpublic static void onConnectFileSourceCamera() {\n\t\tBowlerStudio.runLater(() -> {\n\t\t});\n\n\t\tFileChooser fileChooser = new FileChooser();\n\t\tfileChooser.setTitle(\"Open Image File\");\n\t\tFile f = fileChooser.showOpenDialog(BowlerStudioModularFrame.getPrimaryStage());\n\t\tif (f != null) {\n\t\t\tAbstractImageProvider p = new StaticFileProvider(f);\n\t\t\tString name = \"image\";\n\t\t\taddConnection(p, name);\n\t\t}\n\t}\n\n\tpublic static void onConnectURLSourceCamera() {\n\n\t\tTextInputDialog alert = new TextInputDialog(\"http://neuronrobotics.com/img/AndrewHarrington/2014-09-15-86.jpg\");\n\t\talert.setTitle(\"URL Image Source\");\n\t\talert.setHeaderText(\"This url will be loaded each capture.\");\n\t\talert.setContentText(\"URL \");\n\t\tNode root = alert.getDialogPane();\n\t\tStage stage = (Stage) alert.getDialogPane().getScene().getWindow();\n\t\tstage.setOnCloseRequest(ev -> alert.hide());\n\n\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\tint tmp = fontNum - 10;\n\t\t\tif (tmp < 12)\n\t\t\t\ttmp = 12;\n\n\t\t\troot.setStyle(\"-fx-font-size: \" + tmp + \"pt\");\n\t\t\talert.getDialogPane().applyCss();\n\t\t\talert.getDialogPane().layout();\n\t\t\tstage.sizeToScene();\n\t\t});\n\n\t\t// Traditional way to get the response value.\n\t\tOptional<String> result = alert.showAndWait();\n\t\tif (result.isPresent()) {\n\t\t\tURLImageProvider p;\n\t\t\ttry {\n\t\t\t\tp = new URLImageProvider(new URI(result.get()).toURL());\n\t\t\t\tString name = \"url\";\n\t\t\t\taddConnection(p, name);\n\t\t\t} catch (MalformedURLException | URISyntaxException e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t}\n\n\t}\n\n\tpublic static void onMarlinGCODE() {\n\t\tSet<String> ports = NRSerialPort.getAvailableSerialPorts();\n\t\tList<String> choices = new ArrayList<>();\n\t\tif (ports.isEmpty())\n\t\t\treturn;\n\t\tfor (String s : ports) {\n\t\t\tchoices.add(s);\n\t\t}\n\n\t\tChoiceDialog<String> alert = new ChoiceDialog<>(choices.get(0), choices);\n\t\talert.setTitle(\"GCODE Device Serial Port Chooser\");\n\t\talert.setHeaderText(\"Supports Marlin\");\n\t\talert.setContentText(\"GCODE Device Port:\");\n\t\tNode root = alert.getDialogPane();\n\t\tStage stage = (Stage) alert.getDialogPane().getScene().getWindow();\n\t\tstage.setOnCloseRequest(ev -> alert.hide());\n\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\tint tmp = fontNum - 10;\n\t\t\tif (tmp < 12)\n\t\t\t\ttmp = 12;\n\t\t\troot.setStyle(\"-fx-font-size: \" + tmp + \"pt\");\n\t\t\talert.getDialogPane().applyCss();\n\t\t\talert.getDialogPane().layout();\n\t\t\tstage.sizeToScene();\n\t\t});\n\t\t// Traditional way to get the response value.\n\t\tOptional<String> result = alert.showAndWait();\n\n\t\t// The Java 8 way to get the response value (with lambda expression).\n\t\tresult.ifPresent(letter -> {\n\t\t\tGcodeDevice p = new GcodeDevice(new NRSerialPort(letter, 115200));\n\t\t\tp.connect();\n\t\t\tString name = \"GCODE\";\n\t\t\taddConnection(p, name);\n\t\t});\n\n\t}\n\n\tpublic static void onConnectHokuyoURG() {\n\t\tSet<String> ports = NRSerialPort.getAvailableSerialPorts();\n\t\tList<String> choices = new ArrayList<>();\n\t\tif (ports.isEmpty())\n\t\t\treturn;\n\t\tfor (String s : ports) {\n\t\t\tchoices.add(s);\n\t\t}\n\n\t\tChoiceDialog<String> alert = new ChoiceDialog<>(choices.get(0), choices);\n\t\talert.setTitle(\"LIDAR Serial Port Chooser\");\n\t\talert.setHeaderText(\"Supports URG-04LX-UG01\");\n\t\talert.setContentText(\"Lidar Port:\");\n\t\tNode root = alert.getDialogPane();\n\t\tStage stage = (Stage) alert.getDialogPane().getScene().getWindow();\n\t\tstage.setOnCloseRequest(ev -> alert.hide());\n\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\tint tmp = fontNum - 10;\n\t\t\tif (tmp < 12)\n\t\t\t\ttmp = 12;\n\t\t\troot.setStyle(\"-fx-font-size: \" + tmp + \"pt\");\n\t\t\talert.getDialogPane().applyCss();\n\t\t\talert.getDialogPane().layout();\n\t\t\tstage.sizeToScene();\n\t\t});\n\t\t// Traditional way to get the response value.\n\t\tOptional<String> result = alert.showAndWait();\n\n\t\t// The Java 8 way to get the response value (with lambda expression).\n\t\tresult.ifPresent(letter -> {\n\n\t\t});\n\n\t}\n\n\tpublic static void onConnectGamePad() {\n\t\tArrayList<String> ca = BowlerJInputDevice.getControllers();\n\n\t\tList<String> choices = new ArrayList<>();\n\t\tif (ca.size() == 0)\n\t\t\treturn;\n\t\tfor (String s : ca) {\n\t\t\tchoices.add(s);\n\t\t}\n\n\t\tChoiceDialog<String> alert = new ChoiceDialog<>(choices.get(0), choices);\n\t\talert.setTitle(\"JInput Game Controller Select\");\n\t\talert.setHeaderText(\"Connect a game controller\");\n\t\talert.setContentText(\"Controller:\");\n\t\tNode root = alert.getDialogPane();\n\t\tStage stage = (Stage) alert.getDialogPane().getScene().getWindow();\n\t\tstage.setOnCloseRequest(ev -> alert.hide());\n\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\tint tmp = fontNum - 10;\n\t\t\tif (tmp < 12)\n\t\t\t\ttmp = 12;\n\t\t\troot.setStyle(\"-fx-font-size: \" + tmp + \"pt\");\n\t\t\talert.getDialogPane().applyCss();\n\t\t\talert.getDialogPane().layout();\n\t\t\tstage.sizeToScene();\n\t\t});\n\t\t// Traditional way to get the response value.\n\t\tOptional<String> result = alert.showAndWait();\n\n\t\t// The Java 8 way to get the response value (with lambda expression).\n\t\tresult.ifPresent(letter -> {\n\t\t\tfor (String s : BowlerJInputDevice.getControllers()) {\n\t\t\t\tif (letter.contains(s)) {\n\t\t\t\t\tBowlerJInputDevice p = new BowlerJInputDevice(s);\n\t\t\t\t\tp.connect();\n\t\t\t\t\taddConnection(p, p.getName());\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t});\n\n\t}\n\n\t@Override\n\tpublic void onNewDeviceAdded(BowlerAbstractDevice newDevice) {\n\n\t\tPluginManager mp;\n\t\tLog.debug(\"Adding a \" + newDevice.getClass().getName() + \" with name \" + newDevice.getScriptingName());\n\t\tmp = new PluginManager(newDevice);\n\n\t\tBowlerAbstractConnection con = newDevice.getConnection();\n\t\tNode icon = getIcon(\"images/connection-icon.png\"\n\t\t// \"images/usb-icon.png\"\n\t\t);\n\t\tif (SerialConnection.class.isInstance(con)) {\n\t\t\ticon = getIcon(\n\t\t\t\t\t// \"images/ethernet-icon.png\"\n\t\t\t\t\t\"images/usb-icon.png\");\n\t\t} else if (UsbCDCSerialConnection.class.isInstance(con)) {\n\t\t\ticon = getIcon(\n\t\t\t\t\t// \"images/ethernet-icon.png\"\n\t\t\t\t\t\"images/usb-icon.png\");\n\t\t} else if (BluetoothSerialConnection.class.isInstance(con)) {\n\t\t\ticon = getIcon(\n\t\t\t\t\t// \"images/ethernet-icon.png\"\n\t\t\t\t\t\"images/bluetooth-icon.png\");\n\t\t} else if (UDPBowlerConnection.class.isInstance(con) || BowlerTCPClient.class.isInstance(con)) {\n\t\t\ticon = getIcon(\n\t\t\t\t\t// \"images/ethernet-icon.png\"\n\t\t\t\t\t\"images/ethernet-icon.png\");\n\t\t}\n\t\tString line = String.format(formatStr, newDevice.getScriptingName(), newDevice.getClass().getSimpleName(),\n\t\t\t\tnewDevice.getAddress());\n\t\tPluginManagerWidget e = new PluginManagerWidget(mp, icon);\n\t\tplugins.add(e);\n\t\tBowlerStudio.runLater(() -> accordion.getPanes().add(e));\n\t\tBowlerStudio.runLater(() -> disconnectAll.setDisable(false));\n\n\t\tmp.setName(newDevice.getScriptingName());\n\t\t// DeviceManager.addDeviceAddedListener(new IDeviceAddedListener() {\n\t\t//\n\t\t// @Override\n\t\t// public void onNewDeviceAdded(BowlerAbstractDevice bad) {\n\t\t// // Auto-generated method stub\n\t\t//\n\t\t// }\n\t\t//\n\t\t// @Override\n\t\t// public void onDeviceRemoved(BowlerAbstractDevice bad) {\n\t\t// for (int i = 0; i < plugins.size(); i++) {\n\t\t// PluginManager p = plugins.get(i).getManager();\n\t\t// if (p.getDevice() == bad) {\n\t\t// DeviceManager.remove(p.getDevice());\n\t\t// return;\n\t\t// }\n\t\t// }\n\t\t// }\n\t\t// });\n\t\t// newDevice.addConnectionEventListener(\n\t\t// new IDeviceConnectionEventListener() {\n\t\t// @Override\n\t\t// public void onDisconnect(BowlerAbstractDevice source) {\n\t\t// // clean up after yourself...\n\t\t// //disconectAndRemoveDevice(mp);\n\t\t//\n\t\t// for(int i=0;i<plugins.size();i++){\n\t\t// PluginManager p=plugins.get(i).getManager();\n\t\t// if(p.getDevice()==source){\n\t\t// DeviceManager.remove(p.getDevice());\n\t\t// return;\n\t\t// }\n\t\t// }\n\t\t//\n\t\t// }\n\t\t//\n\t\t// // ignore\n\t\t// @Override\n\t\t// public void onConnect(BowlerAbstractDevice source) {\n\t\t// }\n\t\t// });\n\t\tif (getBowlerStudioController() != null)\n\t\t\tBowlerStudioModularFrame.getBowlerStudioModularFrame().setSelectedTab(this);\n\t}\n\n\tprivate BowlerStudioController getBowlerStudioController() {\n\t\t// Auto-generated method stub\n\t\treturn BowlerStudioController.getBowlerStudio();\n\t}\n\n\tprivate static void disconectAndRemoveDevice(PluginManager mp) {\n\t\tcom.neuronrobotics.sdk.common.Log.error(\"CM Disconnecting \" + mp.getName());\n\t\tLog.warning(\"Disconnecting \" + mp.getName());\n\t\tif (mp.getDevice().isAvailable() || NonBowlerDevice.class.isInstance(mp)) {\n\t\t\ttry {\n\t\t\t\tmp.getDevice().disconnect();\n\t\t\t} catch (Throwable t) {\n\t\t\t\tBowlerStudio.printStackTrace(t);\n\t\t\t}\n\n\t\t}\n\t\tDeviceManager.remove(mp.getDevice());\n\t}\n\n\t@Override\n\tpublic void onDeviceRemoved(BowlerAbstractDevice bad) {\n\t\tLog.warning(\"Removing Device \" + bad.getScriptingName());\n\t\t// new RuntimeException().printStackTrace();\n\t\tfor (int i = 0; i < plugins.size(); i++) {\n\t\t\tPluginManager p = plugins.get(i).getManager();\n\t\t\tif (p.getDevice() == bad) {\n\t\t\t\tLog.warning(\"Found Device \" + bad.getScriptingName());\n\t\t\t\t// new RuntimeException().printStackTrace();\n\t\t\t\tPluginManagerWidget torem = plugins.remove(i);\n\t\t\t\tBowlerStudio.runLater(() -> accordion.getPanes().remove(torem));\n\t\t\t\tif (plugins.isEmpty()) {\n\t\t\t\t\tBowlerStudio.runLater(() -> disconnectAll.setDisable(true));\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static void addConnection() {\n\t\tStage s = new Stage();\n\t\tnew Thread() {\n\t\t\tpublic void run() {\n\t\t\t\tBowlerDatagram.setUseBowlerV4(true);\n\t\t\t\tBowlerConnectionMenu controller = new BowlerConnectionMenu();\n\t\t\t\ttry {\n\t\t\t\t\tcontroller.start(s);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\n\t\t\t}\n\t\t}.start();\n\t\t// DeviceManager.addConnection();\n\t}\n\n\tpublic static ConnectionManager getConnectionManager() {\n\t\treturn connectionManager;\n\t}\n\n\tpublic static void onFirmata() {\n\t\tSet<String> ports = NRSerialPort.getAvailableSerialPorts();\n\t\tList<String> choices = new ArrayList<>();\n\t\tif (ports.isEmpty())\n\t\t\treturn;\n\t\tfor (String s : ports) {\n\t\t\tchoices.add(s);\n\t\t}\n\n\t\tChoiceDialog<String> alert = new ChoiceDialog<>(choices.get(0), choices);\n\t\talert.setTitle(\"Firmata Device Serial Port Chooser\");\n\t\talert.setHeaderText(\"Supports Firmata\");\n\t\talert.setContentText(\"Firmata Device Port:\");\n\t\tNode root = alert.getDialogPane();\n\t\tStage stage = (Stage) alert.getDialogPane().getScene().getWindow();\n\t\tstage.setOnCloseRequest(ev -> alert.hide());\n\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\tint tmp = fontNum - 10;\n\t\t\tif (tmp < 12)\n\t\t\t\ttmp = 12;\n\t\t\troot.setStyle(\"-fx-font-size: \" + tmp + \"pt\");\n\t\t\talert.getDialogPane().applyCss();\n\t\t\talert.getDialogPane().layout();\n\t\t\tstage.sizeToScene();\n\t\t});\n\t\t// Traditional way to get the response value.\n\t\tOptional<String> result = alert.showAndWait();\n\n\t\t// The Java 8 way to get the response value (with lambda expression).\n\t\tresult.ifPresent(letter -> {\n\t\t\tnew Thread(() -> {\n\t\t\t\tSystem.out.print(\"\\nConnecting Firmata...\");\n\t\t\t\tFirmataBowler p = new FirmataBowler(letter);\n\t\t\t\tp.connect();\n\t\t\t\tString name = \"firmata\";\n\t\t\t\taddConnection(p, name);\n\t\t\t\tSystem.out.print(\"Done!\\n\");\n\t\t\t}).start();\n\t\t});\n\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/CreatureLab3dController.java",
    "content": "package com.neuronrobotics.bowlerstudio;\n\n/**\n * Sample Skeleton for \"CreatureLab.fxml\" Controller Class\n * You can copy and paste this code into your favorite IDE\n **/\n\nimport java.net.URL;\nimport java.util.ResourceBundle;\n\nimport com.neuronrobotics.bowlerstudio.assets.AssetFactory;\nimport com.neuronrobotics.bowlerstudio.threed.BowlerStudio3dEngine;\n\nimport javafx.beans.value.ObservableValue;\nimport javafx.event.EventHandler;\nimport javafx.fxml.FXML;\nimport javafx.geometry.Bounds;\nimport javafx.scene.Group;\nimport javafx.scene.Node;\nimport javafx.scene.control.ScrollPane;\nimport javafx.scene.input.KeyEvent;\nimport javafx.scene.layout.AnchorPane;\n\npublic class CreatureLab3dController {\n\n\t@FXML // ResourceBundle that was given to the FXMLLoader\n\tprivate ResourceBundle resources;\n\n\t@FXML // URL location of the FXML file that was given to the FXMLLoader\n\tprivate URL location;\n\n\t@FXML // fx:id=\"CadControlsAnchor\"\n\tprivate AnchorPane CadControlsAnchor; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"DriveControlsAnchor\"\n\tprivate AnchorPane DriveControlsAnchor; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"TempControlsAnchor\"\n\tprivate AnchorPane TempControlsAnchor; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"jfx3dControls\"\n\tprivate AnchorPane jfx3dControls; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"overlayScrollPanel\"\n\tprivate ScrollPane overlayScrollPanel; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"viewContainer\"\n\tprivate AnchorPane viewContainer; // Value injected by FXMLLoader\n\t// private SubScene subScene;\n\tprotected EventHandler<? super KeyEvent> normalKeyPessHandle = null;\n\n\tprivate static BowlerStudio3dEngine engine;\n\n\tpublic CreatureLab3dController() {\n\n\t\tsetEngine(new BowlerStudio3dEngine(\"BowlerStudio3d\"));\n\t}\n\n\tpublic void setOverlayLeft(Node content) {\n\t\tBowlerStudio.runLater(() -> {\n\n\t\t\toverlayScrollPanel.setFitToHeight(true);\n\t\t\toverlayScrollPanel.setContent(content);\n\t\t\tcontent.setOpacity(1);\n\t\t\toverlayScrollPanel.viewportBoundsProperty()\n\t\t\t\t\t.addListener((ObservableValue<? extends Bounds> arg0, Bounds arg1, Bounds arg2) -> {\n\t\t\t\t\t\t// Node content = overlayScrollPanel.getContent();\n\t\t\t\t\t\t//\n\t\t\t\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"Resizing \" + arg2);\n\t\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\t\toverlayScrollPanel.setFitToHeight(true);\n\t\t\t\t\t\t\t/// content.seth\n\t\t\t\t\t\t\toverlayScrollPanel.setContent(content);\n\n\t\t\t\t\t\t});\n\t\t\t\t\t});\n\t\t\toverlayScrollPanel.setVisible(true);\n\t\t});\n\t}\n\n\tpublic void clearOverlayLeft() {\n\t\tBowlerStudio.runLater(() -> {\n\t\t\toverlayScrollPanel.setContent(null);\n\t\t\toverlayScrollPanel.setVisible(false);\n\t\t});\n\t}\n\n\tpublic void setOverlayTop(Group content) {\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tCadControlsAnchor.getChildren().clear();\n\t\t\tCadControlsAnchor.getChildren().add(content);\n\n\t\t\tAnchorPane.setTopAnchor(content, 0.0);\n\t\t\tAnchorPane.setRightAnchor(content, 0.0);\n\t\t\tAnchorPane.setLeftAnchor(content, 0.0);\n\t\t\tAnchorPane.setBottomAnchor(content, 0.0);\n\t\t\tCadControlsAnchor.setVisible(true);\n\t\t});\n\t}\n\n\tpublic void clearOverlayTop() {\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tCadControlsAnchor.getChildren().clear();\n\t\t\tCadControlsAnchor.setVisible(false);\n\t\t});\n\t}\n\n\tpublic void setOverlayTopRight(Group content) {\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tDriveControlsAnchor.getChildren().clear();\n\t\t\tDriveControlsAnchor.getChildren().add(content);\n\t\t\tAnchorPane.setTopAnchor(content, 0.0);\n\t\t\tAnchorPane.setRightAnchor(content, 0.0);\n\t\t\tAnchorPane.setLeftAnchor(content, 0.0);\n\t\t\tAnchorPane.setBottomAnchor(content, 0.0);\n\t\t\tDriveControlsAnchor.setVisible(true);\n\t\t});\n\t}\n\n\tpublic void clearOverlayTopRight() {\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tDriveControlsAnchor.getChildren().clear();\n\t\t\tDriveControlsAnchor.setVisible(false);\n\t\t});\n\t}\n\n\tpublic void setOverlayBottomRight(Group content) {\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tTempControlsAnchor.getChildren().clear();\n\t\t\tTempControlsAnchor.getChildren().add(content);\n\t\t\tAnchorPane.setTopAnchor(content, 0.0);\n\t\t\tAnchorPane.setRightAnchor(content, 0.0);\n\t\t\tAnchorPane.setLeftAnchor(content, 0.0);\n\t\t\tAnchorPane.setBottomAnchor(content, 0.0);\n\t\t\tTempControlsAnchor.setVisible(true);\n\t\t});\n\t}\n\n\tpublic void clearOverlayBottomRight() {\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tTempControlsAnchor.getChildren().clear();\n\t\t\tTempControlsAnchor.setVisible(false);\n\t\t});\n\t}\n\n\t@FXML // This method is called by the FXMLLoader when initialization is complete\n\tvoid initialize() {\n\t\tassert CadControlsAnchor != null\n\t\t\t\t: \"fx:id=\\\"CadControlsAnchor\\\" was not injected: check your FXML file 'CreatureLab.fxml'.\";\n\t\tassert DriveControlsAnchor != null\n\t\t\t\t: \"fx:id=\\\"DriveControlsAnchor\\\" was not injected: check your FXML file 'CreatureLab.fxml'.\";\n\t\tassert TempControlsAnchor != null\n\t\t\t\t: \"fx:id=\\\"TempControlsAnchor\\\" was not injected: check your FXML file 'CreatureLab.fxml'.\";\n\t\tassert jfx3dControls != null\n\t\t\t\t: \"fx:id=\\\"jfx3dControls\\\" was not injected: check your FXML file 'CreatureLab.fxml'.\";\n\t\tassert overlayScrollPanel != null\n\t\t\t\t: \"fx:id=\\\"overlayScrollPanel\\\" was not injected: check your FXML file 'CreatureLab.fxml'.\";\n\t\tassert viewContainer != null\n\t\t\t\t: \"fx:id=\\\"viewContainer\\\" was not injected: check your FXML file 'CreatureLab.fxml'.\";\n\t\tclearOverlayLeft();\n\t\t// Initialize your logic here: all @FXML variables will have been injected\n\t\tsetupUi();\n\n\t}\n\n\tprivate void setupUi() {\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tgetEngine().setFocusTraversable(false);\n\t\t\tgetEngine().bind(viewContainer);\n\t\t});\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tGroup controlsBox = getEngine().getControlsBox(AssetFactory.loadIcon(\"Home-Camera.png\"),\n\t\t\t\t\tAssetFactory.loadIcon(\"Generate-Cad.png\"), AssetFactory.loadIcon(\"Clear-Screen.png\"));\n\t\t\tjfx3dControls.getChildren().add(controlsBox);\n\t\t\tgetEngine().addTo(viewContainer);\n\t\t\tgetEngine().handleMouse(viewContainer);\n\t\t});\n\t}\n\n\tpublic static BowlerStudio3dEngine getEngine() {\n\t\treturn engine;\n\t}\n\n\tpublic static void setEngine(BowlerStudio3dEngine engine) {\n\t\tCreatureLab3dController.engine = engine;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/DeviceSupportPluginMap.java",
    "content": "package com.neuronrobotics.bowlerstudio;\n\nimport com.neuronrobotics.bowlerstudio.tabs.AbstractBowlerStudioTab;\nimport com.neuronrobotics.sdk.common.BowlerAbstractDevice;\nimport com.neuronrobotics.sdk.namespace.bcs.pid.IPidControlNamespace;\n\npublic class DeviceSupportPluginMap implements PluginFactory {\n\n\tprivate Class<?> device;\n\tprivate Class<?> plugin;\n\tprivate PluginFactory factory = null;\n\n\tDeviceSupportPluginMap(Class<?> device, Class<?> plugin) {\n\t\tthis.setDevice(device);\n\t\tthis.setPlugin(plugin);\n\n\t}\n\n\tpublic DeviceSupportPluginMap(Class<?> device, Class<?> plugin, PluginFactory factory) {\n\t\tthis.factory = factory;\n\t\tthis.setDevice(device);\n\t\tthis.setPlugin(plugin);\n\n\t}\n\n\tpublic boolean isFactoryProvided() {\n\t\treturn factory != null;\n\t}\n\n\tpublic Class<?> getDevice() {\n\t\treturn device;\n\t}\n\n\tprivate void setDevice(Class<?> device) {\n\t\tif (BowlerAbstractDevice.class.isAssignableFrom(device) || IPidControlNamespace.class.isAssignableFrom(device))\n\t\t\tthis.device = device;\n\t\telse\n\t\t\tthrow new RuntimeException(\"Devices must subclass BowlerAbstractDevice or NonBowlerDevice\");\n\t}\n\n\tpublic Class<?> getPlugin() {\n\t\treturn plugin;\n\t}\n\n\tprivate void setPlugin(Class<?> plugin) {\n\t\tif (AbstractBowlerStudioTab.class.isAssignableFrom(plugin))\n\t\t\tthis.plugin = plugin;\n\t\telse\n\t\t\tthrow new RuntimeException(\"Plugins must subclass AbstractBowlerStudioTab\");\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"Device: \" + device.getCanonicalName() + \" Plugin: \" + plugin.getCanonicalName();\n\t}\n\n\t@Override\n\tpublic AbstractBowlerStudioTab generateNewPlugin()\n\t\t\tthrows ClassNotFoundException, InstantiationException, IllegalAccessException {\n\t\tif (factory != null)\n\t\t\treturn factory.generateNewPlugin();\n\n\t\t// This is where the new tab allocation is called\n\t\treturn (AbstractBowlerStudioTab) Class.forName(plugin.getName()).cast(plugin.newInstance());\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/GistHelper.java",
    "content": "package com.neuronrobotics.bowlerstudio;\n\nimport com.neuronrobotics.bowlerstudio.scripting.PasswordManager;\nimport com.neuronrobotics.bowlerstudio.scripting.ScriptingEngine;\nimport com.neuronrobotics.sdk.util.ThreadUtil;\nimport org.eclipse.jgit.api.errors.GitAPIException;\n//import org.jfree.util.Log;\nimport org.kohsuke.github.GHGist;\nimport org.kohsuke.github.GHGistBuilder;\nimport org.kohsuke.github.GitHub;\n\nimport java.io.IOException;\n\n/**\n * Created by Ryan Benasutti on 2/5/2016.\n */\n\npublic class GistHelper {\n\tprivate GistHelper() {\n\t}\n\n\tpublic static String createNewGist(String filename, String description, boolean isPublic) {\n\t\t// TODO: Perhaps this method should throw GitAPIException and IOException\n\t\t// Setup gist\n\t\tString defaultContents = \"\";// ;\n\t\tGitHub gitHub = PasswordManager.getGithub();\n\t\tGHGistBuilder builder = gitHub.createGist();\n\t\tbuilder.file(filename, defaultContents);\n\t\tbuilder.description(description);\n\t\tbuilder.public_(isPublic);\n\n\t\t// Make gist\n\t\treturn createGistFromBuilder(builder, filename);\n\t}\n\n\tpublic static String addFileToGist(String filename, String content, GHGist gistID) {\n\t\tGitHub gitHub = PasswordManager.getGithub();\n\t\ttry {\n\t\t\t// Copy from old gist\n\t\t\tGHGist oldGist = gistID;\n\t\t\tGHGistBuilder builder = gitHub.createGist();\n\n\t\t\tbuilder.description(oldGist.getDescription());\n\t\t\tbuilder.public_(oldGist.isPublic());\n\n\t\t\tfor (String key : oldGist.getFiles().keySet())\n\t\t\t\tbuilder.file(key, oldGist.getFiles().get(key).getContent());\n\n\t\t\t// Add new file\n\t\t\tbuilder.file(filename, content);\n\n\t\t\t// Make new gist with old filename\n\t\t\treturn createGistFromBuilder(builder, oldGist.getFiles().values().iterator().next().getFileName());\n\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate static String createGistFromBuilder(GHGistBuilder builder, String filename) {\n\t\tGHGist gist;\n\t\ttry {\n\t\t\tgist = builder.create();\n\t\t\t// String gistID = ScriptingEngine.urlToGist(gist.getHtmlUrl());\n\n\t\t\t// BowlerStudio.openUrlInNewTab(new URL(gist.getHtmlUrl()));\n\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Creating repo\");\n\t\t\twhile (true) {\n\t\t\t\ttry {\n\t\t\t\t\tScriptingEngine.fileFromGit(gist.getGitPullUrl(), filename);\n\t\t\t\t\tbreak;\n\t\t\t\t} catch (GitAPIException e) {\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\n\n\t\t\t\tThreadUtil.wait(500);\n\t\t\t\t// Log.warn(filename + \" not built yet\");\n\t\t\t}\n\n\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Creating gist at \" + filename);\n\t\t\tScriptingEngine.getLangaugeByExtension(filename).getDefaultContents(gist.getGitPullUrl(), filename);\n\t\t\treturn gist.getGitPullUrl();\n\t\t} catch (IOException e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t\treturn null;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/INewVitaminCallback.java",
    "content": "package com.neuronrobotics.bowlerstudio;\n\nimport javafx.scene.control.Menu;\n\npublic interface INewVitaminCallback {\n\tvoid addVitaminType(String s);\n\n\tMenu getTypeMenu(String type);\n\n\tvoid addSizesToMenu(String size, String type);\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/Main.java",
    "content": "package com.neuronrobotics.bowlerstudio;\n\npublic class Main {\n\n\tpublic static void main(String[] args) throws Exception {\n\t\tBowlerStudio.main(args);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/MakeReleaseController.java",
    "content": "package com.neuronrobotics.bowlerstudio;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.URL;\nimport java.util.ResourceBundle;\nimport java.util.function.UnaryOperator;\n\n//import org.kohsuke.github.GHWorkflow;\n\nimport javafx.event.ActionEvent;\nimport javafx.fxml.FXML;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.ListView;\nimport javafx.scene.control.TextField;\n\nimport com.neuronrobotics.bowlerstudio.assets.AssetFactory;\nimport com.neuronrobotics.bowlerstudio.assets.FontSizeManager;\nimport com.neuronrobotics.bowlerstudio.scripting.ScriptingEngine;\nimport com.neuronrobotics.sdk.util.ThreadUtil;\n\nimport javafx.application.Application;\nimport javafx.fxml.FXMLLoader;\nimport javafx.scene.Node;\nimport javafx.scene.Parent;\nimport javafx.scene.Scene;\nimport javafx.scene.control.TextFormatter.Change;\nimport javafx.stage.Modality;\nimport javafx.stage.Stage;\n\nimport java.util.List;\nimport javafx.scene.control.*;\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.control.*;\nimport javafx.scene.layout.*;\nimport javafx.event.ActionEvent;\nimport javafx.collections.*;\nimport javafx.stage.Stage;\n\n@SuppressWarnings(\"restriction\")\npublic class MakeReleaseController extends Application {\n\n\tprivate String gitRepo;\n\n\t@FXML // ResourceBundle that was given to the FXMLLoader\n\tprivate ResourceBundle resources;\n\n\t@FXML // URL location of the FXML file that was given to the FXMLLoader\n\tprivate URL location;\n\n\t@FXML // fx:id=\"bugfix\"\n\tprivate TextField bugfix; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"listOfTags\"\n\tprivate ListView<String> listOfTags; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"major\"\n\tprivate TextField major; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"minor\"\n\tprivate TextField minor; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"releaseButton\"\n\tprivate Button releaseButton; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"tagName\"\n\tprivate Label tagName; // Value injected by FXMLLoader\n\tprivate List<String> tags;\n\tStage primaryStage;\n\n\t@FXML\n\tvoid makeRelease(ActionEvent event) {\n\t\tString newTag = getNewTag();\n\t\tnew Thread(() -> {\n\n\t\t\tFile dir = ScriptingEngine.getRepositoryCloneDirectory(gitRepo);\n\t\t\tFile workflows = new File(dir.getAbsolutePath() + delim() + \".github\" + delim() + \"workflows\");\n\t\t\tboolean hasWorkflow = false;\n\n\t\t\tif (workflows.exists())\n\t\t\t\thasWorkflow = true;\n\n\t\t\tObject st[];\n\t\t\ttry {\n\t\t\t\tst = ScriptingEngine.filesInGit(gitRepo).toArray();\n\t\t\t\tif (!hasWorkflow) {\n\t\t\t\t\tCreatenewWorkflow(event, newTag, st);\n\t\t\t\t} else {\n\t\t\t\t\tScriptingEngine.tagRepo(gitRepo, newTag);\n\t\t\t\t}\n\n\t\t\t} catch (Exception e) {\n\t\t\t\t// Auto-generated catch block\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t}).start();\n\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tprimaryStage.close();\n\t\t});\n\t}\n\n\tprivate void CreatenewWorkflow(ActionEvent event, String newTag, Object[] st) {\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tChoiceDialog alert = new ChoiceDialog(st[0], st);\n\t\t\talert.setTitle(\"Choose File From this Repo to release\");\n\t\t\talert.setHeaderText(\"Select file to compile in CI\");\n\t\t\talert.setContentText(\"File:\");\n\t\t\tNode root = alert.getDialogPane();\n\t\t\tStage stage = (Stage) alert.getDialogPane().getScene().getWindow();\n\t\t\tstage.setOnCloseRequest(ev -> alert.hide());\n\t\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\t\tint tmp = fontNum - 10;\n\t\t\t\tif (tmp < 12)\n\t\t\t\t\ttmp = 12;\n\t\t\t\troot.setStyle(\"-fx-font-size: \" + tmp + \"pt\");\n\t\t\t\talert.getDialogPane().applyCss();\n\t\t\t\talert.getDialogPane().layout();\n\t\t\t\tstage.sizeToScene();\n\t\t\t});\n\t\t\t// show the dialog\n\t\t\talert.showAndWait();\n\t\t\tString selectedItem = (String) alert.getSelectedItem();\n\n\t\t\tnew Thread(() -> {\n\t\t\t\tString filename = selectedItem.split(\"\\\\.\")[0];\n\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(selectedItem + \" selected\");\n\t\t\t\tString fileContents;\n\t\t\t\ttry {\n\t\t\t\t\tfileContents = ScriptingEngine.codeFromGit(\n\t\t\t\t\t\t\t\"https://github.com/CommonWealthRobotics/Bowler-Script-Release-CI.git\", \"TEMPLATE.job\")[0];\n\t\t\t\t\tfileContents = fileContents.replaceAll(\"FILENAME_REPLACE\", selectedItem);\n\t\t\t\t\tfileContents = fileContents.replaceAll(\"JOBNAME_REPLACE\", filename);\n\t\t\t\t\ttry {\n\t\t\t\t\t\tcreateWorkflow(event, fileContents);\n\t\t\t\t\t\tThreadUtil.wait(1000);\n\t\t\t\t\t\tScriptingEngine.tagRepo(gitRepo, newTag);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\t\te.printStackTrace();\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e1) {\n\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\te1.printStackTrace();\n\t\t\t\t}\n\n\t\t\t}).start();\n\t\t});\n\t}\n\n\tprivate void createWorkflow(ActionEvent event, String fileContents) throws Exception, IOException {\n\n\t\tScriptingEngine.pushCodeToGit(gitRepo, null, \".github/workflows/bowler.yml\", fileContents, \"Creating workflow\");\n\t\t// GHWorkflow wf = getWorkflow(gitRepo);\n\t\tmakeRelease(event);\n\t}\n\n\t// private GHWorkflow getWorkflow(String repoURL) throws IOException {\n\t// File repoDir = ScriptingEngine.getRepositoryCloneDirectory(repoURL);\n\t// String Project = repoDir.getParentFile().getName();\n\t// String Repo = repoDir.getName();\n\t// GHRepository repo = PasswordManager.getGithub().getRepository(Project + \"/\" +\n\t// Repo);\n\t// GHWorkflow workflow = repo.getWorkflow(\"bowler.yml\");\n\t// if (!workflow.getState().equals(\"active\")) {\n\t// workflow.enable();\n\t// }\n\t// return workflow;\n\t// }\n\n\tprivate String delim() {\n\t\treturn System.getProperty(\"file.separator\");\n\t}\n\n\t@FXML // This method is called by the FXMLLoader when initialization is complete\n\tvoid initialize() {\n\t\tassert bugfix != null : \"fx:id=\\\"bugfix\\\" was not injected: check your FXML file 'release.fxml'.\";\n\t\tassert listOfTags != null : \"fx:id=\\\"listOfTags\\\" was not injected: check your FXML file 'release.fxml'.\";\n\t\tassert major != null : \"fx:id=\\\"major\\\" was not injected: check your FXML file 'release.fxml'.\";\n\t\tassert minor != null : \"fx:id=\\\"minor\\\" was not injected: check your FXML file 'release.fxml'.\";\n\t\tassert releaseButton != null : \"fx:id=\\\"releaseButton\\\" was not injected: check your FXML file 'release.fxml'.\";\n\t\tassert tagName != null : \"fx:id=\\\"tagName\\\" was not injected: check your FXML file 'release.fxml'.\";\n\t\tUnaryOperator<Change> filter = change -> {\n\t\t\tString text = change.getText();\n\t\t\tif (text.matches(\"[0-9]*\")) {\n\t\t\t\treturn change;\n\t\t\t}\n\t\t\treturn null;\n\t\t};\n\t\tmajor.setTextFormatter(new TextFormatter<String>(filter));\n\t\tminor.setTextFormatter(new TextFormatter<String>(filter));\n\t\tbugfix.setTextFormatter(new TextFormatter<String>(filter));\n\t\tmajor.textProperty().addListener((obs, old, niu) -> {\n\t\t\tcheck();\n\t\t});\n\t\tminor.textProperty().addListener((obs, old, niu) -> {\n\t\t\tcheck();\n\t\t});\n\t\tbugfix.textProperty().addListener((obs, old, niu) -> {\n\t\t\tcheck();\n\t\t});\n\t\treleaseButton.setDisable(true);\n\n\t}\n\n\tprivate void check() {\n\t\tif (major.getText().length() == 0 || minor.getText().length() == 0 || bugfix.getText().length() == 0) {\n\t\t\treturn;\n\t\t}\n\t\tString newTag = getNewTag();\n\t\tfor (String s : tags) {\n\t\t\tif (s.contains(newTag)) {\n\t\t\t\ttagName.setText(\"error: tag exists\");\n\t\t\t\tfor (String Item : listOfTags.getItems()) {\n\t\t\t\t\tif (Item.contentEquals(s)) {\n\t\t\t\t\t\tlistOfTags.getSelectionModel().select(s);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\ttagName.setText(newTag);\n\t\treleaseButton.setDisable(false);\n\t}\n\n\tprivate String getNewTag() {\n\t\treturn major.getText() + \".\" + minor.getText() + \".\" + bugfix.getText();\n\t}\n\n\tpublic MakeReleaseController(String gitRepo) {\n\t\tthis.gitRepo = gitRepo;\n\n\t}\n\n\t@SuppressWarnings(\"restriction\")\n\t@Override\n\tpublic void start(Stage st) throws Exception {\n\t\tprimaryStage = st;\n\t\tFXMLLoader loader = AssetFactory.loadLayout(\"layout/release.fxml\", true);\n\t\tParent root;\n\t\tloader.setController(this);\n\t\t// This is needed when loading on MAC\n\t\tloader.setClassLoader(getClass().getClassLoader());\n\t\troot = loader.load();\n\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\tint tmp = fontNum - 10;\n\t\t\tif (tmp < 12)\n\t\t\t\ttmp = 12;\n\t\t\troot.setStyle(\"-fx-font-size: \" + tmp + \"pt\");\n\t\t});\n\t\ttags = ScriptingEngine.getAllTags(gitRepo);\n\t\tfor (String s : tags) {\n\t\t\tlistOfTags.getItems().add(s);\n\t\t}\n\t\tif (tags.size() > 0) {\n\t\t\tString topValue = tags.get(0);\n\t\t\tString[] top = topValue.split(\"\\\\.\");\n\t\t\tString majorStart = top[0];\n\t\t\tString minorStart = top[1];\n\t\t\tString bugStart = \"\" + (Integer.parseInt(top[2].split(\"-\")[0]) + 1);\n\t\t\tmajor.setText(majorStart);\n\t\t\tminor.setText(minorStart);\n\t\t\tbugfix.setText(bugStart);\n\t\t}\n\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tprimaryStage.setTitle(\"Release for \" + gitRepo);\n\n\t\t\tScene scene = new Scene(root);\n\t\t\tprimaryStage.setScene(scene);\n\t\t\tprimaryStage.initModality(Modality.WINDOW_MODAL);\n\t\t\tprimaryStage.setResizable(true);\n\t\t\tcheck();\n\t\t\tprimaryStage.show();\n\t\t});\n\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/MeasurmentConfig.java",
    "content": "package com.neuronrobotics.bowlerstudio;\n\nimport com.neuronrobotics.bowlerstudio.vitamins.Vitamins;\n\npublic class MeasurmentConfig {\n\tprivate String key;\n\tprivate String type;\n\tprivate String id;\n\n\tpublic MeasurmentConfig(String key, String type, String id) {\n\t\tthis.type = type;\n\t\tthis.id = id;\n\t\tthis.setKey(key);\n\t\tcom.neuronrobotics.sdk.common.Log.error(\"Adding Measurment \" + key + \" \" + getMeasurment());\n\t\tgetMeasurment();\n\t}\n\n\tpublic String getKey() {\n\t\treturn key;\n\t}\n\n\tpublic void setKey(String key) {\n\t\tthis.key = key;\n\t}\n\n\tpublic String getMeasurment() {\n\t\t// if(configs.get(key)==null)\n\t\t// configs.put(key, \"\");\n\n\t\ttry {\n\t\t\treturn Vitamins.getMeasurement(type, id, key).toString();\n\t\t} catch (Exception ex) {\n\t\t\tSystem.out.print(\"\\n\\tGetting measurement of \" + key);\n\t\t\tex.printStackTrace(System.out);\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\tpublic void setMeasurment(String measurment) {\n\t\tcom.neuronrobotics.sdk.common.Log.error(\"Setting field \" + type + \", \" + id + \", \" + key + \" to \" + measurment);\n\t\tVitamins.putMeasurment(type, id, key, measurment);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/MenuRefreshEvent.java",
    "content": "package com.neuronrobotics.bowlerstudio;\n\npublic interface MenuRefreshEvent {\n\tvoid setToLoggedIn();\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/MenuResettingEventHandler.java",
    "content": "package com.neuronrobotics.bowlerstudio;\n\nimport javafx.event.Event;\nimport javafx.event.EventHandler;\n\npublic abstract class MenuResettingEventHandler implements EventHandler<Event> {\n\tprivate Runnable menuReset = null;\n\n\tpublic Runnable getMenuReset() {\n\t\tif (menuReset == null)\n\t\t\tmenuReset = new Runnable() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\t// Auto-generated method stub\n\n\t\t\t\t}\n\t\t\t};\n\t\treturn menuReset;\n\t}\n\n\tpublic void setMenuReset(Runnable menuReset) {\n\t\tthis.menuReset = menuReset;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/NameGetter.java",
    "content": "package com.neuronrobotics.bowlerstudio;\n\nimport java.util.function.Supplier;\n\nimport javax.swing.JFrame;\nimport javax.swing.JOptionPane;\n\npublic class NameGetter implements Supplier<String> {\n\n\t@Override\n\tpublic String get() {\n\t\tString sec = \"d842796170cb0b155f8508ab00822b72e1dfa9bf\";\n\t\tif (sec.contains(\"REPLACE\")) {\n\t\t\tString line = System.getProperty(\"API-SECRET\");\n\t\t\tif (line != null)\n\t\t\t\treturn line;\n\t\t\tJFrame jframe = new JFrame();\n\t\t\tString answer = JOptionPane.showInputDialog(jframe, \"Enter API secret\");\n\t\t\tjframe.dispose();\n\t\t\treturn answer;\n\t\t}\n\t\treturn sec;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/NewCreatureWizard.java",
    "content": "package com.neuronrobotics.bowlerstudio;\n\nimport com.neuronrobotics.bowlerstudio.scripting.IScriptingLanguage;\nimport com.neuronrobotics.bowlerstudio.scripting.RobotHelper;\n\nimport javafx.stage.Stage;\n\npublic class NewCreatureWizard {\n\n\tpublic static void run() {\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tStage s = new Stage();\n\t\t\tnew Thread(() -> {\n\t\t\t\tThread.setDefaultUncaughtExceptionHandler(new IssueReportingExceptionHandler());\n\t\t\t\tAddFileToGistController controller = new AddFileToGistController(null, BowlerStudioMenu.getSelfRef());\n\t\t\t\ttry {\n\t\t\t\t\tcontroller.start(s, (IScriptingLanguage) new RobotHelper());\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\n\n\t\t\t}).start();\n\t\t});\n\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/NewVitaminWizardController.java",
    "content": "package com.neuronrobotics.bowlerstudio;\n/**\n * Sample Skeleton for 'newVitaminWizard.fxml' Controller Class\n */\n\nimport java.net.URISyntaxException;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.ResourceBundle;\nimport java.util.concurrent.CopyOnWriteArrayList;\nimport java.util.stream.Collectors;\n\nimport org.kohsuke.github.GHIssueState;\nimport org.kohsuke.github.GHPullRequest;\nimport org.kohsuke.github.GHRepository;\nimport org.kohsuke.github.GitHub;\n\nimport com.neuronrobotics.bowlerstudio.assets.AssetFactory;\nimport com.neuronrobotics.bowlerstudio.assets.FontSizeManager;\nimport com.neuronrobotics.bowlerstudio.scripting.PasswordManager;\nimport com.neuronrobotics.bowlerstudio.scripting.ScriptingEngine;\nimport com.neuronrobotics.bowlerstudio.vitamins.Vitamins;\n\nimport eu.mihosoft.vrl.v3d.JavaFXInitializer;\nimport javafx.application.Application;\nimport javafx.event.ActionEvent;\nimport javafx.fxml.FXML;\nimport javafx.fxml.FXMLLoader;\nimport javafx.scene.Node;\nimport javafx.scene.Parent;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Alert;\nimport javafx.scene.control.Alert.AlertType;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.CheckBox;\nimport javafx.scene.control.ComboBox;\nimport javafx.scene.control.Menu;\nimport javafx.scene.control.RadioButton;\nimport javafx.scene.control.TableColumn;\nimport javafx.scene.control.TableView;\nimport javafx.scene.control.TextField;\nimport javafx.scene.control.TextInputDialog;\nimport javafx.scene.control.ToggleGroup;\nimport javafx.scene.control.cell.PropertyValueFactory;\nimport javafx.scene.control.cell.TextFieldTableCell;\nimport javafx.scene.layout.AnchorPane;\nimport javafx.scene.paint.Color;\nimport javafx.scene.text.Font;\nimport javafx.stage.Modality;\nimport javafx.stage.Stage;\n\npublic class NewVitaminWizardController extends Application {\n\t@FXML // ResourceBundle that was given to the FXMLLoader\n\tprivate ResourceBundle resources;\n\n\t@FXML // URL location of the FXML file that was given to the FXMLLoader\n\tprivate URL location;\n\n\t@FXML // fx:id=\"typePane\"\n\tprivate AnchorPane typePane; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"x1\"\n\tprivate Font x1; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"x2\"\n\tprivate Color x2; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"existingTypeRadio\"\n\tprivate RadioButton existingTypeRadio; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"newTypeRadio\"\n\tprivate RadioButton newTypeRadio; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"typeComboBox\"\n\tprivate ComboBox<String> typeComboBox; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"sizePane\"\n\tprivate AnchorPane sizePane; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"sizeComboBox\"\n\tprivate ComboBox<String> sizeComboBox; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"measurmentPane\"\n\tprivate AnchorPane measurmentPane; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"measurmentsTable\"\n\tprivate TableView<MeasurmentConfig> measurmentsTable; // Value injected by FXMLLoader\n\t@FXML\n\tprivate TextField newTypeNameField;\n\t@FXML\n\tprivate TextField newSizeField;\n\t@FXML // fx:id=\"nameColumn\"\n\tprivate TableColumn<MeasurmentConfig, String> nameColumn; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"measurmentColumn\"\n\tprivate TableColumn<MeasurmentConfig, String> measurmentColumn; // Value injected by FXMLLoader\n\t@FXML // fx:id=\"isMotor\"\n\tprivate CheckBox isMotor; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"isShaft\"\n\tprivate CheckBox isShaft; // Value injected by FXMLLoader\n\t@FXML // fx:id=\"editExisting\"\n\tprivate CheckBox editExisting; // Value injected by FXMLLoader\n\t@FXML // fx:id=\"editExisting\"\n\tprivate Button newMeasurmentButton;\n\n\tprivate static INewVitaminCallback callback = null;\n\n\tprivate static Stage primaryStage;\n\tprivate static String typeOfVitaminString = null;\n\n\tprivate static String sizeOfVitaminString = null;\n\n\t@FXML\n\tvoid onConfirmAndCreate(ActionEvent event) {\n\t\tsizePane.setDisable(true);\n\t\tmeasurmentPane.setDisable(true);\n\t\ttypePane.setDisable(true);\n\t\tnew Thread(() -> {\n\t\t\ttry {\n\n\t\t\t\tif (newTypeRadio.isSelected()) {\n\t\t\t\t\tif (isShaft.isSelected())\n\t\t\t\t\t\tVitamins.setIsShaft(typeOfVitaminString);\n\t\t\t\t\tif (isMotor.isSelected())\n\t\t\t\t\t\tVitamins.setIsActuator(typeOfVitaminString);\n\t\t\t\t\tGitHub github = PasswordManager.getGithub();\n\n\t\t\t\t\tString newName = typeOfVitaminString + \"CadGenerator\";\n\t\t\t\t\tGHRepository gist = ScriptingEngine.makeNewRepo(newName, newName + \" Generates CAD vitamins \");\n\n\t\t\t\t\tString gitURL = gist.getHtmlUrl().toExternalForm() + \".git\";\n\t\t\t\t\tString filename = typeOfVitaminString + \".groovy\";\n\t\t\t\t\tVitamins.setScript(typeOfVitaminString, gitURL, filename);\n\n\t\t\t\t\tString measurments = \"\";\n\t\t\t\t\tfor (String key : Vitamins.getConfigurationRW(typeOfVitaminString, sizeOfVitaminString).keySet()\n\t\t\t\t\t\t\t.stream().sorted().collect(Collectors.toList())) {\n\t\t\t\t\t\tmeasurments += \"\\n\tdef \" + key + \"Value = measurments.\" + key;\n\t\t\t\t\t}\n\t\t\t\t\tmeasurments += \"\\n\\tfor(String key:measurments.keySet().stream().sorted().collect(Collectors.toList())){\";\n\t\t\t\t\tmeasurments += \"\\n\\t\\tprintln \\\"\" + typeOfVitaminString\n\t\t\t\t\t\t\t+ \" value \\\"+key+\\\" \\\"+measurments.get(key);\\n}\";\n\t\t\t\t\t// for(String key:Vitamins.getConfiguration(\n\t\t\t\t\t// typeOfVitaminString,sizeOfVitaminString).keySet().stream().sorted().collect(Collectors.toList()))\n\t\t\t\t\t// {\n\t\t\t\t\t// String string = key+\"Value\";\n\t\t\t\t\t// measurments+=\"\\n println \\\"Measurment \"+string+\" = \\\"+\"+string;\n\t\t\t\t\t// }\n\t\t\t\t\tString loader = \"import eu.mihosoft.vrl.v3d.parametrics.*;\\n\"\n\t\t\t\t\t\t\t+ \"import java.util.stream.Collectors;\\n\"\n\t\t\t\t\t\t\t+ \"import com.neuronrobotics.bowlerstudio.vitamins.Vitamins;\\n\"\n\t\t\t\t\t\t\t+ \"import eu.mihosoft.vrl.v3d.CSG;\\n\" + \"import eu.mihosoft.vrl.v3d.Cube;\\n\"\n\t\t\t\t\t\t\t+ \"CSG generate(){\\n\" + \"\tString type= \\\"\" + typeOfVitaminString + \"\\\"\\n\"\n\t\t\t\t\t\t\t+ \"\tif(args==null)\\n\" + \"\t\targs=[\\\"\" + sizeOfVitaminString + \"\\\"]\\n\"\n\t\t\t\t\t\t\t+ \"\t// The variable that stores the current size of this vitamin\\n\"\n\t\t\t\t\t\t\t+ \"\tStringParameter size = new StringParameter(\ttype+\\\" Default\\\",\" + \"args.get(0),\"\n\t\t\t\t\t\t\t+ \"Vitamins.listVitaminSizes(type))\\n\"\n\t\t\t\t\t\t\t+ \"\tHashMap<String,Object> measurments = Vitamins.getConfiguration( type,size.getStrValue())\\n\"\n\t\t\t\t\t\t\t+ measurments + \"\\n\" + \"\t// Stub of a CAD object\\n\" + \"\tCSG part = new Cube().toCSG()\\n\"\n\t\t\t\t\t\t\t+ \"\treturn part\\n\" + \"\t\t.setParameter(size)\\n\" + \"\t\t.setRegenerate({generate()})\\n\"\n\t\t\t\t\t\t\t+ \"}\\n\" + \"return generate() \";\n\t\t\t\t\tScriptingEngine.pushCodeToGit(gitURL, ScriptingEngine.getFullBranch(gitURL), filename, loader,\n\t\t\t\t\t\t\t\"new CAD loader script\");\n\t\t\t\t\tnew Thread(() -> BowlerStudio.createFileTab(Vitamins.getScriptFile(typeOfVitaminString))).start();\n\t\t\t\t}\n\t\t\t\tif (isShaft.isSelected())\n\t\t\t\t\tVitamins.setIsShaft(typeOfVitaminString);\n\t\t\t\tif (isMotor.isSelected())\n\t\t\t\t\tVitamins.setIsActuator(typeOfVitaminString);\n\t\t\t\t// Vitamins.saveDatabaseForkIfMissing(typeOfVitaminString);\n\n\t\t\t\tif (newTypeRadio.isSelected()) {\n\t\t\t\t\tcallback.addVitaminType(typeOfVitaminString);\n\t\t\t\t} else if (!editExisting.isSelected()) {\n\t\t\t\t\tcallback.addSizesToMenu(sizeOfVitaminString, typeOfVitaminString);\n\t\t\t\t}\n\t\t\t} catch (Exception e1) {\n\t\t\t\t// Auto-generated catch block\n\t\t\t\tnew IssueReportingExceptionHandler().uncaughtException(Thread.currentThread(), e1);\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tVitamins.saveDatabase(typeOfVitaminString);\n\t\t\t} catch (Exception e) {\n\t\t\t\t// TODO Auto-generated catch block\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\tthis.primaryStage.close();\n\t\t\t\t\tprimaryStage = null;\n\t\t\t\t});\n\n\t\t\t} catch (Exception e) {\n\t\t\t\t// Auto-generated catch block\n\t\t\t\tnew IssueReportingExceptionHandler().uncaughtException(Thread.currentThread(), e);\n\n\t\t\t}\n\t\t\torg.kohsuke.github.GitHub github = PasswordManager.getGithub();\n\n\t\t\tGHRepository repo;\n\t\t\ttry {\n\t\t\t\trepo = github.getRepository(Vitamins.getSourcerepo() + \"/Hardware-Dimensions\");\n\t\t\t\tString head = PasswordManager.getUsername() + \":master\";\n\t\t\t\tList<GHPullRequest> asList = repo.queryPullRequests().state(GHIssueState.OPEN).head(head).list()\n\t\t\t\t\t\t.asList();\n\t\t\t\tif (asList.size() == 0) {\n\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Creating PR for \" + head);\n\t\t\t\t\tGHPullRequest request = repo.createPullRequest(\"User Added vitamins to \" + typeOfVitaminString,\n\t\t\t\t\t\t\thead, \"master\", \"## User added vitamins\", true, true);\n\t\t\t\t\ttry {\n\t\t\t\t\t\tBowlerKernel.upenURL(request.getHtmlUrl().toURI());\n\t\t\t\t\t} catch (URISyntaxException e) {\n\t\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\t\te.printStackTrace();\n\t\t\t\t\t}\n\t\t\t\t} else {\n\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\t// TODO Auto-generated catch block\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\n\t\t}).start();\n\n\t}\n\n\tprivate void saveAndFork() {\n\n\t\ttry {\n\t\t\tVitamins.saveDatabaseForkIfMissing(typeOfVitaminString, PasswordManager.getUsername());\n\n\t\t} catch (Exception e) {\n\t\t\t// Auto-generated catch block\n\t\t\tnew IssueReportingExceptionHandler().uncaughtException(Thread.currentThread(), e);\n\n\t\t}\n\n\t}\n\n\t@FXML\n\tvoid onConfirmSize(ActionEvent event) {\n\t\tif (!editExisting.isSelected()) {\n\t\t\tsizeOfVitaminString = newSizeField.getText();\n\t\t\tif (sizeOfVitaminString.length() < 2) {\n\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\tAlert alert = new Alert(AlertType.WARNING);\n\t\t\t\t\talert.setTitle(\"No name specified!\");\n\t\t\t\t\talert.setHeaderText(\"Names must be at least 2 charrectors long\");\n\t\t\t\t\talert.setContentText(\"Try again...\");\n\t\t\t\t\tNode root = alert.getDialogPane();\n\t\t\t\t\tStage stage = (Stage) alert.getDialogPane().getScene().getWindow();\n\t\t\t\t\tstage.setOnCloseRequest(ev -> alert.hide());\n\t\t\t\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\t\t\t\tint tmp = fontNum - 10;\n\t\t\t\t\t\tif (tmp < 12)\n\t\t\t\t\t\t\ttmp = 12;\n\t\t\t\t\t\troot.setStyle(\"-fx-font-size: \" + tmp + \"pt\");\n\t\t\t\t\t\talert.getDialogPane().applyCss();\n\t\t\t\t\t\talert.getDialogPane().layout();\n\t\t\t\t\t\tstage.sizeToScene();\n\t\t\t\t\t});\n\t\t\t\t\talert.showAndWait();\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tString slug = BowlerStudioMenu.slugify(sizeOfVitaminString);\n\t\t\tif (!sizeOfVitaminString.contentEquals(slug)) {\n\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\tAlert alert = new Alert(AlertType.WARNING);\n\t\t\t\t\talert.setTitle(\"Name Format Wrong\");\n\t\t\t\t\talert.setHeaderText(\"Name must be without spaces or special chars\");\n\t\t\t\t\talert.setContentText(\"Changed to \" + slug + \" confirm to continue...\");\n\t\t\t\t\talert.showAndWait();\n\t\t\t\t\tBowlerStudio.runLater(() -> newSizeField.setText(slug));\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsizeOfVitaminString = slug;\n\t\t} else {\n\t\t\tsizeOfVitaminString = sizeComboBox.getSelectionModel().getSelectedItem();\n\t\t}\n\n\t\tBowlerStudio.runLater(\n\t\t\t\t() -> nameColumn.setCellValueFactory(new PropertyValueFactory<MeasurmentConfig, String>(\"key\")));\n\t\tBowlerStudio.runLater(() -> measurmentColumn\n\t\t\t\t.setCellValueFactory(new PropertyValueFactory<MeasurmentConfig, String>(\"measurment\")));\n\t\tBowlerStudio.runLater(() -> measurmentColumn.setCellFactory(TextFieldTableCell.forTableColumn()));\n\n\t\tBowlerStudio.runLater(() -> measurmentsTable.getSelectionModel().cellSelectionEnabledProperty().set(true));\n\t\tmeasurmentsTable.setEditable(true);\n\t\tnameColumn.setEditable(false);\n\t\tmeasurmentColumn.setEditable(true);\n\t\tmeasurmentColumn.setOnEditCommit(ev -> {\n\t\t\tfinal String value = ev.getNewValue() != null ? (String) ev.getNewValue() : (String) ev.getOldValue();\n\t\t\t((MeasurmentConfig) ev.getTableView().getItems().get(ev.getTablePosition().getRow())).setMeasurment(value);\n\t\t\tmeasurmentsTable.refresh();\n\t\t});\n\n\t\tif (existingTypeRadio.isSelected()) {\n\t\t\tif (!editExisting.isSelected()) {\n\t\t\t\tboolean exists = false;\n\t\t\t\t// for existing types check for existing sizes\n\t\t\t\tArrayList<String> sizes = Vitamins.listVitaminSizes(typeOfVitaminString);\n\t\t\t\tfor (String size : sizes) {\n\t\t\t\t\tif (size.contentEquals(sizeOfVitaminString))\n\t\t\t\t\t\texists = true;\n\t\t\t\t}\n\t\t\t\tif (exists) {\n\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\tAlert alert = new Alert(AlertType.WARNING);\n\t\t\t\t\t\talert.setTitle(\"Size already Exists\");\n\t\t\t\t\t\talert.setHeaderText(\"Name must be unique\");\n\t\t\t\t\t\talert.setContentText(\"Rename and confirm to continue...\");\n\t\t\t\t\t\tNode root = alert.getDialogPane();\n\t\t\t\t\t\tStage stage = (Stage) alert.getDialogPane().getScene().getWindow();\n\t\t\t\t\t\tstage.setOnCloseRequest(ev -> alert.hide());\n\t\t\t\t\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\t\t\t\t\tint tmp = fontNum - 10;\n\t\t\t\t\t\t\tif (tmp < 12)\n\t\t\t\t\t\t\t\ttmp = 12;\n\t\t\t\t\t\t\troot.setStyle(\"-fx-font-size: \" + tmp + \"pt\");\n\t\t\t\t\t\t\talert.getDialogPane().applyCss();\n\t\t\t\t\t\t\talert.getDialogPane().layout();\n\t\t\t\t\t\t\tstage.sizeToScene();\n\t\t\t\t\t\t});\n\t\t\t\t\t\talert.showAndWait();\n\t\t\t\t\t});\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tnew Thread(() -> {\n\t\t\t\tMap<String, Object> configsOld = Vitamins.getConfigurationRW(typeOfVitaminString,\n\t\t\t\t\t\tsizeComboBox.getSelectionModel().getSelectedItem());\n\n\t\t\t\tfor (String key : configsOld.keySet().stream().sorted().collect(Collectors.toList())) {\n\t\t\t\t\tsetupKeyValueToTable(key, configsOld.get(key), sizeOfVitaminString);\n\t\t\t\t}\n\t\t\t}).start();\n\n\t\t}\n\t\tif (newTypeRadio.isSelected()) {\n\t\t\tVitamins.getConfigurationRW(typeOfVitaminString, sizeOfVitaminString);\n\t\t}\n\n\t\tif (Vitamins.isActuator(typeOfVitaminString) || (newTypeRadio.isSelected() && isMotor.isSelected())) {\n\t\t\tsetUpVitaminDefaults();\n\t\t}\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tsizePane.setDisable(true);\n\t\t\ttypePane.setDisable(true);\n\t\t});\n\t\t// new Thread(() -> {\n\t\tHashMap<String, Object> required = new HashMap<String, Object>();\n\t\trequired.put(\"massKg\", 0.001);\n\t\trequired.put(\"source\", \"https://commonwealthrobotics.com\");\n\t\trequired.put(\"price\", 0.01);\n\t\trequired.put(\"massCentroidX\", 0.0);\n\t\trequired.put(\"massCentroidY\", 0.0);\n\t\trequired.put(\"massCentroidZ\", 0.0);\n\t\tsetRequiredFields(required);\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tmeasurmentPane.setDisable(false);\n\t\t});\n\t\t// }).start();\n\n\t}\n\n\tprivate void setUpVitaminDefaults() {\n\t\t// new Thread(() -> {\n\t\tHashMap<String, Object> required = new HashMap<String, Object>();\n\t\trequired.put(\"MaxTorqueNewtonmeters\", 0.001);\n\t\trequired.put(\"MaxFreeSpeedRadPerSec\", 1);\n\t\trequired.put(\"massKg\", 0.001);\n\t\trequired.put(\"shaftType\", \"dShaft\");\n\t\trequired.put(\"shaftSize\", \"5mm\");\n\t\tsetRequiredFields(required);\n\t\t// }).start();\n\t}\n\n\tprivate void setRequiredFields(HashMap<String, Object> required) {\n\t\t// For each vitamin size in a given type\n\n\t\tfor (String size : Vitamins.listVitaminSizes(typeOfVitaminString)) {\n\t\t\tMap<String, Object> configs = Vitamins.getConfigurationRW(typeOfVitaminString, size);\n\t\t\t// For every required key\n\t\t\tfor (String key : required.keySet().stream().sorted().collect(Collectors.toList())) {\n\t\t\t\t// check to see if the current size has this key already\n\t\t\t\tif (!configs.containsKey(key)) {\n\t\t\t\t\t// enter a default value, but ensure that the key exists for downstream code\n\t\t\t\t\tsetupKeyValueToTable(key, required.get(key), size);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void setupKeyValueToTable(String key, Object value, String size) {\n\t\tVitamins.putMeasurment(typeOfVitaminString, size, key, value);\n\t\tif (size.contentEquals(sizeOfVitaminString))\n\t\t\tBowlerStudio.runLater(() -> measurmentsTable.getItems()\n\t\t\t\t\t.add(new MeasurmentConfig(key, typeOfVitaminString, sizeOfVitaminString)));\n\t}\n\n\t@FXML\n\tvoid onConfirmType(ActionEvent event) {\n\n\t\tif (newTypeRadio.isSelected()) {\n\t\t\ttypeOfVitaminString = newTypeNameField.getText();\n\t\t\tif (typeOfVitaminString.length() < 2) {\n\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\tAlert alert = new Alert(AlertType.WARNING);\n\t\t\t\t\talert.setTitle(\"No name specified!\");\n\t\t\t\t\talert.setHeaderText(\"Names must be at least 2 charrectors long\");\n\t\t\t\t\talert.setContentText(\"Try again...\");\n\t\t\t\t\tNode root = alert.getDialogPane();\n\t\t\t\t\tStage stage = (Stage) alert.getDialogPane().getScene().getWindow();\n\t\t\t\t\tstage.setOnCloseRequest(ev -> alert.hide());\n\t\t\t\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\t\t\t\tint tmp = fontNum - 10;\n\t\t\t\t\t\tif (tmp < 12)\n\t\t\t\t\t\t\ttmp = 12;\n\t\t\t\t\t\troot.setStyle(\"-fx-font-size: \" + tmp + \"pt\");\n\t\t\t\t\t\talert.getDialogPane().applyCss();\n\t\t\t\t\t\talert.getDialogPane().layout();\n\t\t\t\t\t\tstage.sizeToScene();\n\t\t\t\t\t});\n\t\t\t\t\talert.showAndWait();\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tString slug = BowlerStudioMenu.slugify(typeOfVitaminString);\n\t\t\tif (!typeOfVitaminString.contentEquals(slug)) {\n\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\tAlert alert = new Alert(AlertType.WARNING);\n\t\t\t\t\talert.setTitle(\"Name Format Wrong\");\n\t\t\t\t\talert.setHeaderText(\"Name must be without spaces or special chars\");\n\t\t\t\t\talert.setContentText(\"Changed to \" + slug + \" confirm to continue...\");\n\t\t\t\t\tNode root = alert.getDialogPane();\n\t\t\t\t\tStage stage = (Stage) alert.getDialogPane().getScene().getWindow();\n\t\t\t\t\tstage.setOnCloseRequest(ev -> alert.hide());\n\t\t\t\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\t\t\t\tint tmp = fontNum - 10;\n\t\t\t\t\t\tif (tmp < 12)\n\t\t\t\t\t\t\ttmp = 12;\n\t\t\t\t\t\troot.setStyle(\"-fx-font-size: \" + tmp + \"pt\");\n\t\t\t\t\t\talert.getDialogPane().applyCss();\n\t\t\t\t\t\talert.getDialogPane().layout();\n\t\t\t\t\t\tstage.sizeToScene();\n\t\t\t\t\t});\n\t\t\t\t\talert.showAndWait();\n\t\t\t\t\tBowlerStudio.runLater(() -> newTypeNameField.setText(slug));\n\t\t\t\t});\n\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttypeOfVitaminString = slug;\n\t\t\tsizeComboBox.setDisable(true);\n\t\t\teditExisting.setDisable(true);\n\t\t\tsizePane.setDisable(false);// saveAndFork();\n\t\t} else {\n\t\t\ttypeOfVitaminString = typeComboBox.getSelectionModel().getSelectedItem();\n\t\t\tsizePane.setDisable(false);// saveAndFork();\n\t\t\tArrayList<String> sizes = Vitamins.listVitaminSizes(typeOfVitaminString);\n\t\t\tboolean hasSize = false;\n\n\t\t\tfor (String size : sizes) {\n\t\t\t\tsizeComboBox.getItems().add(size);\n\t\t\t\tif (typeOfVitaminString != null)\n\t\t\t\t\tif (size.contentEquals(typeOfVitaminString)) {\n\t\t\t\t\t\thasSize = true;\n\t\t\t\t\t}\n\t\t\t}\n\t\t\tif (sizes.size() > 0) {\n\t\t\t\tif (hasSize) {\n\t\t\t\t\tsizeComboBox.getSelectionModel().select(typeOfVitaminString);\n\t\t\t\t} else {\n\t\t\t\t\tsizeComboBox.getSelectionModel().select(sizes.get(0));\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tsizeComboBox.setDisable(true);\n\t\t\t\teditExisting.setDisable(true);\n\t\t\t\teditExisting.setSelected(false);\n\n\t\t\t}\n\t\t}\n\t\tisShaft.setSelected(Vitamins.isShaft(typeOfVitaminString));\n\t\tisMotor.setSelected(Vitamins.isActuator(typeOfVitaminString));\n\t\tmeasurmentPane.setDisable(true);\n\t\ttypePane.setDisable(true);\n\n\t}\n\n\t@FXML\n\tvoid onNewMeasurment(ActionEvent event) {\n\t\tnewMeasurmentButton.setDisable(true);\n\t\tTextInputDialog dialog = new TextInputDialog(\"lengthOfThing\");\n\t\tdialog.setTitle(\"Add new measurment to \" + typeOfVitaminString);\n\t\tdialog.setHeaderText(\"This measurment will be added to all instances of the vitamin\");\n\t\tdialog.setContentText(\"New measurment name:\");\n\n\t\t// Traditional way to get the response value.\n\t\tOptional<String> result = dialog.showAndWait();\n\t\t// The Java 8 way to get the response value (with lambda expression).\n\t\tresult.ifPresent(name -> {\n\t\t\tTextInputDialog dialog2 = new TextInputDialog(\"0.0\");\n\t\t\tdialog2.setTitle(\"Set value of \" + name);\n\t\t\tdialog2.setHeaderText(\"This value will be added to all instances of the vitamin\");\n\t\t\tdialog2.setContentText(name + \" = \");\n\n\t\t\t// Traditional way to get the response value.\n\t\t\tOptional<String> result2 = dialog2.showAndWait();\n\t\t\tresult2.ifPresent(name2 -> {\n\t\t\t\tsetupKeyValueToTable(name, name2, sizeOfVitaminString);\n\t\t\t\tfor (String size : Vitamins.listVitaminSizes(typeOfVitaminString)) {\n\t\t\t\t\tVitamins.putMeasurment(typeOfVitaminString, size, name, name2);\n\n\t\t\t\t}\n\t\t\t});\n\t\t\tnewMeasurmentButton.setDisable(false);\n\n\t\t});\n\n\t}\n\n\t@FXML\n\tvoid onSelectExistingTypeMode(ActionEvent event) {\n\t\tnewTypeNameField.setEditable(false);\n\t\ttypeComboBox.setDisable(false);\n\t\t// isShaft.setDisable(true);\n\t\t// isMotor.setDisable(true);\n\t}\n\n\t@FXML\n\tvoid onSelectNewTypeMode(ActionEvent event) {\n\t\tnewTypeNameField.setEditable(true);\n\t\ttypeComboBox.setDisable(true);\n\t\t// isShaft.setDisable(false);\n\t\t// isMotor.setDisable(false);\n\t}\n\n\t@FXML\n\tvoid onEditExisting(ActionEvent event) {\n\t\tif (editExisting.isSelected()) {\n\t\t\tnewSizeField.setEditable(false);\n\t\t} else {\n\n\t\t\tnewSizeField.setEditable(true);\n\t\t}\n\t}\n\n\t@FXML\n\tvoid onIsMotor(ActionEvent event) {\n\t\tif (isMotor.isSelected()) {\n\t\t\tisShaft.setSelected(false);\n\t\t\tsetUpVitaminDefaults();\n\t\t}\n\t}\n\n\t@FXML\n\tvoid onIsShaft(ActionEvent event) {\n\n\t\tif (isShaft.isSelected())\n\t\t\tisMotor.setSelected(false);\n\n\t}\n\n\t@FXML // This method is called by the FXMLLoader when initialization is complete\n\tvoid initialize() {\n\t\tassert x1 != null : \"fx:id=\\\"x1\\\" was not injected: check your FXML file 'newVitaminWizard.fxml'.\";\n\t\tassert x2 != null : \"fx:id=\\\"x2\\\" was not injected: check your FXML file 'newVitaminWizard.fxml'.\";\n\t\tassert existingTypeRadio != null\n\t\t\t\t: \"fx:id=\\\"existingTypeRadio\\\" was not injected: check your FXML file 'newVitaminWizard.fxml'.\";\n\t\tassert newTypeRadio != null\n\t\t\t\t: \"fx:id=\\\"newTypeRadio\\\" was not injected: check your FXML file 'newVitaminWizard.fxml'.\";\n\t\tassert typeComboBox != null\n\t\t\t\t: \"fx:id=\\\"typeComboBox\\\" was not injected: check your FXML file 'newVitaminWizard.fxml'.\";\n\t\tassert sizeComboBox != null\n\t\t\t\t: \"fx:id=\\\"sizeComboBox\\\" was not injected: check your FXML file 'newVitaminWizard.fxml'.\";\n\t\tassert measurmentsTable != null\n\t\t\t\t: \"fx:id=\\\"measurmentsTable\\\" was not injected: check your FXML file 'newVitaminWizard.fxml'.\";\n\n\t\tsizePane.setDisable(true);\n\t\tmeasurmentPane.setDisable(true);\n\t\tToggleGroup groupForType = new ToggleGroup();\n\t\texistingTypeRadio.setSelected(true);\n\t\tnewTypeRadio.setSelected(false);\n\t\texistingTypeRadio.setToggleGroup(groupForType);\n\t\tnewTypeRadio.setToggleGroup(groupForType);\n\t\tnewTypeNameField.setEditable(false);\n\t\tCopyOnWriteArrayList<String> types = Vitamins.listVitaminTypes();\n\n\t\tfor (String s : types) {\n\t\t\ttypeComboBox.getItems().add(s);\n\t\t}\n\t\tif (typeOfVitaminString == null)\n\t\t\ttypeComboBox.getSelectionModel().select(types.get(0));\n\t\telse\n\t\t\ttypeComboBox.getSelectionModel().select(typeOfVitaminString);\n\t\tisShaft.setDisable(false);\n\t\tisMotor.setDisable(false);\n\n\t}\n\n\tpublic static void launchWizard(INewVitaminCallback callback) throws Exception {\n\t\tNewVitaminWizardController.callback = callback;\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tStage s = new Stage();\n\t\t\tprimaryStage = s;\n\t\t\tnew Thread(() -> {\n\t\t\t\tNewVitaminWizardController controller = new NewVitaminWizardController();\n\t\t\t\ttry {\n\t\t\t\t\tSplashManager.renderSplashFrame(0, \"Creating personal Fork Of Vitamins\");\n\t\t\t\t\tcontroller.saveAndFork();\n\t\t\t\t\tSplashManager.closeSplash();\n\t\t\t\t\tcontroller.start(s);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t\tSplashManager.closeSplash();\n\t\t\t\t}\n\t\t\t}).start();\n\t\t});\n\t}\n\n\tpublic static void main(String[] args) throws Exception {\n\t\tJavaFXInitializer.go();\n\n\t\tNewVitaminWizardController.launchWizard(new INewVitaminCallback() {\n\n\t\t\t@Override\n\t\t\tpublic Menu getTypeMenu(String type) {\n\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Get Vitamin Menu\");\n\n\t\t\t\treturn new Menu(type);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void addVitaminType(String s) {\n\t\t\t\tgetTypeMenu(s);\n\t\t\t\tArrayList<String> sizes = Vitamins.listVitaminSizes(s);\n\t\t\t\tfor (String size : sizes) {\n\t\t\t\t\taddSizesToMenu(size, s);\n\t\t\t\t}\n\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Add addVitaminType \" + s);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void addSizesToMenu(String size, String type) {\n\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Add addSizesToMenu \" + type + \" \" + size);\n\t\t\t}\n\t\t});\n\n\t}\n\n\t@Override\n\tpublic void start(Stage primaryStage) throws Exception {\n\t\tFXMLLoader loader = AssetFactory.loadLayout(\"layout/newVitaminWizard.fxml\", true);\n\t\tParent root;\n\t\t// loader.setController(this);\n\t\t// This is needed when loading on MAC\n\t\tloader.setClassLoader(getClass().getClassLoader());\n\t\troot = loader.load();\n\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\tint tmp = fontNum - 10;\n\t\t\tif (tmp < 12)\n\t\t\t\ttmp = 12;\n\t\t\troot.setStyle(\"-fx-font-size: \" + tmp + \"pt\");\n\t\t});\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tprimaryStage.setTitle(\"Edit Vitamins Wizard\");\n\n\t\t\tScene scene = new Scene(root);\n\t\t\tprimaryStage.setScene(scene);\n\t\t\tprimaryStage.initModality(Modality.WINDOW_MODAL);\n\t\t\tprimaryStage.setResizable(true);\n\t\t\tprimaryStage.show();\n\t\t});\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/OutputFilter.java",
    "content": "/*\n * To change this license header, choose License Headers in Project Properties.\n * To change this template file, choose Tools | Templates\n * and open the template in the editor.\n */\n\npackage com.neuronrobotics.bowlerstudio;\n\n/**\n *\n * @author Michael Hoffer &lt;info@michaelhoffer.de&gt;\n */\n@FunctionalInterface\npublic interface OutputFilter {\n\tpublic boolean onMatch(String s);\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/PluginFactory.java",
    "content": "package com.neuronrobotics.bowlerstudio;\n\nimport com.neuronrobotics.bowlerstudio.tabs.AbstractBowlerStudioTab;\n\npublic interface PluginFactory {\n\tAbstractBowlerStudioTab generateNewPlugin()\n\t\t\tthrows ClassNotFoundException, InstantiationException, IllegalAccessException;\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/PluginManager.java",
    "content": "package com.neuronrobotics.bowlerstudio;\n\nimport com.neuronrobotics.bowlerstudio.assets.AssetFactory;\nimport com.neuronrobotics.bowlerstudio.creature.CreatureLab;\nimport com.neuronrobotics.bowlerstudio.tabs.*;\nimport com.neuronrobotics.nrconsole.plugin.BowlerCam.BowlerCamController;\nimport com.neuronrobotics.nrconsole.plugin.DyIO.Secheduler.AnamationSequencer;\nimport com.neuronrobotics.nrconsole.plugin.bootloader.BootloaderPanel;\nimport com.neuronrobotics.pidsim.LinearPhysicsEngine;\nimport com.neuronrobotics.pidsim.PidLab;\nimport com.neuronrobotics.sdk.addons.gamepad.BowlerJInputDevice;\nimport com.neuronrobotics.sdk.addons.kinematics.FirmataBowler;\nimport com.neuronrobotics.sdk.addons.kinematics.MobileBase;\nimport com.neuronrobotics.sdk.bootloader.NRBootLoader;\nimport com.neuronrobotics.sdk.bowlercam.device.BowlerCamDevice;\nimport com.neuronrobotics.sdk.common.BowlerAbstractDevice;\nimport com.neuronrobotics.sdk.common.DMDevice;\nimport com.neuronrobotics.sdk.common.Log;\nimport com.neuronrobotics.sdk.dyio.DyIO;\nimport com.neuronrobotics.sdk.util.ThreadUtil;\n\nimport javafx.event.Event;\nimport javafx.event.EventHandler;\nimport javafx.scene.Node;\nimport javafx.scene.control.*;\nimport javafx.scene.control.cell.CheckBoxTreeCell;\nimport javafx.scene.layout.VBox;\nimport javafx.scene.text.Text;\nimport java.util.ArrayList;\n\n//import com.neuronrobotics.nrconsole.plugin.DyIO.DyIOConsole;\n\npublic class PluginManager {\n\n\tprivate BowlerAbstractDevice dev;\n\n\tprivate static ArrayList<DeviceSupportPluginMap> deviceSupport = new ArrayList<>();\n\tprivate ArrayList<AbstractBowlerStudioTab> liveTabs = new ArrayList<>();\n\n\t// add tabs to the support list based on thier class\n\t// adding additional classes here will show up in the default\n\t// tabs list for objects of that type\n\tstatic {\n\t\t// DyIO\n\t\taddPlugin(new DeviceSupportPluginMap(DyIO.class, AnamationSequencer.class));\n\n\t\t// Ipid\n\t\t// Image s\n\t\t// addPlugin(new DeviceSupportPluginMap(AbstractImageProvider.class,\n\t\t// CameraTab.class));\n\t\t// addPlugin(new DeviceSupportPluginMap(AbstractImageProvider.class,\n\t\t// SalientTab.class));\n\t\t// Bootloader\n\t\taddPlugin(new DeviceSupportPluginMap(NRBootLoader.class, BootloaderPanel.class));\n\t\t// BowlerBoard Specific\n\t\t// addPlugin(new DeviceSupportPlugginMap(BowlerBoardDevice.class, //none yet));\n\t\t// AbstractKinematicsNR\n\t\t// addPlugin(new DeviceSupportPluginMap(AbstractKinematicsNR.class,\n\t\t// JogKinematicsDevice.class));\n\t\t// addPlugin(new DeviceSupportPluginMap(AbstractKinematicsNR.class,\n\t\t// AdvancedKinematicsController.class));\n\t\t// addPlugin(new DeviceSupportPluginMap(AbstractKinematicsNR.class,\n\t\t// DhLab.class));\n\t\taddPlugin(new DeviceSupportPluginMap(MobileBase.class, CreatureLab.class));\n\t\t// NRPrinter\n\t\t// Bowler Cam\n\t\taddPlugin(new DeviceSupportPluginMap(BowlerCamDevice.class, BowlerCamController.class));\n\t\t// LinearPhysicsEngine\n\t\taddPlugin(new DeviceSupportPluginMap(LinearPhysicsEngine.class, PidLab.class));\n\t\t// Firmata\n\t\taddPlugin(new DeviceSupportPluginMap(FirmataBowler.class, FirmataTab.class));\n\t\t// game controller\n\t\taddPlugin(new DeviceSupportPluginMap(BowlerJInputDevice.class, CalibrateGameControl.class));\n\t}\n\n\tpublic PluginManager(BowlerAbstractDevice dev) {\n\t\tthis.dev = dev;\n\t\tif (!dev.isAvailable())\n\t\t\tthrow new RuntimeException(\"Device is not reporting available \" + dev.getClass().getSimpleName());\n\t}\n\n\tpublic static void addPlugin(DeviceSupportPluginMap newMap) {\n\n\t\tfor (int i = 0; i < deviceSupport.size(); i++) {\n\t\t\ttry {\n\t\t\t\tif (deviceSupport.get(i).getDevice() == newMap.getDevice()\n\t\t\t\t\t\t&& deviceSupport.get(i).getPlugin() == newMap.getPlugin()) {\n\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Removing duplicate plugin: \" + deviceSupport.remove(i));\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t}\n\t\tLog.debug(\"Adding Plugin \" + newMap);\n\t\tdeviceSupport.add(newMap);\n\t}\n\n\tpublic void setName(String name) {\n\t\tdev.setScriptingName(name);\n\t}\n\n\tpublic String getName() {\n\t\treturn dev.getScriptingName();\n\t}\n\n\tpublic BowlerAbstractDevice getDevice() {\n\t\treturn dev;\n\t}\n\n\tprivate AbstractBowlerStudioTab generateTab(DeviceSupportPluginMap c)\n\t\t\tthrows ClassNotFoundException, InstantiationException, IllegalAccessException {\n\t\tfor (AbstractBowlerStudioTab t : liveTabs) {\n\t\t\tif (c.getPlugin().isInstance(t)) {\n\t\t\t\t// tab already exists, wake it up and return it\n\t\t\t\tt.onTabReOpening();\n\t\t\t\treturn t;\n\t\t\t}\n\t\t}\n\t\tAbstractBowlerStudioTab t = c.generateNewPlugin();\n\n\t\tt.setDevice(dev);\n\t\tliveTabs.add(t);\n\n\t\treturn t;\n\t}\n\n\tpublic void setTree(TreeItem<String> item) {\n\n\t}\n\n\tpublic BowlerStudioController getBowlerStudioController() {\n\t\treturn BowlerStudioController.getBowlerStudio();\n\t}\n\n\tpublic Node getBowlerBrowser() {\n\n\t\tCheckBoxTreeItem<String> rpc = new CheckBoxTreeItem<>(\"Bowler RPC\");\n\t\tTreeView<String> treeView = new TreeView<>(rpc);\n\t\ttreeView.setCellFactory(CheckBoxTreeCell.forTreeView());\n\n\t\treturn treeView;\n\n\t}\n\n\tpublic ArrayList<TitledPane> getPlugins() {\n\t\tArrayList<TitledPane> plugins = new ArrayList<>();\n\n\t\tVBox pluginLauncher = new VBox(20);\n\n\t\tfor (DeviceSupportPluginMap c : deviceSupport) {\n\t\t\tif (c.getDevice().isInstance(dev)) {\n\t\t\t\tButton launcher;\n\t\t\t\ttry {\n\t\t\t\t\tlauncher = new Button(c.getPlugin().getSimpleName(), AssetFactory.loadIcon(\"Plugin-Icon.png\"));\n\t\t\t\t} catch (RuntimeException e) {\n\t\t\t\t\tlauncher = new Button(c.getPlugin().getSimpleName());\n\t\t\t\t}\n\t\t\t\tlauncher.setTooltip(\n\t\t\t\t\t\tnew javafx.scene.control.Tooltip(\"Launch plugin to \" + c.getPlugin().getSimpleName()));\n\t\t\t\ttry {// These tabs are the select few to autoload when a device of theis type is\n\t\t\t\t\t\t// connected\n\t\t\t\t\tif (BootloaderPanel.class == c.getPlugin() || CreatureLab.class == c.getPlugin()) {\n\t\t\t\t\t\tif (getBowlerStudioController() != null) {\n\t\t\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Auto loading \" + c.getPlugin().getSimpleName());\n\t\t\t\t\t\t\tLog.warning(\"Attempting Autoloading \" + c);\n\t\t\t\t\t\t\t// if(CreatureLab.class !=c.getPlugin())\n\t\t\t\t\t\t\tlaunchTab(c, launcher);\n\t\t\t\t\t\t\t// else\n\t\t\t\t\t\t\t// generateTab(c);// dont add the creature lab it uses the overlays\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tLog.warning(\"Not autoloading \" + c);\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\te.printStackTrace();\n\n\t\t\t\t}\n\t\t\t\tButton l = launcher;\n\t\t\t\tlauncher.setOnAction(b -> {\n\t\t\t\t\tlaunchTab(c, l);\n\t\t\t\t});\n\n\t\t\t\tpluginLauncher.getChildren().add(launcher);\n\t\t\t}\n\t\t}\n\t\tString simpleName = dev.getClass().getSimpleName();\n\t\tif (DMDevice.class.isInstance((dev))) {\n\t\t\tsimpleName = ((DMDevice) dev).getWrapped().getClass().getSimpleName();\n\t\t}\n\n\t\tText text = new Text(\"\\n\" + simpleName + \"\\n\");\n\n\t\tTitledPane info = new TitledPane(\"Device Info\", text);\n\n\t\tTitledPane protocol = new TitledPane(\"Bowler Protocol\", getBowlerBrowser());\n\t\tTitledPane pluginsPane = new TitledPane(\"Plugins\", pluginLauncher);\n\t\tinfo.setGraphic(AssetFactory.loadIcon(\"Info.png\"));\n\t\tprotocol.setGraphic(AssetFactory.loadIcon(\"BowlerStudio.png\"));\n\t\tpluginsPane.setGraphic(AssetFactory.loadIcon(\"Plugins.png\"));\n\t\tplugins.add(info);\n\t\tif (dev.getConnection() != null)\n\t\t\tplugins.add(protocol);\n\t\tplugins.add(pluginsPane);\n\t\treturn plugins;\n\t}\n\n\tprivate void launchTab(DeviceSupportPluginMap c, Button launcher) {\n\t\tnew Thread() {\n\t\t\tpublic void run() {\n\t\t\t\tThread.currentThread().setUncaughtExceptionHandler(new IssueReportingExceptionHandler());\n\n\t\t\t\tsetName(\"Launching \" + c.getPlugin().getSimpleName());\n\t\t\t\ttry {\n\t\t\t\t\tAbstractBowlerStudioTab t = generateTab(c);\n\n\t\t\t\t\t// allow the threads to finish before adding\n\t\t\t\t\tThreadUtil.wait(150);\n\t\t\t\t\tif (t.getContent() == null) {\n\t\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"ERROR tab failed to load!\");\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tgetBowlerStudioController().addTab(t, true);\n\n\t\t\t\t\tt.setOnCloseRequest(new EventHandler<Event>() {\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic void handle(Event arg0) {\n\t\t\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"PM is Closing \" + t.getText());\n\t\t\t\t\t\t\tt.onTabClosing();\n\t\t\t\t\t\t\tBowlerStudio.runLater(() -> launcher.setDisable(false));\n\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\tlauncher.setDisable(true);\n\t\t\t\t\t});\n\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Launching \" + c.getPlugin().getSimpleName());\n\n\t\t\t\t} catch (Exception e1) {\n\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\te1.printStackTrace();\n\t\t\t\t}\n\t\t\t}\n\t\t}.start();\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/PluginManagerWidget.java",
    "content": "package com.neuronrobotics.bowlerstudio;\n\nimport com.neuronrobotics.bowlerstudio.assets.AssetFactory;\nimport com.neuronrobotics.sdk.common.Log;\nimport javafx.geometry.Insets;\nimport javafx.scene.Node;\nimport javafx.scene.control.Accordion;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.TextField;\nimport javafx.scene.control.TitledPane;\nimport javafx.scene.layout.HBox;\nimport javafx.scene.layout.Priority;\n\nimport java.util.ArrayList;\n\npublic class PluginManagerWidget extends TitledPane {\n\tprivate PluginManager manager;\n\tprivate TextField deviceName = new TextField();\n\tprivate Button disconnectTHis;\n\tfinal Accordion accordion = new Accordion();\n\n\tpublic PluginManagerWidget(PluginManager m, Node graphic) {\n\t\tHBox content = new HBox(20);\n\n\t\tcontent.setPadding(new Insets(0, 20, 10, 20));\n\t\tthis.manager = m;\n\t\tArrayList<TitledPane> plugins = manager.getPlugins();\n\t\taccordion.getPanes().addAll(plugins);\n\t\tdisconnectTHis = new Button(\"Disconnect \" + manager.getName(), AssetFactory.loadIcon(\"Disconnect-Device.png\"));\n\n\t\tdisconnectTHis.setOnMousePressed(event -> {\n\t\t\tnew Thread() {\n\t\t\t\tpublic void run() {\n\t\t\t\t\tThread.currentThread().setUncaughtExceptionHandler(new IssueReportingExceptionHandler());\n\n\t\t\t\t\tsetName(\"disconnect plugins\");\n\t\t\t\t\tLog.warning(\"Disconnect button for \" + manager.getName() + \" pressed\");\n\t\t\t\t\tgetManager().getDevice().disconnect();\n\n\t\t\t\t}\n\t\t\t}.start();\n\n\t\t});\n\t\tsetGraphic(AssetFactory.loadIcon(\"Bowler-Device-In-Manager.png\"));\n\t\tdeviceName.setOnAction(event -> {\n\t\t\tgetManager().setName(deviceName.getText());\n\t\t\tsetText(manager.getName());\n\t\t\tdisconnectTHis.setText(\"Disconnect \" + manager.getName());\n\t\t});\n\t\tBowlerStudio.runLater(() -> deviceName.setText(manager.getName()));\n\t\tcontent.setHgrow(accordion, Priority.ALWAYS);\n\t\tcontent.getChildren().addAll(graphic, disconnectTHis, deviceName, accordion);\n\t\tsetContent(content);\n\t\tsetText(manager.getName());\n\t}\n\n\tpublic PluginManager getManager() {\n\t\treturn manager;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/PsudoSplash.java",
    "content": "package com.neuronrobotics.bowlerstudio;\n\nimport java.net.URISyntaxException;\n//import java.awt.AlphaComposite;\n//import java.awt.BorderLayout;\n//import java.awt.Color;\n//import java.awt.Dimension;\n//import java.awt.EventQueue;\n//import java.awt.Graphics;\n//import java.awt.Graphics2D;\n//import java.awt.RenderingHints;\n//import java.awt.image.BufferedImage;\n//import java.io.IOException;\nimport java.net.URL;\n//\n//import javax.imageio.ImageIO;\n//import javax.swing.JFrame;\n//import javax.swing.JPanel;\n//import javax.swing.SwingUtilities;\n//import javax.swing.UIManager;\n//import javax.swing.UnsupportedLookAndFeelException;\n\nimport com.neuronrobotics.bowlerstudio.assets.FontSizeManager;\nimport com.neuronrobotics.bowlerstudio.assets.StudioBuildInfo;\nimport com.neuronrobotics.bowlerstudio.scripting.DownloadManager;\nimport com.neuronrobotics.bowlerstudio.scripting.GitLogProgressMonitor;\nimport com.neuronrobotics.bowlerstudio.scripting.ScriptingEngine;\n\nimport eu.mihosoft.vrl.v3d.JavaFXInitializer;\nimport javafx.scene.paint.Color;\nimport javafx.application.Platform;\nimport javafx.scene.Scene;\n\nimport javafx.scene.control.Label;\nimport javafx.scene.image.Image;\nimport javafx.scene.image.ImageView;\nimport javafx.scene.layout.*;\nimport javafx.stage.Modality;\nimport javafx.stage.Stage;\nimport javafx.stage.StageStyle;\n\npublic class PsudoSplash implements GitLogProgressMonitor {\n\t// Static configuration\n\tprivate static int versionX = 65;\n\tprivate static int versionY = 45;\n\tprivate static int messageX = 65;\n\tprivate static int messageY = 260;\n\tprivate static int logY = 120;\n\tprivate static int logX = 15;\n\tprivate static PsudoSplash singelton = null;\n\tprivate static URL resource = PsudoSplash.class.getResource(\"splash.png\");\n\tprivate static URL dockIcon;\n\tprivate static Color TextColor = Color.WHITE;\n\n\t// Class Variables\n\tprivate long timeOfLastUpdate = 0;\n\tprivate String message = \"\";\n\tprivate String log1 = \"\";\n\tprivate String log2 = \"\";\n\tprivate Stage popupStage;\n\tprivate ImageView imageView;\n\tprivate Scene popupScene;\n\tprivate AnchorPane popupRoot;\n\tprivate Label verL = new Label();\n\tprivate Label logL1 = new Label();\n\tprivate Label logL2 = new Label();\n\tprivate Label mesL = new Label();\n\tprivate double setWidth;\n\tprivate double scale;\n\tprivate static Stage parentWindow = null;\n\n\tpublic static boolean isInitialized() {\n\t\treturn singelton != null;\n\t}\n\n\tpublic static PsudoSplash get() {\n\t\tif (singelton == null)\n\t\t\tsingelton = new PsudoSplash();\n\t\tif (!singelton.isVisibleSplash()) {\n\t\t\tPlatform.runLater(() -> {\n\t\t\t\tif (singelton != null)\n\t\t\t\t\tsingelton.showPopup();\n\t\t\t});\n\t\t\t// new Exception(\"Opening Splash\").printStackTrace();\n\t\t}\n\t\treturn singelton;\n\t}\n\n\tpublic static void close() {\n\t\tif (singelton != null)\n\t\t\tsingelton.closeSplashLocal();\n\t\tsingelton = null;\n\t}\n\n\t@Override\n\tpublic void onLogUpdate(String update, Exception e) {\n\n\t\tString[] s = update.split(\"\\n\");\n\t\tlog1 = s[0];\n\t\tlog2 = \"\";\n\t\tif (s.length > 1) {\n\t\t\tfor (int i = 1; i < s.length; i++) {\n\t\t\t\tlog2 += s[i] + \" \";\n\t\t\t}\n\t\t}\n\n\t\tif (isVisibleSplash())\n\t\t\tupdateSplash();\n\t}\n\n\tpublic static int getVersionX() {\n\t\treturn versionX;\n\t}\n\n\tpublic static void setVersionX(int x) {\n\t\tversionX = x;\n\t}\n\n\tpublic static int getVersionY() {\n\t\treturn versionY;\n\t}\n\n\tpublic static void setVersionY(int y) {\n\t\tversionY = y;\n\t}\n\n\tprivate PsudoSplash() {\n\n\t\tPlatform.runLater(() -> {\n\n\t\t\ttry {\n\t\t\t\tsetPopupStage(new Stage(StageStyle.TRANSPARENT));\n\t\t\t} catch (IllegalStateException ex) {\n\t\t\t\tJavaFXInitializer.go();\n\t\t\t\tsetPopupStage(new Stage(StageStyle.TRANSPARENT));\n\t\t\t}\n\n\t\t\t// Always show on top\n\t\t\t// popupStage.setAlwaysOnTop(true);\n\n\t\t\tpopupRoot = new AnchorPane();\n\n\t\t\t// Load your image\n\n\t\t\tString path;\n\t\t\ttry {\n\t\t\t\tpath = resource.toURI().toString();\n\t\t\t} catch (URISyntaxException e) {\n\t\t\t\te.printStackTrace();\n\t\t\t\tclose();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcom.neuronrobotics.sdk.common.Log.debug(\"Loading splash image: \" + path);\n\t\t\tImage image = new Image(path);\n\t\t\timageView = new ImageView(image);\n\t\t\tdouble height = image.getHeight();\n\t\t\tdouble width = image.getWidth();\n\n\t\t\tsetWidth = 500;\n\n\t\t\tscale = setWidth / width;\n\t\t\tdouble calculatedHeight = scale * height;\n\t\t\timageView.setFitWidth(setWidth);\n\t\t\timageView.setFitHeight(calculatedHeight);\n\n\t\t\t// Add the image to the popup root\n\t\t\tpopupRoot.getChildren().add(imageView);\n\t\t\tpopupRoot.getChildren().add(verL);\n\t\t\tpopupRoot.getChildren().add(mesL);\n\t\t\tpopupRoot.getChildren().add(logL1);\n\t\t\tpopupRoot.getChildren().add(logL2);\n\t\t\tpopupScene = new Scene(popupRoot);\n\t\t\tpopupScene.setFill(null); // Make scene background transparent\n\n\t\t\tgetPopupStage().setScene(popupScene);\n\n\t\t\t// Optional: Allow the popup to be dragged\n\t\t\tfinal double[] xOffset = {0};\n\t\t\tfinal double[] yOffset = {0};\n\n\t\t\tpopupRoot.setOnMousePressed(event -> {\n\t\t\t\txOffset[0] = event.getSceneX();\n\t\t\t\tyOffset[0] = event.getSceneY();\n\t\t\t\tgetPopupStage().setAlwaysOnTop(false);\n\t\t\t\tif (event.getClickCount() == 2) {\n\t\t\t\t\tclose();\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tpopupRoot.setOnMouseDragged(event -> {\n\t\t\t\tgetPopupStage().setX(event.getScreenX() - xOffset[0]);\n\t\t\t\tgetPopupStage().setY(event.getScreenY() - yOffset[0]);\n\n\t\t\t});\n\t\t\ttry {\n\t\t\t\t// CADoodle-Icon.png\n\t\t\t\tif (dockIcon != null) {\n\t\t\t\t\tImage loadAsset = new Image(dockIcon.toString());\n\t\t\t\t\tgetPopupStage().getIcons().add(loadAsset);\n\n\t\t\t\t}\n\n\t\t\t} catch (Exception e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t\tif (parentWindow == null) {\n\t\t\t\t// Use NONE modality to prevent the window from becoming disabled\n\t\t\t\tgetPopupStage().initModality(Modality.NONE);\n\t\t\t\tgetPopupStage().setAlwaysOnTop(true);\n\t\t\t} else {\n\t\t\t\tgetPopupStage().initOwner(parentWindow);\n\t\t\t\tgetPopupStage().initModality(Modality.WINDOW_MODAL);\n\t\t\t}\n\t\t\tupdateSplash();\n\t\t});\n\t\ttry {\n\t\t\tThread.sleep(20);\n\t\t} catch (InterruptedException e) {\n\t\t\t// TODO Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t}\n\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\tdouble tmp = FontSizeManager.getImageScale() * 14;\n\t\t\tmesL.setStyle(\"-fx-font-size: \" + ((int) tmp) + \"pt\");\n\t\t\tlogL1.setStyle(\"-fx-font-size: \" + ((int) tmp) + \"pt\");\n\t\t\tlogL2.setStyle(\"-fx-font-size: \" + ((int) tmp) + \"pt\");\n\t\t\tverL.setStyle(\"-fx-font-size: \" + ((int) tmp) + \"pt\");\n\t\t});\n\t}\n\n\tprivate void showPopup() {\n\t\tStage popupStage2 = getPopupStage();\n\t\tif (popupStage2 != null)\n\t\t\tpopupStage2.show();\n\t}\n\n\tpublic static boolean isVisibleSplash() {\n\t\tif (singelton == null)\n\t\t\treturn false;\n\t\tif (singelton.getPopupStage() == null)\n\t\t\treturn false;\n\t\treturn singelton.getPopupStage().isShowing();\n\t}\n\n\tprivate void closeSplashLocal() {\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tStage popupStage2 = getPopupStage();\n\t\t\tif (popupStage2 != null)\n\t\t\t\tpopupStage2.hide();\n\t\t\tsetPopupStage(null);\n\t\t});\n\t\tif (!Platform.isFxApplicationThread())\n\t\t\ttry {\n\t\t\t\tThread.sleep(20);\n\t\t\t} catch (InterruptedException e) {\n\t\t\t\t// TODO Auto-generated catch block\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t}\n\n\tvoid updateSplash() {\n\t\tif (System.currentTimeMillis() - timeOfLastUpdate < 100) {\n\t\t\treturn;\n\t\t}\n\t\ttimeOfLastUpdate = System.currentTimeMillis();\n\t\tif (popupScene != null) {\n\t\t\t// com.neuronrobotics.sdk.common.Log.debug(\"Updating Splash\n\t\t\t// \"+imageView.getFitWidth());\n\t\t\tPlatform.runLater(() -> {\n\t\t\t\t// popupStage.setAlwaysOnTop(false);\n\t\t\t\tpopupScene.setFill(null);\n\t\t\t\tpopupScene.getStylesheets().clear();\n\t\t\t\t// Explicitly set an empty style\n\t\t\t\tpopupRoot.setStyle(\"-fx-background-color: transparent;\");\n\t\t\t\tlogL1.setLayoutX(logX * scale);\n\t\t\t\tlogL1.setLayoutY(logY * scale);\n\n\t\t\t\tlogL2.setLayoutX((logX + 30) * scale);\n\t\t\t\tlogL2.setLayoutY((logY + 40) * scale);\n\t\t\t\tmesL.setLayoutX(messageX * scale);\n\t\t\t\tmesL.setLayoutY(messageY * scale);\n\t\t\t\tverL.setLayoutX(versionX * scale);\n\t\t\t\tverL.setLayoutY(versionY * scale);\n\n\t\t\t\tlogL1.setTextFill(TextColor);\n\t\t\t\tlogL2.setTextFill(TextColor);\n\t\t\t\tmesL.setTextFill(TextColor);\n\t\t\t\tverL.setTextFill(TextColor);\n\n\t\t\t\tlogL1.setText(log1);\n\t\t\t\tlogL2.setText(log2);\n\n\t\t\t\tmesL.setText(message);\n\t\t\t\tverL.setText(StudioBuildInfo.getVersion());\n\t\t\t}); // Make scene background transparent\n\t\t}\n\t}\n\n\tpublic String getMessage() {\n\t\treturn message;\n\t}\n\n\tpublic void setMessage(String message) {\n\t\tif (message.length() > 45) {\n\t\t\tthis.message = message.subSequence(0, 45).toString();\n\t\t\t// new RuntimeException().printStackTrace();\n\t\t} else\n\t\t\tthis.message = message;\n\n\t\tScriptingEngine.addLogListener(this);\n\t\tDownloadManager.addLogListener(this);\n\t\tlog1 = \"\";\n\t\tlog2 = \"\";\n\t}\n\n\tpublic static URL getResource() {\n\t\treturn resource;\n\t}\n\n\tpublic static void setResource(URL r) {\n\t\tresource = r;\n\t}\n\n\tpublic static Color getTextColor() {\n\t\treturn TextColor;\n\t}\n\n\tpublic static void setTextColor(Color textColor) {\n\t\tTextColor = textColor;\n\t}\n\n\tpublic static int getMessageX() {\n\t\treturn messageX;\n\t}\n\n\tpublic static void setMessageX(int messageX) {\n\t\tPsudoSplash.messageX = messageX;\n\t}\n\n\tpublic static int getMessageY() {\n\t\treturn messageY;\n\t}\n\n\tpublic static void setMessageY(int messageY) {\n\t\tPsudoSplash.messageY = messageY;\n\t}\n\n\tpublic static int getLogY() {\n\t\treturn logY;\n\t}\n\n\tpublic static void setLogY(int logY) {\n\t\tPsudoSplash.logY = logY;\n\t}\n\n\tpublic static int getLogX() {\n\t\treturn logX;\n\t}\n\n\tpublic static void setLogX(int logX) {\n\t\tPsudoSplash.logX = logX;\n\t}\n\n\tpublic static void setTrayIcon(URL resource2) {\n\t\tPsudoSplash.setDockIconResource(resource2);\n\t}\n\n\tpublic static URL getDockIconResource() {\n\t\treturn dockIcon;\n\t}\n\n\tpublic static void setDockIconResource(URL resource2) {\n\t\tPsudoSplash.dockIcon = resource2;\n\t}\n\n\tpublic static Stage getParentWindow() {\n\t\treturn parentWindow;\n\t}\n\n\tpublic static void setParentWindow(Stage pw) {\n\t\tparentWindow = pw;\n\t}\n\n\tpublic Stage getPopupStage() {\n\t\treturn popupStage;\n\t}\n\n\tpublic void setPopupStage(Stage popupStage) {\n\t\tthis.popupStage = popupStage;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/RedirectableStream.java",
    "content": "\n/*\n * RedirectableStream.java\n *\n * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.\n *\n * Copyright (c) 2009–2012 Steinbeis Forschungszentrum (STZ Ölbronn),\n * Copyright (c) 2006–2012 by Michael Hoffer\n *\n * This file is part of Visual Reflection Library (VRL).\n *\n * VRL is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Lesser General Public License version 3\n * as published by the Free Software Foundation.\n *\n * see: http://opensource.org/licenses/LGPL-3.0\n *      file://path/to/VRL/src/eu/mihosoft/vrl/resources/license/lgplv3.txt\n *\n * VRL is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Lesser General Public License for more details.\n *\n * This version of VRL includes copyright notice and attribution requirements.\n * According to the LGPL this information must be displayed even if you modify\n * the source code of VRL. Neither the VRL Canvas attribution icon nor any\n * copyright statement/attribution may be removed.\n *\n * Attribution Requirements:\n *\n * If you create derived work you must do three things regarding copyright\n * notice and author attribution.\n *\n * First, the following text must be displayed on the Canvas:\n * \"based on VRL source code\". In this case the VRL canvas icon must be removed.\n *\n * Second, the copyright notice must remain. It must be reproduced in any\n * program that uses VRL.\n *\n * Third, add an additional notice, stating that you modified VRL. In addition\n * you must cite the publications listed below. A suitable notice might read\n * \"VRL source code modified by YourName 2012\".\n *\n * Note, that these requirements are in full accordance with the LGPL v3\n * (see 7. Additional Terms, b).\n *\n * Publications:\n *\n * M. Hoffer, C.Poliwoda, G.Wittum. Visual Reflection Library -\n * A Framework for Declarative GUI Programming on the Java Platform.\n * Computing and Visualization in Science, 2011, in press.\n */\npackage com.neuronrobotics.bowlerstudio;\n\nimport java.io.OutputStream;\nimport java.io.PrintStream;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.FutureTask;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport javafx.application.Platform;\nimport javafx.scene.control.TextArea;\n\n/**\n *\n * @author Michael Hoffer info@michaelhoffer.de\n */\npublic class RedirectableStream extends PrintStream {\n\n\tpublic static PrintStream ORIGINAL_SOUT = System.out;\n\tpublic static PrintStream ORIGINAL_SERR = System.err;\n\n\tprivate final List<TextArea> views = new ArrayList<>();\n\tprivate boolean redirectToUi;\n\tprivate boolean redirectToStdOut;\n\tprivate final List<OutputFilter> filters = new ArrayList<>();\n\n\tpublic RedirectableStream(OutputStream out, TextArea... views) {\n\t\tsuper(out);\n\t\tthis.views.clear();\n\n\t\tfor (TextArea textArea : views) {\n\t\t\taddView(textArea);\n\t\t}\n\n\t\tsetRedirectToStdOut(true);\n\t}\n\n\t@Override\n\tpublic synchronized void write(byte[] buf, int off, int len) {\n\t\tif (isRedirectToUi()) {\n\n\t\t\tinvokeAndWait(() -> {\n\n\t\t\t\tint i = 0;\n\t\t\t\tfor (TextArea view : views) {\n\n\t\t\t\t\tString s = new String(buf, off, len);\n\n\t\t\t\t\tif (filters.get(i).onMatch(s)) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tint startOffSet = view.getText().length();\n\t\t\t\t\t\t\tview.insertText(startOffSet, s);\n\t\t\t\t\t\t} catch (IllegalArgumentException e) {\n\t\t\t\t\t\t\te.printStackTrace();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\ti++;\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// view.setCaretPosition(startOffSet + len);\n\t\t\t// }\n\t\t\t// catch (BadLocationException e) {\n\t\t\t// e.printStackTrace();\n\t\t\t// Platform.invokeLater(new Runnable() {\n\t\t\t//\n\t\t\t// @Override\n\t\t\t// public void run() {\n\t\t\t// // automatically scroll down to the last line\n\t\t\t//// view.scrollRectToVisible(\n\t\t\t//// new Rectangle(0, view.getHeight() - 1, 1, 1));\n\t\t\t//\n\t\t\t// }\n\t\t\t// });\n\t\t}\n\n\t\tif (isRedirectToStdOut()) {\n\t\t\tsuper.write(buf, off, len);\n\t\t}\n\t}\n\n\tpublic final void addView(TextArea view) {\n\t\tviews.add(view);\n\t\tfilters.add((OutputFilter) (String s) -> {\n\t\t\treturn true;\n\t\t});\n\t}\n\n\tpublic void setFilter(TextArea view, OutputFilter filter) {\n\t\tint i = views.indexOf(view);\n\t\tfilters.set(i, filter);\n\t}\n\n\t/**\n\t * @return the redirectToUi\n\t */\n\tpublic boolean isRedirectToUi() {\n\t\treturn redirectToUi;\n\t}\n\n\t/**\n\t * @param redirectToUi\n\t *            the redirectToUi to set\n\t */\n\tpublic void setRedirectToUi(boolean redirectToUi) {\n\t\tthis.redirectToUi = redirectToUi;\n\t}\n\n\t/**\n\t * @return the redirectToStdOut\n\t */\n\tpublic boolean isRedirectToStdOut() {\n\t\treturn redirectToStdOut;\n\t}\n\n\t/**\n\t * @param redirectToStdOut\n\t *            the redirectToStdOut to set\n\t */\n\tpublic final void setRedirectToStdOut(boolean redirectToStdOut) {\n\t\tthis.redirectToStdOut = redirectToStdOut;\n\t}\n\n\tprivate static void invokeAndWait(Runnable r) {\n\t\tif (Platform.isFxApplicationThread()) {\n\t\t\tr.run();\n\t\t} else {\n\t\t\tFutureTask<Boolean> task = new FutureTask<>(r, true);\n\n\t\t\tBowlerStudio.runLater(task);\n\n\t\t\ttry {\n\t\t\t\ttask.get(); // like join()\n\t\t\t} catch (InterruptedException | ExecutionException ex) {\n\t\t\t\tLogger.getLogger(RedirectableStream.class.getName()).log(Level.SEVERE, null, ex);\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/RpcCommandPanel.java",
    "content": "package com.neuronrobotics.bowlerstudio;\n\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.util.ArrayList;\n\nimport javafx.scene.control.CheckBoxTreeItem;\n\nimport javax.swing.JButton;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\nimport javax.swing.JTextField;\n\nimport net.miginfocom.swing.MigLayout;\n\nimport com.neuronrobotics.sdk.common.BowlerAbstractDevice;\nimport com.neuronrobotics.sdk.common.BowlerDataType;\nimport com.neuronrobotics.sdk.common.BowlerDatagram;\nimport com.neuronrobotics.sdk.common.RpcEncapsulation;\n\npublic class RpcCommandPanel extends JPanel implements ActionListener {\n\n\t/**\n\t *\n\t */\n\tprivate static final long serialVersionUID = -9199252749669892888L;\n\tprivate BowlerAbstractDevice device;\n\tprivate RpcEncapsulation rpc;\n\tprivate CheckBoxTreeItem<?> rpcDhild;\n\tprivate boolean commandsEnabled = false;\n\tprivate ArrayList<JTextField> tx = new ArrayList<>();\n\tprivate ArrayList<JLabel> rx = new ArrayList<>();\n\tprivate JButton send = new JButton(\"Send\");\n\tprivate JLabel txRpc = new JLabel(\"****\");\n\tprivate JLabel rxRpc = new JLabel(\"****\");\n\n\tpublic RpcCommandPanel(RpcEncapsulation rpc, BowlerAbstractDevice device, CheckBoxTreeItem<?> rpcDhild) {\n\t\tthis.setRpcDhild(rpcDhild);\n\t\tthis.setRpc(rpc);\n\t\tthis.setDevice(device);\n\t\tsetLayout(new MigLayout());\n\t\tadd(new JLabel(\"Namespace\"), \"cell 0 0,alignx leading\");\n\t\tadd(new JLabel(rpc.getNamespace().split(\";\")[0]), \"cell 1 0,alignx leading\");\n\n\t\tadd(new JLabel(\"Method\"), \"cell 0 1,alignx leading\");\n\t\tadd(new JLabel(rpc.getDownstreamMethod().toString()), \"cell 1 1,alignx leading\");\n\n\t\tadd(new JLabel(\"RPC\"), \"cell 0 2,alignx leading\");\n\t\tadd(new JLabel(rpc.getRpc()), \"cell 1 2,alignx leading\");\n\n\t\tadd(new JLabel(\"Tx>>\"), \"cell 0 3,alignx leading\");\n\t\ttxRpc.setText(rpc.getRpc());\n\t\tadd(txRpc, \"cell 0 3,alignx leading\");\n\t\tadd(new JLabel(\"Rx<<\"), \"cell 0 4,alignx leading\");\n\t\tadd(rxRpc, \"cell 0 4,alignx leading\");\n\t\tadd(send, \"cell 2 3,alignx leading\");\n\n\t\tJPanel txPanel = new JPanel(new MigLayout());\n\t\tJPanel rxPanel = new JPanel(new MigLayout());\n\n\t\tint i = 0;\n\t\tfor (BowlerDataType s : rpc.getDownstreamArguments()) {\n\t\t\tJTextField tmp = new JTextField(5);\n\t\t\ttmp.setText(\"0\");\n\t\t\ttxPanel.add(new JLabel(s.toString()), \"cell \" + i + \" 0,alignx leading\");\n\t\t\ttx.add(tmp);\n\t\t\ti++;\n\t\t}\n\t\ti = 0;\n\t\tfor (BowlerDataType s : rpc.getUpstreamArguments()) {\n\t\t\tJLabel tmp = new JLabel();\n\t\t\ttmp.setText(\"0\");\n\t\t\trxPanel.add(new JLabel(s.toString()), \"cell \" + i + \" 0,alignx leading\");\n\t\t\trx.add(tmp);\n\t\t\ti++;\n\t\t}\n\t\ti = 0;\n\t\tfor (JTextField t : tx) {\n\t\t\ttxPanel.add(t, \"cell \" + i + \" 1,alignx leading\");\n\t\t\ti++;\n\t\t}\n\t\ti = 0;\n\t\tfor (JLabel t : rx) {\n\t\t\trxPanel.add(t, \"cell \" + i + \" 1,alignx leading\");\n\t\t\ti++;\n\t\t}\n\n\t\tadd(txPanel, \"cell 1 3,alignx leading\");\n\t\tadd(rxPanel, \"cell 1 4,alignx leading\");\n\n\t\tsend.addActionListener(this);\n\t}\n\n\tpublic RpcEncapsulation getRpc() {\n\t\treturn rpc;\n\t}\n\n\tprivate void setRpc(RpcEncapsulation rpc) {\n\t\tthis.rpc = rpc;\n\t}\n\n\tpublic BowlerAbstractDevice getDevice() {\n\t\treturn device;\n\t}\n\n\tprivate void setDevice(BowlerAbstractDevice device) {\n\t\tthis.device = device;\n\t}\n\n\tpublic CheckBoxTreeItem<?> getRpcDhild() {\n\t\treturn rpcDhild;\n\t}\n\n\tpublic void setRpcDhild(CheckBoxTreeItem<?> rpcDhild) {\n\t\tthis.rpcDhild = rpcDhild;\n\t}\n\n\tpublic void enableCommands() {\n\t\tif (commandsEnabled)\n\t\t\treturn;\n\n\t\tcommandsEnabled = true;\n\t}\n\n\t@Override\n\tpublic void actionPerformed(ActionEvent arg0) {\n\t\tObject[] values = new Object[tx.size()];\n\t\tfor (int i = 0; i < values.length; i++) {\n\t\t\tif (rpc.getDownstreamArguments()[i] == BowlerDataType.ASCII)\n\t\t\t\tvalues[i] = tx.get(i).getText();\n\t\t\telse\n\t\t\t\tvalues[i] = Integer.parseInt(tx.get(i).getText());\n\t\t}\n\t\tBowlerDatagram bd = device.send(rpc.getCommand(values));\n\t\trxRpc.setText(bd.getRPC());\n\t\tObject[] up = rpc.parseResponse(bd);\n\t\tfor (int i = 0; i < up.length; i++) {\n\t\t\tif (up[i] != null)\n\t\t\t\trx.get(i).setText(up[i].toString());\n\t\t\telse {\n\t\t\t\trx.get(i).setText(\"Null\");\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/SplashManager.java",
    "content": "package com.neuronrobotics.bowlerstudio;\n\nimport java.awt.Graphics2D;\nimport java.util.function.BooleanSupplier;\n\nimport org.jfree.util.Log;\n\npublic class SplashManager {\n\tprivate static Graphics2D splashGraphics;\n\n\tprivate static boolean loadFirst = true;\n\tprivate static BooleanSupplier closePreventer = () -> false;\n\tpublic static void closeSplash() {\n\t\tif (isVisibleSplash())\n\t\t\tcloseSplashLocal();\n\n\t}\n\n\tprivate static void closeSplashLocal() {\n\t\tif (BowlerStudio.splash != null) {\n\t\t\tBowlerStudio.splash.close();\n\t\t\tsplashGraphics = null;\n\t\t\treturn;\n\t\t}\n\t\tif (closePreventer.getAsBoolean()) {\n\t\t\tLog.debug(\"Close prevented by \" + closePreventer);\n\t\t\treturn;\n\t\t}\n\t\tPsudoSplash.close();\n\t}\n\n\tpublic static boolean isVisibleSplash() {\n\t\tif (BowlerStudio.splash != null)\n\t\t\treturn BowlerStudio.splash.isVisible();\n\t\tif (!PsudoSplash.isInitialized())\n\t\t\treturn false;\n\t\treturn PsudoSplash.isVisibleSplash();\n\t}\n\n\tprivate static void updateSplash() {\n\t\tPsudoSplash.get().updateSplash();\n\t}\n\n\tpublic static void renderSplashFrame(int percent, String message) {\n\t\tif (loadFirst) {\n\n\t\t\tinitialize();\n\t\t}\n\t\tString string = percent + \"% \" + message;\n\t\t// com.neuronrobotics.sdk.common.Log.debug(\" Splash Rendering \" + percent + \" \"\n\t\t// + message);\n\t\tPsudoSplash.get().setMessage(string);\n\t\twaitForUpdate();\n\t}\n\n\tpublic static void onLogUpdate(String message) {\n\t\tif (loadFirst) {\n\n\t\t\tinitialize();\n\t\t}\n\t\tPsudoSplash.get().onLogUpdate(message, null);\n\t\twaitForUpdate();\n\t}\n\n\tprivate static void waitForUpdate() {\n\t\tupdateSplash();\n\n\t\t// if (Platform.isFxApplicationThread())\n\t\t// throw new RuntimeException(\"Splash manager can not be opened from a javafx\n\t\t// thread!\");\n\t\tint index = 0;\n\t\twhile (!SplashManager.isVisibleSplash()) {\n\t\t\tcom.neuronrobotics.sdk.common.Log.debug(\"Waiting for splash to open before moving on\");\n\t\t\ttry {\n\t\t\t\tThread.sleep(100);\n\t\t\t\tindex++;\n\t\t\t} catch (InterruptedException e) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (index > 10)\n\t\t\t\treturn;\n\t\t}\n\t}\n\n\tprivate static void initialize() {\n\t\tcom.neuronrobotics.sdk.common.Log.error(\"No splash screen available!\");\n\n\t\tloadFirst = false;\n\t}\n\n\tpublic static BooleanSupplier getClosePreventer() {\n\t\treturn closePreventer;\n\t}\n\n\tpublic static void setClosePreventer(BooleanSupplier cp) {\n\t\tclosePreventer = cp;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/Terminal.java",
    "content": "package com.neuronrobotics.bowlerstudio;\n/**\n * Sample Skeleton for \"Terminal.fxml\" Controller Class\n * You can copy and paste this code into your favorite IDE\n **/\n\nimport com.neuronrobotics.bowlerstudio.assets.AssetFactory;\nimport com.neuronrobotics.bowlerstudio.assets.FontSizeManager;\nimport com.neuronrobotics.bowlerstudio.scripting.ScriptingEngine;\n//import com.neuronrobotics.imageprovider.OpenCVImageProvider;\nimport com.neuronrobotics.sdk.common.Log;\nimport com.neuronrobotics.sdk.util.ThreadUtil;\n\nimport eu.mihosoft.vrl.v3d.parametrics.CSGDatabase;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\nimport javafx.fxml.FXML;\nimport javafx.scene.control.ComboBox;\nimport javafx.scene.control.TextArea;\nimport javafx.scene.control.TextField;\nimport javafx.scene.image.Image;\nimport javafx.scene.image.ImageView;\nimport javafx.scene.input.KeyCode;\nimport javafx.scene.input.KeyEvent;\n\nimport java.io.IOException;\nimport java.io.PrintWriter;\nimport java.io.StringWriter;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.ResourceBundle;\n\npublic class Terminal {\n\n\t@FXML // ResourceBundle that was given to the FXMLLoader\n\tprivate ResourceBundle resources;\n\n\t@FXML // URL location of the FXML file that was given to the FXMLLoader\n\tprivate URL location;\n\n\t@FXML // fx:id=\"executionBox\"\n\tprivate TextField executionBox; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"langaugeIcon\"\n\tprivate ImageView langaugeIcon; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"langauges\"\n\tprivate ComboBox<String> langauges; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"outputBox\"\n\tprivate TextArea outputBox; // Value injected by FXMLLoader\n\n\tprivate ArrayList<String> history = new ArrayList<>();\n\n\tprivate int historyIndex = 0;\n\tprivate boolean running = false;\n\tprivate Thread scriptRunner = null;\n\n\t@SuppressWarnings(\"restriction\")\n\t@FXML // This method is called by the FXMLLoader when initialization is\n\t\t\t\t\t\t\t\t\t\t\t// complete\n\tvoid initialize() {\n\t\tassert executionBox != null : \"fx:id=\\\"executionBox\\\" was not injected: check your FXML file 'Terminal.fxml'.\";\n\t\tassert langaugeIcon != null : \"fx:id=\\\"langaugeIcon\\\" was not injected: check your FXML file 'Terminal.fxml'.\";\n\t\tassert langauges != null : \"fx:id=\\\"langauges\\\" was not injected: check your FXML file 'Terminal.fxml'.\";\n\t\tassert outputBox != null : \"fx:id=\\\"outputBox\\\" was not injected: check your FXML file 'Terminal.fxml'.\";\n\t\tBowlerStudio.setLogViewRefStatic(outputBox);\n\t\t// Initialize your logic here: all @FXML variables will have been\n\t\t// injected\n\t\tlangauges.getItems().clear();\n\t\texecutionBox.setOnAction(event -> {\n\t\t\tnew Thread() {\n\t\t\t\tpublic void run() {\n\t\t\t\t\tThread.currentThread().setUncaughtExceptionHandler(new IssueReportingExceptionHandler());\n\n\t\t\t\t\tstartStopAction();\n\t\t\t\t}\n\t\t\t}.start();\n\n\t\t});\n\t\texecutionBox.setPrefWidth(80 * 4);\n\t\texecutionBox.addEventFilter(KeyEvent.KEY_PRESSED, event -> {\n\t\t\t// BowlerStudio.runLater(() -> {\n\t\t\tif ((event.getCode() == KeyCode.UP || event.getCode() == KeyCode.DOWN)) {\n\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Key pressed \" + event.getCode() + \" history index = \"\n\t\t\t\t\t\t+ historyIndex + \" history size= \" + history.size());\n\t\t\t\tif (historyIndex == 0) {\n\t\t\t\t\tString text = executionBox.getText();\n\t\t\t\t\tif (text.length() > 0) {\n\t\t\t\t\t\t// store what was in the box into he history\n\t\t\t\t\t\thistory.add(text);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (event.getCode() == KeyCode.UP)\n\t\t\t\t\thistoryIndex++;\n\t\t\t\telse\n\t\t\t\t\thistoryIndex--;\n\t\t\t\tif (!history.isEmpty()) {\n\t\t\t\t\tif (historyIndex > history.size()) {\n\t\t\t\t\t\thistoryIndex = history.size();\n\t\t\t\t\t}\n\t\t\t\t\tif (historyIndex < 0)\n\t\t\t\t\t\thistoryIndex = 0;\n\t\t\t\t\t// History index established\n\t\t\t\t\tif (historyIndex > 0)\n\t\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\t\texecutionBox.setText(history.get(history.size() - historyIndex));\n\t\t\t\t\t\t});\n\t\t\t\t\telse\n\t\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\t\texecutionBox.setText(\"\");\n\t\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tevent.consume();\n\t\t\t}\n\t\t\t// });\n\t\t});\n\t\ttry {\n\t\t\thistory = BowlerKernel.loadHistory();\n\t\t} catch (IOException e1) {\n\t\t\t// Auto-generated catch block\n\t\t\t// e1.printStackTrace();\n\t\t}\n\t\tRuntime.getRuntime().addShutdownHook(new Thread() {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\n\t\t\t\tBowlerKernel.writeHistory(history);\n\t\t\t}\n\t\t});\n\t\tList<String> langs = ScriptingEngine.getAllLangauges();\n\t\tObservableList<String> options = FXCollections.observableArrayList(langs);\n\t\t//\n\t\tfor (String s : options) {\n\t\t\tlangauges.getItems().add(s);\n\t\t}\n\t\tlangauges.getSelectionModel().select(\"Groovy\");\n\t\tImage icon;\n\t\ttry {\n\t\t\ticon = AssetFactory.loadAsset(\"Script-Tab-\" + langauges.getSelectionModel().getSelectedItem() + \".png\");\n\t\t\tlangaugeIcon.setImage(icon);\n\t\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\t\tlangaugeIcon.setScaleX(FontSizeManager.getImageScale());\n\t\t\t\tlangaugeIcon.setScaleY(FontSizeManager.getImageScale());\n\t\t\t});\n\t\t} catch (Exception e2) {\n\t\t\t// Auto-generated catch block\n\t\t\te2.printStackTrace();\n\t\t}\n\n\t\tlangauges.setOnAction(event -> {\n\t\t\ttry {\n\n\t\t\t\tlangaugeIcon.setImage(AssetFactory\n\t\t\t\t\t\t.loadAsset(\"Script-Tab-\" + langauges.getSelectionModel().getSelectedItem() + \".png\"));\n\t\t\t} catch (Exception e1) {\n\t\t\t\t// Auto-generated catch block\n\t\t\t\te1.printStackTrace();\n\t\t\t}\n\n\t\t});\n\n\t}\n\n\tprivate void reset() {\n\t\trunning = false;\n\n\t}\n\n\tprivate void start(String code) {\n\n\t\trunning = true;\n\n\t\tscriptRunner = new Thread() {\n\n\t\t\t@SuppressWarnings(\"restriction\")\n\t\t\tpublic void run() {\n\n\t\t\t\ttry {\n\t\t\t\t\tScriptingEngine.inlineScriptStringRun(CSGDatabase.getInstance(), code, null,\n\t\t\t\t\t\t\tlangauges.getSelectionModel().getSelectedItem());\n\t\t\t\t\treset();\n\t\t\t\t} catch (groovy.lang.MissingPropertyException | org.python.core.PyException d) {\n\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\tStringWriter sw = new StringWriter();\n\t\t\t\t\t\tPrintWriter pw = new PrintWriter(sw);\n\t\t\t\t\t\td.printStackTrace(pw);\n\t\t\t\t\t\tBowlerStudioController.highlightException(null, d);\n\n\t\t\t\t\t\treset();\n\t\t\t\t\t});\n\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Script exception of type= \" + ex.getClass().getName());\n\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\treset();\n\t\t\t\t\t});\n\n\t\t\t\t\tBowlerStudioController.highlightException(null, ex);\n\t\t\t\t}\n\n\t\t\t}\n\t\t};\n\n\t\tscriptRunner.start();\n\n\t}\n\n\tprivate void startStopAction() {\n\t\tString text = executionBox.getText();\n\t\ttext += \"\\r\\n\";\n\t\tBowlerStudio.runLater(() -> {\n\t\t\texecutionBox.setText(\"\");\n\t\t});\n\t\tcom.neuronrobotics.sdk.common.Log.error(text);\n\t\thistory.add(text);\n\t\tBowlerKernel.writeHistory(history);\n\t\tif (historyIndex != 0)\n\t\t\thistoryIndex--;\n\t\telse\n\t\t\thistoryIndex = 0;\n\t\tif (running)\n\t\t\tstop();\n\t\telse\n\t\t\tstart(text);\n\t}\n\n\tpublic void stop() {\n\t\t// Auto-generated method stub\n\n\t\treset();\n\t\tif (scriptRunner != null)\n\t\t\twhile (scriptRunner.isAlive()) {\n\n\t\t\t\tLog.debug(\"Interrupting\");\n\t\t\t\tThreadUtil.wait(10);\n\t\t\t\ttry {\n\t\t\t\t\tscriptRunner.interrupt();\n\t\t\t\t\tscriptRunner.join();\n\t\t\t\t} catch (InterruptedException e) {\n\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\n\t\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/TestServer.java",
    "content": "package com.neuronrobotics.bowlerstudio;\n\nimport java.io.IOException;\n\nimport com.neuronrobotics.sdk.common.BowlerAbstractDevice;\nimport com.neuronrobotics.sdk.common.BowlerDataType;\nimport com.neuronrobotics.sdk.common.BowlerDatagram;\nimport com.neuronrobotics.sdk.common.BowlerMethod;\nimport com.neuronrobotics.sdk.common.DeviceManager;\nimport com.neuronrobotics.sdk.common.Log;\nimport com.neuronrobotics.sdk.common.MACAddress;\nimport com.neuronrobotics.sdk.common.RpcEncapsulation;\nimport com.neuronrobotics.sdk.common.device.server.BowlerAbstractDeviceServerNamespace;\nimport com.neuronrobotics.sdk.common.device.server.BowlerAbstractServer;\nimport com.neuronrobotics.sdk.common.device.server.IBowlerCommandProcessor;\nimport com.neuronrobotics.sdk.network.BowlerTCPClient;\n\npublic class TestServer {\n\n\tprivate TestServer() {\n\t}\n\n\tpublic static void main(String[] args) throws Exception {\n\t\tclass SampleBowlerServer extends BowlerAbstractServer {\n\t\t\tBowlerAbstractDeviceServerNamespace ns = new BowlerAbstractDeviceServerNamespace(getMacAddress(),\n\t\t\t\t\t\"test.thingy.*;0.3;;\") {\n\t\t\t};\n\n\t\t\tpublic SampleBowlerServer() {\n\t\t\t\tsuper(new MACAddress());\n\n\t\t\t\tns.addRpc(new RpcEncapsulation(ns.getNamespaceIndex(), ns.getNamespace(), \"test\", BowlerMethod.GET,\n\t\t\t\t\t\tnew BowlerDataType[]{BowlerDataType.I32, BowlerDataType.I32, BowlerDataType.I32}, // send 3\n\t\t\t\t\t\t// integers\n\t\t\t\t\t\tBowlerMethod.POST,\n\t\t\t\t\t\tnew BowlerDataType[]{BowlerDataType.I32, BowlerDataType.I32, BowlerDataType.I32}, // get 3\n\t\t\t\t\t\t// integers\n\t\t\t\t\t\t// back\n\t\t\t\t\t\tnew IBowlerCommandProcessor() {\n\t\t\t\t\t\t\tpublic Object[] process(Object[] data) {\n\t\t\t\t\t\t\t\tfor (int i = 0; i < data.length; i++) {\n\t\t\t\t\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Server Got # \" + data[i]);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\treturn new Object[]{37, 42, 999999};\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}));\n\t\t\t\taddBowlerDeviceServerNamespace(ns);\n\n\t\t\t\tLog.info(\"Starting UDP\");\n\t\t\t\ttry {\n\t\t\t\t\tstartNetworkServer(1865);\n\t\t\t\t} catch (IOException e) {\n\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t} // starts the UDP server\n\t\t\t\t\t// this also starts tcp server on port+1, in this case 1866\n\n\t\t\t}\n\t\t}\n\n\t\tclass SampleBowlerClient extends BowlerAbstractDevice {\n\n\t\t\tpublic void runCommand() {\n\t\t\t\tObject[] args = send(\"test.thingy.*;0.3;;\", BowlerMethod.GET, \"test\", new Object[]{36, 83, 13});// send\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// some\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// numbers\n\t\t\t\tfor (int i = 0; i < args.length; i++) {\n\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Client Received  # \" + args[i]);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onAsyncResponse(BowlerDatagram data) {\n\t\t\t}// no async in this demo\n\t\t}\n\n\t\tSampleBowlerClient client = new SampleBowlerClient();\n\n\t\t// client.setConnection(new\n\t\t// UDPBowlerConnection(InetAddress.getByName(\"127.0.0.1\"), 1865));\n\t\t// Alternately you can use the tcp connection\n\t\tclient.setConnection(new BowlerTCPClient(\"127.0.0.1\", 1866));\n\t\tDeviceManager.addConnection(client, \"sampleClient\");\n\n\t\tclient.runCommand();// runs our test command from client to server and\n\t\t\t\t\t\t\t// back\n\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/Tutorial.java",
    "content": "package com.neuronrobotics.bowlerstudio;\n\nimport java.io.File;\nimport org.eclipse.jetty.server.Connector;\nimport org.eclipse.jetty.server.Handler;\nimport org.eclipse.jetty.server.Server;\nimport org.eclipse.jetty.server.ServerConnector;\nimport org.eclipse.jetty.server.handler.DefaultHandler;\nimport org.eclipse.jetty.server.handler.HandlerList;\nimport org.eclipse.jetty.server.handler.ResourceHandler;\nimport com.neuronrobotics.bowlerstudio.assets.ConfigurationDatabase;\nimport com.neuronrobotics.bowlerstudio.scripting.ScriptingEngine;\nimport com.neuronrobotics.sdk.util.ThreadUtil;\nimport com.neuronrobotics.video.OSUtil;\n\npublic class Tutorial {\n\tprivate static int WEBSERVER_PORT = 37037;\n\tprivate static final String HOME_Local_URL_ROOT = \"/BowlerStudio/Welcome-To-BowlerStudio/\";\n\tprivate static final String weburl = \"http://CommonWealthRobotics.com\" + HOME_Local_URL_ROOT;\n\tprivate static String HOME_URL = null;\n\tprivate static String HOME_Local_URL = null;\n\tprivate static boolean doneLoadingTutorials;\n\tprivate static Boolean startedLoadingTutorials = false;\n\tpublic static String getHomeUrl() throws Exception {\n\t\tConfigurationDatabase.setObject(\"BowlerStudioConfigs\", \"tutorialBranch\", \"deploy\");\n\t\tif (OSUtil.isOSX()) {\n\t\t\tConfigurationDatabase.setObject(\"BowlerStudioConfigs\", \"tutorialSource\", weburl);\n\t\t\treturn weburl;\n\t\t}\n\t\tFile i = null;\n\t\tString remoteURI = (String) ConfigurationDatabase.getObject(\"BowlerStudioConfigs\", \"tutorialSource\",\n\t\t\t\t\"https://github.com/CommonWealthRobotics/CommonWealthRobotics.github.io.git\");\n\t\tdo {\n\n\t\t\ti = ScriptingEngine.fileFromGit(remoteURI,\n\t\t\t\t\t(String) ConfigurationDatabase.getObject(\"BowlerStudioConfigs\", \"tutorialBranch\", \"deploy\"), // the\n\t\t\t\t\t// default\n\t\t\t\t\t// branch\n\t\t\t\t\t// is\n\t\t\t\t\t// source,\n\t\t\t\t\t// so\n\t\t\t\t\t// this\n\t\t\t\t\t// needs\n\t\t\t\t\t// to\n\t\t\t\t\t// be specified\n\t\t\t\t\t\"docs/index.html\");\n\t\t} while (!i.exists());\n\t\tScriptingEngine.pull(remoteURI);\n\t\tFile indexOfTutorial = i;\n\t\tif (!doneLoadingTutorials) {\n\t\t\tif (!startedLoadingTutorials) {\n\t\t\t\t// synchronized(startedLoadingTutorials){\n\t\t\t\tstartedLoadingTutorials = true;\n\t\t\t\t// }\n\t\t\t\tnew Thread() {\n\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\tThread.currentThread().setUncaughtExceptionHandler(new IssueReportingExceptionHandler());\n\n\t\t\t\t\t\t// HOME_Local_URL = indexOfTutorial.toURI().toString().replace(\"file:/\",\n\t\t\t\t\t\t// \"file:///\");\n\t\t\t\t\t\tServer server = new Server(WEBSERVER_PORT);\n\t\t\t\t\t\tServerConnector connector = new ServerConnector(server);\n\t\t\t\t\t\tserver.setConnectors(new Connector[]{connector});\n\t\t\t\t\t\tResourceHandler resource_handler = new ResourceHandler();\n\t\t\t\t\t\tresource_handler.setDirectoriesListed(true);\n\t\t\t\t\t\tresource_handler.setWelcomeFiles(new String[]{\"index.html\"});\n\t\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Serving \" + indexOfTutorial.getParent());\n\t\t\t\t\t\tresource_handler.setResourceBase(indexOfTutorial.getParent());\n\n\t\t\t\t\t\tHandlerList handlers = new HandlerList();\n\t\t\t\t\t\thandlers.setHandlers(new Handler[]{resource_handler, new DefaultHandler()});\n\t\t\t\t\t\tserver.setHandler(handlers);\n\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tserver.start();\n\t\t\t\t\t\t\tWEBSERVER_PORT = connector.getLocalPort();\n\t\t\t\t\t\t\tHOME_Local_URL = \"http://localhost:\" + WEBSERVER_PORT + HOME_Local_URL_ROOT;\n\t\t\t\t\t\t\tdoneLoadingTutorials = true;\n\t\t\t\t\t\t\tserver.join();\n\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\tthrow new RuntimeException(e);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\t\t\t\t}.start();\n\t\t\t}\n\t\t\tlong start = System.currentTimeMillis();\n\t\t\t// wait up to 30 seconds for menue to load, then fail over to the web version\n\t\t\twhile (!doneLoadingTutorials && (System.currentTimeMillis() - start < 3000)) {\n\t\t\t\tThreadUtil.wait(100);\n\t\t\t}\n\n\t\t\tif (doneLoadingTutorials)\n\t\t\t\tHOME_URL = HOME_Local_URL;\n\t\t\telse\n\t\t\t\tHOME_URL = weburl;\n\t\t}\n\t\treturn HOME_URL;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/assets/BowlerStudioResourceFactory.java",
    "content": "package com.neuronrobotics.bowlerstudio.assets;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport org.eclipse.jgit.api.errors.GitAPIException;\nimport org.eclipse.jgit.api.errors.InvalidRemoteException;\nimport org.eclipse.jgit.api.errors.TransportException;\n\nimport com.neuronrobotics.bowlerstudio.BowlerStudio;\nimport com.neuronrobotics.bowlerstudio.scripting.GithubLoginFX;\n//import com.neuronrobotics.nrconsole.plugin.DyIO.DyIOConsole;\nimport com.neuronrobotics.sdk.dyio.DyIOChannelMode;\n\nimport javafx.fxml.FXMLLoader;\nimport javafx.scene.image.Image;\n\npublic class BowlerStudioResourceFactory {\n\tprivate static final Map<DyIOChannelMode, Image> lookup = new HashMap<>();\n\tprivate static Image chanHighlight;\n\tprivate static Image chanUpdate;\n\tprivate static Image chanDefault;\n\tprivate static final ArrayList<FXMLLoader> fxmlLoaders = new ArrayList<>();\n\tprivate static FXMLLoader githubLogin;\n\tprivate static FXMLLoader mainControllerPanel;\n\tprivate static boolean loaded = false;\n\tprivate BowlerStudioResourceFactory() {\n\t}\n\n\tpublic static FXMLLoader getLoader(int channelIndex) {\n\t\treturn fxmlLoaders.get(channelIndex);\n\t}\n\n\t@SuppressWarnings(\"restriction\")\n\tpublic static void load() throws Exception {\n\t\tif (loaded)\n\t\t\treturn;\n\t\tloaded = true;\n\t\ttry {\n\t\t\t// mainPanel.setController(new DyIOPanel());\n\t\t\tBowlerStudio.renderSplashFrame(95, \"Loading GitHub\");\n\n\t\t\tgithubLogin = AssetFactory.loadLayout(\"layout/githublogin.fxml\");\n\t\t\t// githubLogin.setController(new GithubLoginFX());\n\t\t\tgithubLogin.setClassLoader(GithubLoginFX.class.getClassLoader());\n\t\t} catch (InvalidRemoteException e1) {\n\t\t\t// Auto-generated catch block\n\t\t\tcom.neuronrobotics.sdk.common.Log.error(e1);\n\t\t} catch (TransportException e1) {\n\t\t\t// Auto-generated catch block\n\t\t\tcom.neuronrobotics.sdk.common.Log.error(e1);\n\t\t} catch (GitAPIException e1) {\n\t\t\t// Auto-generated catch block\n\t\t\tcom.neuronrobotics.sdk.common.Log.error(e1);\n\t\t} catch (IOException e1) {\n\t\t\t// Auto-generated catch block\n\t\t\tcom.neuronrobotics.sdk.common.Log.error(e1);\n\t\t} catch (Exception e1) {\n\t\t\t// Auto-generated catch block\n\t\t\tcom.neuronrobotics.sdk.common.Log.error(e1);\n\t\t}\n\n\t\ttry {\n\t\t\tgithubLogin.load();\n\t\t\tjavafx.scene.Parent root = githubLogin.getRoot();\n\t\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\t\tint tmp = fontNum - 10;\n\t\t\t\tif (tmp < 12)\n\t\t\t\t\ttmp = 12;\n\t\t\t\troot.setStyle(\"-fx-font-size: \" + tmp + \"pt\");\n\t\t\t});\n\t\t} catch (IOException e) {\n\t\t\tLogger.getLogger(BowlerStudio.class.getName()).log(Level.SEVERE, null, e);\n\t\t}\n\n\t}// stub to force a load from the static in a specific thread\n\n\tpublic static Image getModeImage(DyIOChannelMode mode) {\n\t\treturn lookup.get(mode);\n\t}\n\n\tpublic static Image getChanHighlight() {\n\t\treturn chanHighlight;\n\t}\n\n\tpublic static void setChanHighlight(Image chanHighlight) {\n\t\tBowlerStudioResourceFactory.chanHighlight = chanHighlight;\n\t}\n\n\tpublic static Image getChanUpdate() {\n\t\treturn chanUpdate;\n\t}\n\n\tpublic static void setChanUpdate(Image chanUpdate) {\n\t\tBowlerStudioResourceFactory.chanUpdate = chanUpdate;\n\t}\n\n\tpublic static Image getChanDefault() {\n\t\treturn chanDefault;\n\t}\n\n\tpublic static void setChanDefault(Image chanDefault) {\n\t\tBowlerStudioResourceFactory.chanDefault = chanDefault;\n\t}\n\n\tpublic static FXMLLoader getGithubLogin() {\n\t\treturn githubLogin;\n\t}\n\n\tpublic static void setGithubLogin(FXMLLoader githubLogin) {\n\t\tBowlerStudioResourceFactory.githubLogin = githubLogin;\n\t}\n\n\tpublic static FXMLLoader getMainControllerPanel() {\n\t\treturn mainControllerPanel;\n\t}\n\n\tpublic static void setMainControllerPanel(FXMLLoader mainControllerPanel) {\n\t\tBowlerStudioResourceFactory.mainControllerPanel = mainControllerPanel;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/creature/AbstractGameController.java",
    "content": "package com.neuronrobotics.bowlerstudio.creature;\n\nimport java.util.ArrayList;\n\npublic abstract class AbstractGameController {\n\tprivate ArrayList<IGameControllerUpdateListener> listeners = new ArrayList<>();\n\n\tpublic void addIGameControllerUpdateListener(IGameControllerUpdateListener l) {\n\t\tif (!listeners.contains(l))\n\t\t\tlisteners.add(l);\n\t}\n\n\tpublic void removeIGameControllerUpdateListener(IGameControllerUpdateListener l) {\n\t\tif (listeners.contains(l))\n\t\t\tlisteners.remove(l);\n\t}\n\n\tpublic void clearIGameControllerUpdateListener() {\n\t\tlisteners.clear();\n\t}\n\n\tprotected void fireGameControllerUpdate() {\n\n\t}\n\n\tpublic abstract double getControls0Plus();\n\n\tpublic abstract double getControls0Minus();\n\n\tpublic abstract double getControls1Plus();\n\n\tpublic abstract double getControls1Minus();\n\n\tpublic abstract double getControls2Plus();\n\n\tpublic abstract double getControls2Minus();\n\n\tpublic abstract double getControls3Plus();\n\n\tpublic abstract double getControls3Minus();\n\n\tpublic abstract double getNavUp();\n\n\tpublic abstract double getNavDown();\n\n\tpublic abstract double getNavLeft();\n\n\tpublic abstract double getNavRight();\n\n\tpublic abstract double getActionLeft();\n\n\tpublic abstract double getActionRight();\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/creature/AdjustbodyMassWidget.java",
    "content": "package com.neuronrobotics.bowlerstudio.creature;\n\nimport com.neuronrobotics.sdk.addons.kinematics.MobileBase;\nimport com.neuronrobotics.sdk.addons.kinematics.math.TransformNR;\n\nimport eu.mihosoft.vrl.v3d.parametrics.CSGDatabase;\nimport javafx.scene.Group;\nimport javafx.scene.control.TextField;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.text.Text;\n\npublic class AdjustbodyMassWidget extends Group {\n\n\tprivate MobileBase device;\n\tprivate MobileBaseCadManager manager;\n\tdouble textToNum(TextField mass) {\n\t\ttry {\n\t\t\treturn Double.parseDouble(mass.getText().trim());\n\t\t} catch (Throwable t) {\n\t\t\tmass.setText(\"0\");\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tpublic AdjustbodyMassWidget(MobileBase device) {\n\t\tthis.device = device;\n\t\tmanager = MobileBaseCadManager.get(CSGDatabase.getInstance(), device);\n\t\tGridPane pane = new GridPane();\n\n\t\tTextField mass = new TextField(CreatureLab.getFormatted(device.getMassKg()));\n\t\tmass.setOnAction(event -> {\n\t\t\tdevice.setMassKg(textToNum(mass));\n\t\t\tif (manager != null)\n\t\t\t\tmanager.generateCad(CSGDatabase.getInstance());\n\t\t});\n\t\tTransformNR currentCentroid = device.getCenterOfMassFromCentroid();\n\t\tTextField massx = new TextField(CreatureLab.getFormatted(currentCentroid.getX()));\n\t\tmassx.setOnAction(event -> {\n\t\t\tcurrentCentroid.setX(textToNum(massx));\n\t\t\tdevice.setCenterOfMassFromCentroid(currentCentroid);;\n\t\t\tif (manager != null)\n\t\t\t\tmanager.generateCad(CSGDatabase.getInstance());\n\n\t\t});\n\n\t\tTextField massy = new TextField(CreatureLab.getFormatted(currentCentroid.getY()));\n\t\tmassy.setOnAction(event -> {\n\t\t\tcurrentCentroid.setY(textToNum(massy));\n\t\t\tdevice.setCenterOfMassFromCentroid(currentCentroid);;\n\t\t\tif (manager != null)\n\t\t\t\tmanager.generateCad(CSGDatabase.getInstance());\n\n\t\t});\n\n\t\tTextField massz = new TextField(CreatureLab.getFormatted(currentCentroid.getZ()));\n\t\tmassz.setOnAction(event -> {\n\t\t\tcurrentCentroid.setZ(textToNum(massz));\n\t\t\tdevice.setCenterOfMassFromCentroid(currentCentroid);;\n\t\t\tif (manager != null)\n\t\t\t\tmanager.generateCad(CSGDatabase.getInstance());\n\n\t\t});\n\n\t\tpane.add(new Text(\"Mass\"), 0, 0);\n\t\tpane.add(mass, 1, 0);\n\n\t\tpane.add(new Text(\"Mass Centroid x\"), 0, 1);\n\t\tpane.add(massx, 1, 1);\n\n\t\tpane.add(new Text(\"Mass Centroid y\"), 0, 2);\n\t\tpane.add(massy, 1, 2);\n\t\tpane.add(new Text(\"Mass Centroid z\"), 0, 3);\n\t\tpane.add(massz, 1, 3);\n\t\tgetChildren().add(pane);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/creature/CreatureLab.java",
    "content": "package com.neuronrobotics.bowlerstudio.creature;\n\nimport com.neuronrobotics.bowlerstudio.BowlerStudio;\nimport com.neuronrobotics.bowlerstudio.BowlerStudioController;\nimport com.neuronrobotics.bowlerstudio.BowlerStudioModularFrame;\nimport com.neuronrobotics.bowlerstudio.assets.AssetFactory;\nimport com.neuronrobotics.bowlerstudio.scripting.ScriptingEngine;\nimport com.neuronrobotics.bowlerstudio.tabs.AbstractBowlerStudioTab;\nimport com.neuronrobotics.sdk.addons.gamepad.BowlerJInputDevice;\nimport com.neuronrobotics.sdk.addons.kinematics.DHLink;\nimport com.neuronrobotics.sdk.addons.kinematics.DHParameterKinematics;\nimport com.neuronrobotics.sdk.addons.kinematics.IDriveEngine;\nimport com.neuronrobotics.sdk.addons.kinematics.MobileBase;\nimport com.neuronrobotics.sdk.common.BowlerAbstractDevice;\nimport com.neuronrobotics.sdk.util.ThreadUtil;\n\nimport eu.mihosoft.vrl.v3d.parametrics.CSGDatabase;\nimport eu.mihosoft.vrl.v3d.parametrics.CSGDatabaseInstance;\nimport javafx.beans.value.ChangeListener;\nimport javafx.fxml.FXMLLoader;\nimport javafx.geometry.Insets;\nimport javafx.geometry.Pos;\nimport javafx.scene.Parent;\nimport javafx.scene.control.*;\nimport javafx.scene.layout.AnchorPane;\nimport javafx.scene.layout.GridPane;\n\nimport org.eclipse.jgit.api.errors.GitAPIException;\nimport org.eclipse.jgit.api.errors.InvalidRemoteException;\nimport org.eclipse.jgit.api.errors.TransportException;\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.HashMap;\n\npublic class CreatureLab extends AbstractBowlerStudioTab implements IOnEngineeringUnitsChange {\n\n\tprivate BowlerAbstractDevice pm;\n\tprivate boolean enabled = true;\n\n\tprivate IDriveEngine defaultDriveEngine;\n\t// private DhInverseSolver defaultDHSolver;\n\tprivate Menu localMenue;\n\tprivate ProgressIndicator pi;\n\n\tprivate MobileBaseCadManager baseManager;\n\tprivate CheckBox autoRegen = new CheckBox(\"Auto\");\n\tprivate Button regen = new Button(\"Generate Vitamins Now\");\n\tParent root;\n\tprivate BowlerJInputDevice gameController = null;\n\tCreatureLabControlsTab tab = new CreatureLabControlsTab();\n\tboolean stateOfAutoWhenForced = true;\n\tprivate long timeSinceLastUpdate = 0;\n\n\tprivate GridPane radioOptions;\n\tprivate long timeOfLastDisable = 0;\n\n\t@Override\n\tpublic void onTabClosing() {\n\t\tbaseManager.onTabClosing();\n\t}\n\n\t@Override\n\tpublic String[] getMyNameSpaces() {\n\t\t// Auto-generated method stub\n\t\treturn new String[0];\n\t}\n\n\t@SuppressWarnings({\"restriction\", \"restriction\"})\n\t@Override\n\tpublic void initializeUI(BowlerAbstractDevice pm) {\n\t\tsetGraphic(AssetFactory.loadIcon(\"CreatureLab-Tab.png\"));\n\t\tthis.pm = pm;\n\t\tautoRegen.setSelected(true);\n\n\t\tautoRegen.setOnAction(event -> {\n\t\t\tstateOfAutoWhenForced = autoRegen.isSelected();\n\t\t\tbaseManager.setAutoRegen(autoRegen.isSelected());\n\t\t\t// BowlerStudio.runLater(() -> {\n\t\t\tregenFromUiEvent();\n\t\t\t// });\n\t\t});\n\t\tregen.setOnAction(event -> {\n\t\t\tstateOfAutoWhenForced = autoRegen.isSelected();\n\t\t\tautoRegen.setSelected(true);\n\t\t\tbaseManager.setAutoRegen(true);\n\t\t\t// BowlerStudio.runLater(()->{\n\t\t\tregenFromUiEvent();\n\t\t\t// });\n\t\t});\n\t\tregen.setGraphic(AssetFactory.loadIcon(\"Generate-Cad.png\"));\n\t\t// Auto-generated method stub\n\t\tsetText(pm.getScriptingName());\n\n\t\tMobileBase device = (MobileBase) pm;\n\n\t\t// Button save = new Button(\"Save Configuration\");\n\n\t\tFXMLLoader loader;\n\t\ttry {\n\t\t\tloader = AssetFactory.loadLayout(\"layout/CreatureLabControlsTab.fxml\", true);\n\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\tloader.setController(tab);\n\t\t\t\t// This is needed when loading on MAC\n\t\t\t\tloader.setClassLoader(getClass().getClassLoader());\n\t\t\t\ttry {\n\t\t\t\t\troot = loader.load();\n\t\t\t\t\tsetContent(root);\n\t\t\t\t\tnew Thread(() -> {\n\t\t\t\t\t\tfinishLoading(device);\n\t\t\t\t\t}).start();\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\n\t\t\t});\n\t\t\tThreadUtil.wait(16);\n\t\t} catch (Exception e1) {\n\t\t\t// Auto-generated catch block\n\t\t\te1.printStackTrace();\n\t\t}\n\t\twhile (getContent() == null)\n\t\t\ttry {\n\t\t\t\tThread.sleep(100);\n\t\t\t} catch (InterruptedException e) {\n\t\t\t\t// Auto-generated catch block\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t}\n\n\tprivate void regenFromUiEvent() {\n\t\tif (System.currentTimeMillis() - timeSinceLastUpdate < 500) {\n\t\t\treturn;\n\t\t}\n\t\tcom.neuronrobotics.sdk.common.Log.error(\"Regenerating robot \" + System.currentTimeMillis());\n\t\ttimeSinceLastUpdate = System.currentTimeMillis();\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tif (autoRegen.isSelected()) {\n\t\t\t\tgenerateCad(CSGDatabase.getInstance());\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate void finishLoading(MobileBase device) {\n\n\t\tTreeItem<String> rootItem = null;\n\t\tTreeItem<String> mainBase = null;\n\t\tint count = 1;\n\t\tfor (DHParameterKinematics kin : device.getAllDHChains()) {\n\t\t\tfor (int i = 0; i < kin.getNumberOfLinks(); i++) {\n\t\t\t\tDHLink dhLink = kin.getDhLink(i);\n\t\t\t\tif (dhLink.getSlaveMobileBase() != null) {\n\t\t\t\t\tcount++;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\ttry {\n\t\t\trootItem = new TreeItem<String>(\"Mobile Bases\", AssetFactory.loadIcon(\"creature.png\"));\n\t\t\tmainBase = new TreeItem<String>(device.getScriptingName(), AssetFactory.loadIcon(\"creature.png\"));\n\t\t} catch (Exception e) {\n\t\t\trootItem = new TreeItem<String>(device.getScriptingName());\n\t\t}\n\t\tif (count == 1) {\n\t\t\trootItem = mainBase;\n\t\t} else {\n\t\t\trootItem.getChildren().add(mainBase);\n\t\t}\n\t\tTreeItem<String> rootItemFinal = rootItem;\n\t\tTreeItem<String> mainBaseFinal = mainBase;\n\t\tAnchorPane treebox1 = tab.getTreeBox();\n\t\t// @JansenSmith - placed contents in llambda runnable - 20220915\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tTreeView<String> tree = new TreeView<>(rootItemFinal);\n\n\t\t\ttreebox1.getChildren().clear();\n\t\t\ttreebox1.getChildren().add(tree);\n\n\t\t\tAnchorPane.setTopAnchor(tree, 0.0);\n\t\t\tAnchorPane.setLeftAnchor(tree, 0.0);\n\t\t\tAnchorPane.setRightAnchor(tree, 0.0);\n\t\t\tAnchorPane.setBottomAnchor(tree, 0.0);\n\n\t\t\tHashMap<TreeItem<String>, Runnable> callbackMapForTreeitems = new HashMap<>();\n\t\t\tHashMap<TreeItem<String>, Parent> widgetMapForTreeitems = new HashMap<>();\n\t\t\tFile source;\n\t\t\tboolean creatureIsOwnedByUser = false;\n\t\t\ttry {\n\t\t\t\tsource = ScriptingEngine.fileFromGit(device.getGitSelfSource()[0], device.getGitSelfSource()[1]);\n\t\t\t\tcreatureIsOwnedByUser = ScriptingEngine.checkOwner(source);\n\t\t\t} catch (Exception e) {\n\t\t\t\te.printStackTrace();\n\n\t\t\t}\n\n\t\t\trootItemFinal.setExpanded(true);\n\t\t\tMobileBaseCadManager.get(CSGDatabase.getInstance(), device, BowlerStudioController.getMobileBaseUI());\n\t\t\tMobleBaseMenueFactory.load(device, tree, mainBaseFinal, callbackMapForTreeitems, widgetMapForTreeitems,\n\t\t\t\t\tthis, true, creatureIsOwnedByUser);\n\t\t\t// tree.setPrefWidth(325);\n\t\t\ttreebox1.widthProperty().addListener((obs, oldVal, newVal) -> {\n\t\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"Creature lab width \"+newVal);\n\t\t\t\ttree.setPrefWidth((double) newVal);\n\t\t\t});\n\t\t\ttree.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);\n\t\t\tJogMobileBase walkWidget = new JogMobileBase(device);\n\t\t\ttree.getSelectionModel().selectedItemProperty()\n\t\t\t\t\t.addListener((ChangeListener<Object>) (observable, oldValue, newValue) -> {\n\t\t\t\t\t\ttree.autosize();\n\t\t\t\t\t\t@SuppressWarnings(\"unchecked\")\n\t\t\t\t\t\tTreeItem<String> treeItem = (TreeItem<String>) newValue;\n\t\t\t\t\t\tmainWidget(device, callbackMapForTreeitems, widgetMapForTreeitems, walkWidget, treeItem);\n\n\t\t\t\t\t});\n\t\t\tif (hasWalking(device)) {\n\t\t\t\ttab.setOverlayTopRight(walkWidget);\n\t\t\t}\n\t\t});\n\t\t// VBox progress = new VBox(10);\n\n\t\tfinal ToggleGroup group = new ToggleGroup();\n\n\t\tRadioButton rb1 = new RadioButton();\n\t\trb1.setToggleGroup(group);\n\t\trb1.setSelected(true);\n\t\trb1.setOnAction(event -> {\n\t\t\t// disable();\n\t\t\t// autoRegen.setText(\"Auto-Generate CAD\");\n\t\t\tregen.setText(\"Generate CAD Now\");\n\n\t\t\tBowlerStudio.runLater(() -> setCadMode(false));\n\t\t});\n\t\tregen.setMinWidth(120);\n\n\t\tRadioButton rb2 = new RadioButton();\n\t\trb2.setToggleGroup(group);\n\t\trb2.fire();\n\t\trb2.setOnAction(event -> {\n\t\t\t// disable();\n\n\t\t\t// autoRegen.setText(\"Auto-Generate Vitamins\");\n\t\t\tregen.setText(\"Generate Vitamins Now\");\n\t\t\tBowlerStudio.runLater(() -> setCadMode(true));\n\t\t});\n\n\t\tradioOptions = new GridPane();\n\t\tradioOptions.setPadding(new Insets(10, 10, 10, 10));\n\t\tradioOptions.setVgap(5);\n\t\tradioOptions.setHgap(5);\n\n\t\t// Setting the Grid alignment\n\t\tradioOptions.setAlignment(Pos.CENTER);\n\t\tradioOptions.add(new Label(\"Select Display Mode:\"), 0, 0);\n\t\tradioOptions.add(new Label(\"Cad Generation\"), 0, 1);\n\t\tradioOptions.add(rb1, 1, 1);\n\n\t\tradioOptions.add(new Label(\"Vitamins View\"), 0, 2);\n\t\tradioOptions.add(rb2, 1, 2);\n\n\t\tpi = new ProgressIndicator(0);\n\t\tbaseManager = MobileBaseCadManager.get(CSGDatabase.getInstance(), device,\n\t\t\t\tBowlerStudioController.getMobileBaseUI());\n\t\tpi.progressProperty().bindBidirectional(baseManager.getProcesIndictor());\n\n\t\tradioOptions.add(pi, 1, 0);\n\t\tradioOptions.add(autoRegen, 1, 3);\n\t\tradioOptions.add(regen, 0, 3);\n\n\t\ttab.setOverlayTop(radioOptions);\n\n\t\tBowlerStudioModularFrame.getBowlerStudioModularFrame().showCreatureLab();\n\t\tsetCadMode(true);// start the UI in config mode\n\n\t}\n\n\tprivate void mainWidget(MobileBase device, HashMap<TreeItem<String>, Runnable> callbackMapForTreeitems,\n\t\t\tHashMap<TreeItem<String>, Parent> widgetMapForTreeitems, JogMobileBase walkWidget,\n\t\t\tTreeItem<String> treeItem) {\n\t\tnew Thread() {\n\t\t\tpublic void run() {\n\n\t\t\t\tif (callbackMapForTreeitems.get(treeItem) != null) {\n\t\t\t\t\tcallbackMapForTreeitems.get(treeItem).run();\n\t\t\t\t}\n\t\t\t\tAnchorPane box = hasWalking(device) ? tab.getControlsBox() : tab.getWalkingBox();\n\t\t\t\tif (widgetMapForTreeitems.get(treeItem) != null) {\n\n\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\tbox.getChildren().clear();\n\t\t\t\t\t\tParent g = widgetMapForTreeitems.get(treeItem);\n\t\t\t\t\t\tbox.getChildren().add(g);\n\t\t\t\t\t\tAnchorPane.setTopAnchor(g, 0.0);\n\t\t\t\t\t\tAnchorPane.setLeftAnchor(g, 0.0);\n\t\t\t\t\t\tAnchorPane.setRightAnchor(g, 0.0);\n\t\t\t\t\t\tAnchorPane.setBottomAnchor(g, 0.0);\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\tbox.getChildren().clear();\n\t\t\t\t\t\tif (hasWalking(device)) {\n\t\t\t\t\t\t\ttab.setOverlayTopRight(walkWidget);\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t}.start();\n\t}\n\n\tprivate void disable() {\n\t\tenabled = false;\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tautoRegen.setDisable(true);\n\t\t\tif (radioOptions != null)\n\t\t\t\tradioOptions.setDisable(true);\n\t\t\tregen.setDisable(true);\n\t\t});\n\t}\n\n\tprivate void enable() {\n\t\tenabled = true;\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tautoRegen.setDisable(false);\n\t\t\tif (radioOptions != null)\n\t\t\t\tradioOptions.setDisable(false);\n\t\t\tregen.setDisable(false);\n\t\t});\n\t}\n\n\tprivate boolean hasWalking(MobileBase device) {\n\t\treturn device.getLegs().size() > 0 || device.getSteerable().size() > 0 || device.getDrivable().size() > 0;\n\t}\n\n\tprivate void setCadMode(boolean mode) {\n\t\tnew Thread(() -> {\n\t\t\tbaseManager.setConfigurationViewerMode(mode);\n\t\t\tregenFromUiEvent();\n\t\t}).start();\n\n\t}\n\n\tpublic void generateCad(CSGDatabaseInstance db) {\n\t\t// new RuntimeException().printStackTrace();\n\t\tif (!enabled)\n\t\t\treturn;\n\t\tdisable();\n\t\tbaseManager.generateCadWithEnd(db, () -> {\n\t\t\tenable();\n\t\t\tautoRegen.setSelected(stateOfAutoWhenForced);\n\t\t});\n\t}\n\n\t@Override\n\tpublic void onTabReOpening() {\n\t\tbaseManager.run();\n\t\ttry {\n\t\t\tif (autoRegen.isSelected())\n\t\t\t\tgenerateCad(CSGDatabase.getInstance());\n\t\t} catch (Exception ex) {\n\n\t\t}\n\t}\n\n\tpublic static String getFormatted(double value) {\n\t\treturn String.format(\"%4.3f%n\", (double) value);\n\t}\n\n\t@Override\n\tpublic void onSliderMoving(EngineeringUnitsSliderWidget source, double newAngleDegrees) {\n\t\t// Auto-generated method stub\n\n\t}\n\n\t@Override\n\tpublic void onSliderDoneMoving(EngineeringUnitsSliderWidget source, double newAngleDegrees) {\n\t\tif (autoRegen.isSelected())\n\t\t\tgenerateCad(CSGDatabase.getInstance());\n\t}\n\n\tpublic BowlerJInputDevice getController() {\n\n\t\treturn getGameController();\n\t}\n\n\tpublic BowlerJInputDevice getGameController() {\n\t\treturn gameController;\n\t}\n\n\tpublic void setGameController(BowlerJInputDevice bowlerJInputDevice) {\n\t\tthis.gameController = bowlerJInputDevice;\n\t}\n\n\tpublic void setGitDhEngine(String gitsId, String file, DHParameterKinematics dh) {\n\t\tMobileBaseLoader.setDefaultDhParameterKinematics(CSGDatabase.getInstance(), dh);\n\n\t}\n\n\tpublic void setGitCadEngine(String gitsId, String file, MobileBase device)\n\t\t\tthrows InvalidRemoteException, TransportException, GitAPIException, IOException {\n\t\tbaseManager.setGitCadEngine(gitsId, file, device);\n\t}\n\n\tpublic void setGitCadEngine(String gitsId, String file, DHParameterKinematics dh)\n\t\t\tthrows InvalidRemoteException, TransportException, GitAPIException, IOException {\n\t\tbaseManager.setGitCadEngine(gitsId, file, dh);\n\t}\n\n\tpublic void setGitWalkingEngine(String git, String file, MobileBase device) {\n\n\t\tMobileBaseLoader.get(CSGDatabase.getInstance(), baseManager.getMobileBase()).setGitWalkingEngine(git, file,\n\t\t\t\tdevice);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/creature/CreatureLabControlsTab.java",
    "content": "package com.neuronrobotics.bowlerstudio.creature;\nimport com.neuronrobotics.bowlerstudio.BowlerStudio;\n\nimport javafx.fxml.FXML;\nimport javafx.scene.layout.AnchorPane;\nimport javafx.scene.layout.GridPane;\n\npublic class CreatureLabControlsTab {\n\t@FXML\n\tprivate AnchorPane walkingBox;\n\n\t@FXML\n\tprivate AnchorPane controlsBox;\n\n\t@FXML\n\tprivate AnchorPane progressBar;\n\n\t@FXML\n\tprivate AnchorPane treeBox;\n\n\tpublic AnchorPane getWalkingBox() {\n\t\treturn walkingBox;\n\t}\n\n\t// public void setWalkingBox(AnchorPane walkingBox) {\n\t// this.walkingBox = walkingBox;\n\t// }\n\n\tpublic AnchorPane getControlsBox() {\n\t\treturn walkingBox;\n\t}\n\n\t// public void setControlsBox(AnchorPane controlsBox) {\n\t// this.controlsBox = controlsBox;\n\t// }\n\n\t// public AnchorPane getProgressBar() {\n\t// return progressBar;\n\t// }\n\n\tpublic void setProgressBar(AnchorPane progressBar) {\n\t\tthis.progressBar = progressBar;\n\t}\n\n\tpublic AnchorPane getTreeBox() {\n\t\treturn treeBox;\n\t}\n\n\tpublic void setTreeBox(AnchorPane treeBox) {\n\t\tthis.treeBox = treeBox;\n\t}\n\n\tpublic void setOverlayTop(GridPane radioOptions) {\n\t\t// Auto-generated method stub\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tprogressBar.getChildren().clear();\n\t\t\tprogressBar.getChildren().add(radioOptions);\n\t\t\tAnchorPane.setTopAnchor(radioOptions, 0.0);\n\t\t\tAnchorPane.setLeftAnchor(radioOptions, 0.0);\n\t\t\tAnchorPane.setRightAnchor(radioOptions, 0.0);\n\t\t\tAnchorPane.setBottomAnchor(radioOptions, 0.0);\n\t\t});\n\t}\n\n\tpublic void setOverlayTopRight(JogMobileBase walkWidget) {\n\t\t// Auto-generated method stub\n\t\t// @JansenSmith - placed contents in llambda runnable - 20220915\n\t\tBowlerStudio.runLater(() -> {\n\t\t\twalkingBox.getChildren().clear();\n\t\t\twalkingBox.getChildren().add(walkWidget);\n\t\t\tAnchorPane.setTopAnchor(walkWidget, 0.0);\n\t\t\tAnchorPane.setLeftAnchor(walkWidget, 0.0);\n\t\t\tAnchorPane.setRightAnchor(walkWidget, 0.0);\n\t\t\tAnchorPane.setBottomAnchor(walkWidget, 0.0);\n\t\t});\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/creature/DhLab.java",
    "content": "package com.neuronrobotics.bowlerstudio.creature;\n\nimport com.neuronrobotics.sdk.common.BowlerAbstractDevice;\n\npublic class DhLab extends CreatureLab {\n\n\t@Override\n\tpublic void initializeUI(BowlerAbstractDevice pm) {\n\t\tsuper.initializeUI(pm);\n\t\tsetText(\"DH Lab\");\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/creature/DhSettingsWidget.java",
    "content": "package com.neuronrobotics.bowlerstudio.creature;\n\nimport javafx.scene.layout.ColumnConstraints;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.RowConstraints;\nimport javafx.scene.text.Text;\n\nimport com.neuronrobotics.sdk.addons.kinematics.DHLink;\nimport com.neuronrobotics.sdk.addons.kinematics.DHParameterKinematics;\n\npublic class DhSettingsWidget extends javafx.scene.Group implements IOnEngineeringUnitsChange {\n\n\tprivate DHLink dhLink;\n\tprivate EngineeringUnitsSliderWidget delta;\n\tprivate EngineeringUnitsSliderWidget theta;\n\tprivate EngineeringUnitsSliderWidget alpha;\n\tprivate EngineeringUnitsSliderWidget radius;\n\tprivate DHParameterKinematics dh;\n\tprivate IOnEngineeringUnitsChange externalListener;\n\tprivate DhSettingsWidget self = this;\n\tpublic DhSettingsWidget(DHLink dhl, DHParameterKinematics device2, IOnEngineeringUnitsChange externalListener) {\n\t\tthis.dhLink = dhl;\n\t\tthis.dh = device2;\n\n\t\tthis.externalListener = externalListener;\n\n\t\tdelta = new EngineeringUnitsSliderWidget(this, 0, 200, dhLink.getDelta(), 180, \" mm \");\n\n\t\ttheta = new EngineeringUnitsSliderWidget(new IOnEngineeringUnitsChange() {\n\n\t\t\t@Override\n\t\t\tpublic void onSliderMoving(EngineeringUnitsSliderWidget source, double newAngleDegrees) {\n\t\t\t\tdouble[] joint = dh.getCurrentJointSpaceVector();\n\t\t\t\tdhLink.setTheta(Math.toRadians(newAngleDegrees));\n\t\t\t\tif (externalListener != null)\n\t\t\t\t\texternalListener.onSliderMoving(source, newAngleDegrees);\n\n\t\t\t\tdh.getChain().getChain(joint);\n\t\t\t\tdh.updateCadLocations();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onSliderDoneMoving(EngineeringUnitsSliderWidget source, double newAngleDegrees) {\n\t\t\t\tif (externalListener != null)\n\t\t\t\t\texternalListener.onSliderDoneMoving(source, newAngleDegrees);\n\t\t\t}\n\t\t}, -180, 180, Math.toDegrees(dhLink.getTheta()), 180, \"degrees\");\n\n\t\tradius = new EngineeringUnitsSliderWidget(this, 0, 200, dhLink.getRadius(), 180, \" mm \");\n\n\t\talpha = new EngineeringUnitsSliderWidget(this, -180, 180, Math.toDegrees(dhLink.getAlpha()), 180, \"degrees\");\n\t\tdelta.showSlider(false);\n\t\tradius.showSlider(false);\n\t\talpha.showSlider(false);\n\t\tGridPane gridpane = new GridPane();\n\t\tgridpane.getColumnConstraints().add(new ColumnConstraints(120)); // column 1 is 75 wide\n\t\tgridpane.getColumnConstraints().add(new ColumnConstraints(320)); // column 2 is 300 wide\n\t\tgridpane.getColumnConstraints().add(new ColumnConstraints(100)); // column 2 is 100 wide\n\t\tgridpane.getRowConstraints().add(new RowConstraints(70)); //\n\t\tgridpane.getRowConstraints().add(new RowConstraints(70)); //\n\t\tgridpane.getRowConstraints().add(new RowConstraints(70)); //\n\t\tgridpane.getRowConstraints().add(new RowConstraints(70)); //\n\t\tgridpane.add(new Text(\"Height (D value)\"), 0, 0);\n\t\tgridpane.add(delta, 1, 0);\n\t\tgridpane.add(new Text(\"Length (A value)\"), 0, 1);\n\t\tgridpane.add(radius, 1, 1);\n\n\t\tgridpane.getColumnConstraints().add(new ColumnConstraints(120)); // column 1 is 75 wide\n\t\tgridpane.getColumnConstraints().add(new ColumnConstraints(320)); // column 2 is 300 wide\n\t\tgridpane.getColumnConstraints().add(new ColumnConstraints(100)); // column 2 is 100 wide\n\t\tgridpane.getRowConstraints().add(new RowConstraints(70)); //\n\t\tgridpane.getRowConstraints().add(new RowConstraints(70)); //\n\t\tgridpane.add(new Text(\"Theta\"), 0, 2);\n\t\tgridpane.add(theta, 1, 2);\n\t\tgridpane.add(new Text(\"Alpha\"), 0, 3);\n\t\tgridpane.add(alpha, 1, 3);\n\n\t\tgetChildren().add(gridpane);\n\t}\n\n\t@Override\n\tpublic void onSliderMoving(EngineeringUnitsSliderWidget source, double newAngleDegrees) {\n\t\tif (source == theta)\n\t\t\tdhLink.setTheta(Math.toRadians(newAngleDegrees));\n\t\tif (source == alpha)\n\t\t\tdhLink.setAlpha(Math.toRadians(newAngleDegrees));\n\t\tif (source == radius)\n\t\t\tdhLink.setRadius(newAngleDegrees);\n\t\tif (source == delta)\n\t\t\tdhLink.setDelta(newAngleDegrees);\n\n\t\tif (externalListener != null)\n\t\t\texternalListener.onSliderMoving(source, newAngleDegrees);\n\t}\n\n\t@Override\n\tpublic void onSliderDoneMoving(EngineeringUnitsSliderWidget source, double newAngleDegrees) {\n\t\tonSliderMoving(source, newAngleDegrees);\n\t\tdouble[] joint = dh.getCurrentJointSpaceVector();\n\t\tdh.getChain().getChain(joint);\n\t\tif (externalListener != null)\n\t\t\texternalListener.onSliderDoneMoving(source, newAngleDegrees);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/creature/EngineeringUnitsSliderWidget.java",
    "content": "package com.neuronrobotics.bowlerstudio.creature;\n\nimport com.neuronrobotics.bowlerstudio.BowlerStudio;\nimport com.neuronrobotics.bowlerstudio.assets.FontSizeManager;\n\nimport javafx.beans.value.ChangeListener;\nimport javafx.beans.value.ObservableValue;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Slider;\nimport javafx.scene.control.TextField;\nimport javafx.scene.layout.ColumnConstraints;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.text.Text;\nimport javafx.scene.control.Label;\n\n@SuppressWarnings(\"restriction\")\npublic class EngineeringUnitsSliderWidget extends GridPane implements ChangeListener<Number> {\n\tprivate TextField spv;\n\tprivate Label increment;\n\tprivate Slider setpoint;\n\tprivate IOnEngineeringUnitsChange listener;\n\tprivate boolean intCast = false;\n\tprivate boolean allowResize = true;\n\tprivate Button jogplus = new Button(\"+\");\n\tprivate Button jogminus = new Button(\"-\");\n\tprivate double instantValueStore = 0;\n\tprivate boolean editing = false;\n\tprivate double jogIncrement = 1.0;\n\tprivate String units;\n\tprivate double min;\n\tprivate double max;\n\n\tpublic EngineeringUnitsSliderWidget(IOnEngineeringUnitsChange listener, double min, double max, double current,\n\t\t\tdouble width, String units, boolean intCast) {\n\t\tthis(listener, min, max, current, width, units);\n\t\tthis.intCast = intCast;\n\t}\n\n\tpublic EngineeringUnitsSliderWidget(IOnEngineeringUnitsChange listener, double current, double width,\n\t\t\tString units) {\n\t\tthis(listener, -Float.MAX_VALUE, Float.MAX_VALUE, current, width, units);\n\n\t}\n\n\tprivate void onSliderMovingInternal(EngineeringUnitsSliderWidget source, double newAngleDegrees) {\n\t\tediting = true;\n\t\t// com.neuronrobotics.sdk.common.Log.error(\"Slider moving \");\n\t\tgetListener().onSliderMoving(this, newAngleDegrees);\n\t}\n\n\tprivate void onSliderDoneMovingInternal(EngineeringUnitsSliderWidget source, double newAngleDegrees) {\n\t\tediting = false;\n\t\tinstantValueStore = (newAngleDegrees);\n\t\t// com.neuronrobotics.sdk.common.Log.error(\"Slider done\");\n\t\tgetListener().onSliderDoneMoving(this, newAngleDegrees);\n\t}\n\n\tpublic EngineeringUnitsSliderWidget(IOnEngineeringUnitsChange listener, double minIn, double maxIn, double current,\n\t\t\tdouble width, String units) {\n\t\tthis.min = minIn;\n\t\tthis.max = maxIn;\n\t\tthis.units = units;\n\t\tthis.setListener(listener);\n\t\tsetpoint = new Slider();\n\t\tincrement = new Label(jogIncrement + \"\");\n\t\tinstantValueStore = current;\n\t\tif (min > max) {\n\t\t\tdouble minStart = min;\n\t\t\tmin = max;\n\t\t\tmax = minStart;\n\t\t}\n\t\tif (min > current)\n\t\t\tmin = current;\n\t\tif (max < current)\n\t\t\tmax = current;\n\t\tdouble range = Math.abs(max - min);\n\t\tif (range < 1) {\n\t\t\tmin = min - 100;\n\t\t\tmax = max + 100;\n\t\t\trange = 200;\n\t\t}\n\t\tsetpoint.setMin(min);\n\t\tsetpoint.setMax(max);\n\t\tsetpoint.setValue(current);\n\t\tsetpoint.setShowTickLabels(true);\n\t\tsetpoint.setShowTickMarks(true);\n\t\t// setpoint.setSnapToTicks(true);\n\t\tsetpoint.setMajorTickUnit(range);\n\t\tsetpoint.setMinorTickCount(5);\n\t\t// setpoint.setBlockIncrement(range/100);\n\t\tspv = new TextField(getFormatted(current));\n\t\tspv.focusedProperty().addListener(new ChangeListener<Boolean>() {\n\t\t\t@Override\n\t\t\tpublic void changed(ObservableValue<? extends Boolean> arg0, Boolean oldPropertyValue,\n\t\t\t\t\tBoolean newPropertyValue) {\n\t\t\t\tif (newPropertyValue) {\n\t\t\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"Textfield on focus\");\n\t\t\t\t\tediting = true;\n\t\t\t\t} else {\n\t\t\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"Textfield out focus\");\n\t\t\t\t\tediting = false;\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tspv.setOnAction(event -> {\n\t\t\ttry {\n\t\t\t\tlocalSetValue(Double.parseDouble(spv.getText()));\n\t\t\t} catch (Throwable t) {\n\t\t\t\tt.printStackTrace();\n\t\t\t\tlocalSetValue(instantValueStore);\n\n\t\t\t}\n\t\t});\n\t\tsetpoint.setMaxWidth(width);\n\t\tsetpoint.valueChangingProperty().addListener((ChangeListener<Boolean>) (observable, oldValue, newValue) -> {\n\t\t\ttry {\n\t\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"Slider moving = \"+newValue);\n\t\t\t\tif (!newValue)\n\t\t\t\t\tonSliderDoneMovingInternal(this, setpoint.getValue());\n\t\t\t} catch (java.lang.NumberFormatException ex) {\n\t\t\t\tex.printStackTrace();\n\t\t\t\tsetValue(0);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t});\n\t\tsetpoint.valueProperty().addListener(this);\n\n\t\tString unitsString = \"(\" + units + \")\";\n\t\tdouble scale = (double) (FontSizeManager.getDefaultSize()) / 12.0;\n\t\tgetColumnConstraints().add(new ColumnConstraints(30 * scale)); // column 2 is 100 wide\n\t\tgetColumnConstraints().add(new ColumnConstraints(40 * scale)); // column 2 is 100 wide\n\t\tgetColumnConstraints().add(new ColumnConstraints(30 * scale)); // column 2 is 100 wide\n\t\tgetColumnConstraints().add(new ColumnConstraints(100 * scale)); // column 2 is 100 wide\n\t\tgetColumnConstraints().add(new ColumnConstraints(unitsString.length() * 7 * scale)); // column 2 is 100 wide\n\n\t\tadd(setpoint, 3, 1);\n\t\tadd(jogplus, 2, 0);\n\t\tadd(increment, 1, 0);\n\t\tadd(jogminus, 0, 0);\n\t\tadd(spv, 3, 0);\n\t\tadd(new Text(unitsString), 4, 0);\n\n\t\tjogplus.setOnAction(event -> {\n\t\t\tjogPlusOne();\n\t\t});\n\t\tjogminus.setOnAction(event -> {\n\t\t\tjogMinusOne();\n\t\t});\n\t}\n\n\tprivate void localSetValue(double val) {\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tediting = false;\n\t\t\tsetValue(val);\n\n\t\t\tonSliderMovingInternal(this, val);\n\t\t\tonSliderDoneMovingInternal(this, val);\n\t\t});\n\t}\n\n\tpublic void jogMinusOne() {\n\t\tjog(-getJogIncrement());\n\t}\n\n\tpublic void jogPlusOne() {\n\t\tjog(getJogIncrement());\n\t}\n\n\tpublic void jog(double amount) {\n\t\tdouble value = getValue() + amount;\n\t\tdouble max2 = setpoint.getMax();\n\t\tif (value > max2)\n\t\t\treturn;\n\t\tdouble min2 = setpoint.getMin();\n\t\tif (value < min2)\n\t\t\treturn;\n\t\tsetValue(value);\n\t\tonSliderMovingInternal(this, value);\n\t\tonSliderDoneMovingInternal(this, value);\n\t}\n\n\tpublic void setUpperBound(double newBound) {\n\t\tsetpoint.setMax(newBound);\n\t}\n\n\tpublic void setLowerBound(double newBound) {\n\t\tsetpoint.setMin(newBound);\n\t}\n\n\t@Override\n\tpublic void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {\n\t\tupdateValue();\n\t\t// com.neuronrobotics.sdk.common.Log.error(\"Updating value to \"+newValue);\n\t}\n\n\tprivate void updateValue() {\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tspv.setText(getFormatted(setpoint.getValue()));\n\t\t\tonSliderMovingInternal(this, setpoint.getValue());\n\t\t});\n\t}\n\n\tpublic void setValue(double value) {\n\t\tif (editing)\n\t\t\treturn;// do not overwrite an editing field\n\t\tdouble val = value;\n\t\tif (val > setpoint.getMax()) {\n\t\t\tif (isAllowResize())\n\t\t\t\tsetpoint.setMax(val);\n\t\t\telse\n\t\t\t\tval = setpoint.getMax();\n\t\t}\n\t\tif (val < setpoint.getMin()) {\n\t\t\tif (isAllowResize())\n\t\t\t\tsetpoint.setMin(val);\n\t\t\telse\n\t\t\t\tval = setpoint.getMin();\n\t\t}\n\t\tinstantValueStore = val;\n\t\tdouble toSet = val;\n\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tsetValueLocal(toSet);\n\t\t});\n\n\t}\n\n\tprivate void setValueLocal(double value) {\n\t\tsetpoint.valueProperty().removeListener(this);\n\t\tdouble range = Math.abs(setpoint.getMax() - setpoint.getMin());\n\t\tif (range > 0)\n\t\t\tsetpoint.setMajorTickUnit(range);\n\t\tsetpoint.setValue(value);\n\t\tspv.setText(getFormatted(setpoint.getValue()));\n\t\tsetpoint.valueProperty().addListener(this);\n\t\t// com.neuronrobotics.sdk.common.Log.error(\"Setpoint changed to \"+val);\n\t}\n\n\tpublic double getValue() {\n\t\treturn instantValueStore;\n\t}\n\n\tpublic String getFormatted(double value) {\n\t\tif (intCast)\n\t\t\treturn String.valueOf((int) value);\n\t\treturn String.format(\"%8.3f\", (double) value);\n\t}\n\n\tpublic IOnEngineeringUnitsChange getListener() {\n\t\tif (listener == null)\n\t\t\treturn new IOnEngineeringUnitsChange() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void onSliderMoving(EngineeringUnitsSliderWidget source, double newAngleDegrees) {\n\t\t\t\t\t// Auto-generated method stub\n\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic void onSliderDoneMoving(EngineeringUnitsSliderWidget source, double newAngleDegrees) {\n\t\t\t\t\t// Auto-generated method stub\n\n\t\t\t\t}\n\n\t\t\t};\n\t\treturn listener;\n\t}\n\n\tpublic void setListener(IOnEngineeringUnitsChange listener) {\n\t\tthis.listener = listener;\n\t}\n\n\tpublic boolean isAllowResize() {\n\t\treturn allowResize;\n\t}\n\n\tpublic void setAllowResize(boolean allowResize) {\n\t\tthis.allowResize = allowResize;\n\t}\n\n\tpublic void showSlider(boolean b) {\n\t\tsetpoint.setVisible(b);\n\t}\n\n\tpublic boolean isEditing() {\n\t\treturn editing;\n\t}\n\n\t/**\n\t * @return the jogIncrement\n\t */\n\tpublic double getJogIncrement() {\n\t\treturn jogIncrement;\n\t}\n\n\t/**\n\t * @param jogIncrement\n\t *            the jogIncrement to set\n\t */\n\tpublic void setJogIncrement(double j) {\n\t\t// com.neuronrobotics.sdk.common.Log.error(\"Increment set to \"+j+\" \"+units);\n\t\tjogIncrement = Math.abs(j);\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tincrement.setText(\"\" + jogIncrement);\n\t\t});\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/creature/GameControlThreadManager.java",
    "content": "package com.neuronrobotics.bowlerstudio.creature;\n\nimport java.io.File;\n\nimport com.neuronrobotics.bowlerstudio.BowlerStudio;\nimport com.neuronrobotics.bowlerstudio.scripting.ScriptingEngine;\nimport com.neuronrobotics.sdk.util.ThreadUtil;\n\nimport eu.mihosoft.vrl.v3d.parametrics.CSGDatabase;\n\npublic class GameControlThreadManager {\n\tprivate static Thread scriptRunner = null;\n\tprivate static IAmControlled currentController = null;\n\tprivate static boolean running = false;\n\tpublic static void stop() {\n\t\tif (!isRunning())\n\t\t\treturn;\n\t\t// new RuntimeException().printStackTrace();\n\n\t\treset();\n\t\tThread tmp = scriptRunner;\n\t\tif (tmp != null)\n\t\t\twhile (tmp.isAlive()) {\n\n\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Interrupting \" + currentController.getName());\n\t\t\t\tThreadUtil.wait(10);\n\t\t\t\ttry {\n\t\t\t\t\ttmp.interrupt();\n\t\t\t\t\ttmp.join();\n\t\t\t\t} catch (InterruptedException e) {\n\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\t// e.printStackTrace();\n\t\t\t\t}\n\t\t\t}\n\n\t\tscriptRunner = null;\n\t}\n\n\t/**\n\t * @return the currentController\n\t */\n\tpublic static IAmControlled getCurrentController() {\n\t\treturn currentController;\n\t}\n\n\t/**\n\t * @param currentController\n\t *            the currentController to set\n\t */\n\tpublic static void setCurrentController(IAmControlled c) {\n\t\tboolean was = isRunning();\n\t\tboolean b = false;\n\t\tif (currentController != null) {\n\t\t\tb = c != currentController;\n\t\t\tif (b)\n\t\t\t\tstop();\n\t\t}\n\t\tcurrentController = c;\n\t\tif (was && b) {\n\t\t\tstart();\n\t\t}\n\t}\n\n\tpublic static void startStopAction() {\n\t\t// new RuntimeException().printStackTrace();\n\t\tcurrentController.getRunStopButton().setDisable(true);\n\t\tif (isRunning())\n\t\t\tstop();\n\t\telse\n\t\t\ttry {\n\t\t\t\tstart();\n\t\t\t} catch (Exception e) {\n\t\t\t\t// Auto-generated catch block\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\tcurrentController.getRunStopButton().setDisable(false);\n\t}\n\n\tpublic static void start() {\n\t\tFile currentFile = currentController.getScriptFile();\n\n\t\tsetRunning(true);\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tBowlerStudio.setToStopButton(currentController.getRunStopButton());\n\t\t});\n\t\t// new RuntimeException().printStackTrace();\n\t\tscriptRunner = new Thread() {\n\n\t\t\tpublic void run() {\n\t\t\t\ttry {\n\n\t\t\t\t\tScriptingEngine.inlineFileScriptRun(CSGDatabase.getInstance(), currentFile,\n\t\t\t\t\t\t\tcurrentController.getArguments());\n\t\t\t\t\treset();\n\n\t\t\t\t} catch (Throwable ex) {\n\t\t\t\t\tBowlerStudio.printStackTrace(ex);\n\t\t\t\t\treset();\n\t\t\t\t}\n\n\t\t\t}\n\t\t};\n\n\t\ttry {\n\n\t\t\tscriptRunner.start();\n\t\t} catch (Exception e) {\n\t\t\t// Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t}\n\n\t}\n\n\tpublic static void reset() {\n\t\tsetRunning(false);\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tcurrentController.getRunStopButton().setText(currentController.getButtonRunText());\n\t\t\t// game.setGraphic(AssetFactory.loadIcon(\"Run.png\"));\n\t\t\tfor (String classes : currentController.getRunStopButton().getStyleClass()) {\n\t\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"Clearing \"+classes);\n\t\t\t}\n\t\t\tBowlerStudio.setToRunButton(currentController.getRunStopButton());\n\t\t\tcurrentController.getRunStopButton().setGraphic(currentController.getRunAsset());\n\t\t});\n\n\t}\n\n\t/**\n\t * @return the running\n\t */\n\tpublic static boolean isRunning() {\n\t\treturn running;\n\t}\n\n\t/**\n\t * @param running\n\t *            the running to set\n\t */\n\tprivate static void setRunning(boolean running) {\n\t\tGameControlThreadManager.running = running;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/creature/IAmControlled.java",
    "content": "package com.neuronrobotics.bowlerstudio.creature;\n\nimport java.io.File;\nimport java.util.ArrayList;\n\npublic interface IAmControlled {\n\tFile getScriptFile();\n\n\tArrayList<Object> getArguments();\n\n\tjavafx.scene.image.ImageView getRunAsset();\n\n\tjavafx.scene.control.Button getRunStopButton();\n\n\tString getButtonRunText();\n\n\tString getName();\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/creature/IGameControllerUpdateListener.java",
    "content": "package com.neuronrobotics.bowlerstudio.creature;\n\npublic interface IGameControllerUpdateListener {\n\tpublic void onControllerUpdate(AbstractGameController source);\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/creature/IGistPromptCompletionListener.java",
    "content": "package com.neuronrobotics.bowlerstudio.creature;\n\npublic interface IGistPromptCompletionListener {\n\tpublic void done(String gitsId, String file);\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/creature/IJogProvider.java",
    "content": "package com.neuronrobotics.bowlerstudio.creature;\n\nimport com.neuronrobotics.sdk.addons.kinematics.math.TransformNR;\n\npublic interface IJogProvider {\n\n\tpublic TransformNR getJogIncrement();\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/creature/IOnEngineeringUnitsChange.java",
    "content": "package com.neuronrobotics.bowlerstudio.creature;\n\npublic interface IOnEngineeringUnitsChange {\n\tpublic void onSliderMoving(EngineeringUnitsSliderWidget source, double newAngleDegrees);\n\n\tpublic void onSliderDoneMoving(EngineeringUnitsSliderWidget source, double newAngleDegrees);\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/creature/IOnTransformChange.java",
    "content": "package com.neuronrobotics.bowlerstudio.creature;\n\nimport com.neuronrobotics.sdk.addons.kinematics.math.TransformNR;\n\npublic interface IOnTransformChange {\n\tpublic abstract void onTransformChaging(TransformNR newTrans);\n\n\tpublic abstract void onTransformFinished(TransformNR newTrans);\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/creature/ITransformProvider.java",
    "content": "package com.neuronrobotics.bowlerstudio.creature;\n\nimport com.neuronrobotics.sdk.addons.kinematics.VitaminLocation;\nimport com.neuronrobotics.sdk.addons.kinematics.math.TransformNR;\n\npublic interface ITransformProvider {\n\tTransformNR get(VitaminLocation selectedVitamin);\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/creature/ITrimControl.java",
    "content": "package com.neuronrobotics.bowlerstudio.creature;\n\npublic interface ITrimControl {\n\tpublic void trimPlus();\n\n\tpublic void trimMinus();\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/creature/JogMobileBase.java",
    "content": "package com.neuronrobotics.bowlerstudio.creature;\n\nimport com.neuronrobotics.bowlerstudio.BowlerStudio;\nimport com.neuronrobotics.bowlerstudio.assets.AssetFactory;\nimport com.neuronrobotics.bowlerstudio.assets.ConfigurationDatabase;\nimport com.neuronrobotics.bowlerstudio.scripting.ScriptingEngine;\nimport com.neuronrobotics.sdk.addons.gamepad.BowlerJInputDevice;\nimport com.neuronrobotics.sdk.addons.gamepad.IGameControlEvent;\nimport com.neuronrobotics.sdk.addons.kinematics.DHParameterKinematics;\nimport com.neuronrobotics.sdk.addons.kinematics.MobileBase;\nimport com.neuronrobotics.sdk.addons.kinematics.math.RotationNR;\nimport com.neuronrobotics.sdk.addons.kinematics.math.TransformNR;\nimport com.neuronrobotics.sdk.common.BowlerAbstractDevice;\nimport com.neuronrobotics.sdk.common.IDeviceConnectionEventListener;\nimport com.neuronrobotics.sdk.common.Log;\nimport com.neuronrobotics.sdk.util.ThreadUtil;\n\nimport eu.mihosoft.vrl.v3d.parametrics.CSGDatabase;\nimport javafx.geometry.Insets;\nimport javafx.scene.control.*;\nimport javafx.scene.layout.Background;\nimport javafx.scene.layout.BackgroundFill;\nimport javafx.scene.layout.ColumnConstraints;\nimport javafx.scene.layout.CornerRadii;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.RowConstraints;\nimport javafx.scene.paint.Color;\n\nimport org.eclipse.jgit.api.errors.GitAPIException;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\npublic class JogMobileBase extends GridPane implements IGameControlEvent, IJogProvider {\n\tdouble defauletSpeed = 0.3;\n\tprivate MobileBase mobilebase = null;\n\tButton px = new Button(\"\", AssetFactory.loadIcon(\"Plus-X.png\"));\n\tButton nx = new Button(\"\", AssetFactory.loadIcon(\"Minus-X.png\"));\n\tButton py = new Button(\"\", AssetFactory.loadIcon(\"Plus-Y.png\"));\n\tButton ny = new Button(\"\", AssetFactory.loadIcon(\"Minus-Y.png\"));\n\tButton pz = new Button(\"\", AssetFactory.loadIcon(\"Plus-Z.png\"));\n\tButton nz = new Button(\"\", AssetFactory.loadIcon(\"Minus-Z.png\"));\n\tButton home = new Button(\"\", AssetFactory.loadIcon(\"Home.png\"));\n\tButton game = new Button(\"Run Game Controller\", AssetFactory.loadIcon(\"Add-Game-Controller.png\"));\n\tButton conf = new Button(\"Configure...\", AssetFactory.loadIcon(\"Configure-Game-Controller.png\"));\n\tTextField increment = new TextField(Double.toString(defauletSpeed));\n\tTextField sec = new TextField(\"0.01\");\n\tdouble x, y, rz, slider = 0;\n\tprivate boolean stop = true;\n\tprivate String paramsKey;\n\tprivate GridPane buttons;\n\tprivate static ArrayList<JogMobileBase> allWidgets = new ArrayList<JogMobileBase>();\n\tprivate boolean running = false;\n\tprivate Thread scriptRunner = null;\n\tprivate File currentFile = null;\n\tpublic JogMobileBase(MobileBase kinimatics) {\n\t\tallWidgets.add(this);\n\t\tif (!kinimatics.isAvailable())\n\t\t\tkinimatics.connect();\n\t\tmobilebase = kinimatics;\n\t\tpy = new Button(\"\", AssetFactory.loadIcon(\"Rotation-Z.png\"));\n\t\tny = new Button(\"\", AssetFactory.loadIcon(\"Rotation-Neg-Z.png\"));\n\t\tpx.setOnMousePressed(event -> {\n\t\t\ttry {\n\t\t\t\thandle((Button) event.getSource());\n\t\t\t} catch (Throwable T) {\n\t\t\t\tT.printStackTrace();\n\t\t\t}\n\t\t});\n\t\tnx.setOnMousePressed(event -> {\n\t\t\ttry {\n\t\t\t\thandle((Button) event.getSource());\n\t\t\t} catch (Throwable T) {\n\t\t\t\tT.printStackTrace();\n\t\t\t}\n\t\t});\n\t\tpy.setOnMousePressed(event -> {\n\t\t\ttry {\n\t\t\t\thandle((Button) event.getSource());\n\t\t\t} catch (Throwable T) {\n\t\t\t\tT.printStackTrace();\n\t\t\t}\n\t\t});\n\t\tny.setOnMousePressed(event -> {\n\t\t\ttry {\n\t\t\t\thandle((Button) event.getSource());\n\t\t\t} catch (Throwable T) {\n\t\t\t\tT.printStackTrace();\n\t\t\t}\n\t\t});\n\t\tpz.setOnMousePressed(event -> {\n\t\t\ttry {\n\t\t\t\thandle((Button) event.getSource());\n\t\t\t} catch (Throwable T) {\n\t\t\t\tT.printStackTrace();\n\t\t\t}\n\t\t});\n\t\tnz.setOnMousePressed(event -> {\n\t\t\ttry {\n\t\t\t\thandle((Button) event.getSource());\n\t\t\t} catch (Throwable T) {\n\t\t\t\tT.printStackTrace();\n\t\t\t}\n\t\t});\n\t\thome.setOnMousePressed(event -> {\n\t\t\ttry {\n\t\t\t\thandle((Button) event.getSource());\n\t\t\t} catch (Throwable T) {\n\t\t\t\tT.printStackTrace();\n\t\t\t}\n\t\t});\n\n\t\tpx.setOnMouseReleased(event -> {\n\t\t\ttry {\n\t\t\t\thandle((Button) event.getSource());\n\t\t\t} catch (Throwable T) {\n\t\t\t\tT.printStackTrace();\n\t\t\t}\n\t\t});\n\t\tnx.setOnMouseReleased(event -> {\n\t\t\ttry {\n\t\t\t\thandle((Button) event.getSource());\n\t\t\t} catch (Throwable T) {\n\t\t\t\tT.printStackTrace();\n\t\t\t}\n\t\t});\n\t\tpy.setOnMouseReleased(event -> {\n\t\t\ttry {\n\t\t\t\thandle((Button) event.getSource());\n\t\t\t} catch (Throwable T) {\n\t\t\t\tT.printStackTrace();\n\t\t\t}\n\t\t});\n\t\tny.setOnMouseReleased(event -> {\n\t\t\ttry {\n\t\t\t\thandle((Button) event.getSource());\n\t\t\t} catch (Throwable T) {\n\t\t\t\tT.printStackTrace();\n\t\t\t}\n\t\t});\n\t\tpz.setOnMouseReleased(event -> {\n\t\t\ttry {\n\t\t\t\thandle((Button) event.getSource());\n\t\t\t} catch (Throwable T) {\n\t\t\t\tT.printStackTrace();\n\t\t\t}\n\t\t});\n\t\tnz.setOnMouseReleased(event -> {\n\t\t\ttry {\n\t\t\t\thandle((Button) event.getSource());\n\t\t\t} catch (Throwable T) {\n\t\t\t\tT.printStackTrace();\n\t\t\t}\n\t\t});\n\t\thome.setOnMouseReleased(event -> {\n\t\t\ttry {\n\t\t\t\thandle((Button) event.getSource());\n\t\t\t} catch (Throwable T) {\n\t\t\t\tT.printStackTrace();\n\t\t\t}\n\t\t});\n\t\tgame.setBackground(new Background(new BackgroundFill(Color.LIGHTGREEN, CornerRadii.EMPTY, Insets.EMPTY)));\n\t\tgame.setOnAction(event -> {\n\t\t\tnew Thread() {\n\t\t\t\tpublic void run() {\n\t\t\t\t\tpushThisMobileBaseAsKatapult();\n\t\t\t\t\tstartStopAction();\n\t\t\t\t}\n\t\t\t}.start();\n\n\t\t});\n\t\tconf.setOnAction(event -> {\n\t\t\tnew Thread() {\n\t\t\t\tpublic void run() {\n\t\t\t\t\tpushThisMobileBaseAsKatapult();\n\t\t\t\t\tConfigurationDatabase.save();\n\t\t\t\t}\n\t\t\t}.start();\n\t\t});\n\n\t\tgame.setTooltip(new Tooltip(\"Connect game controllers and use them to control your robot\"));\n\t\tconf.setTooltip(new Tooltip(\"Save this robot to be used in Katapult plauncher\"));\n\n\t\tbuttons = new GridPane();\n\t\tbuttons.getColumnConstraints().add(new ColumnConstraints(80)); // column 1 is 75 wide\n\t\tbuttons.getColumnConstraints().add(new ColumnConstraints(80)); // column 2 is 300 wide\n\t\tbuttons.getColumnConstraints().add(new ColumnConstraints(80)); // column 2 is 100 wide\n\n\t\tbuttons.getRowConstraints().add(new RowConstraints(40)); //\n\t\tbuttons.getRowConstraints().add(new RowConstraints(40)); //\n\t\tbuttons.getRowConstraints().add(new RowConstraints(40)); //\n\t\tbuttons.getRowConstraints().add(new RowConstraints(40)); //\n\n\t\tbuttons.add(py, 0, 1);\n\t\tbuttons.add(home, 1, 1);\n\t\tbuttons.add(ny, 2, 1);\n\n\t\tbuttons.add(px, 1, 0);\n\n\t\tbuttons.add(nx, 1, 2);\n\t\tbuttons.add(increment, 0, 3);\n\t\tbuttons.add(new Label(\"m/s\"), 1, 3);\n\t\tbuttons.add(game, 0, 4);\n\t\tbuttons.add(conf, 1, 4);\n\n\t\tbuttons.add(sec, 2, 3);\n\t\tbuttons.add(new Label(\"sec\"), 3, 3);\n\n\t\tadd(buttons, 0, 0);\n\n\t\ttry {\n\n\t\t\tcurrentFile = ScriptingEngine.fileFromGit(\"https://github.com/OperationSmallKat/Katapult.git\",\n\t\t\t\t\t\"launch.groovy\");\n\t\t} catch (GitAPIException | IOException e) {\n\t\t\t// Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t}\n\t\tmobilebase.addConnectionEventListener(new IDeviceConnectionEventListener() {\n\n\t\t\t@Override\n\t\t\tpublic void onDisconnect(BowlerAbstractDevice source) {\n\t\t\t\tstop();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onConnect(BowlerAbstractDevice source) {\n\t\t\t\t// Auto-generated method stub\n\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate void startStopAction() {\n\t\tgame.setDisable(true);\n\t\tif (running)\n\t\t\tstop();\n\t\telse\n\t\t\tstart();\n\t\tgame.setDisable(false);\n\t}\n\n\tprivate void pushThisMobileBaseAsKatapult() {\n\t\tConfigurationDatabase.setObject(\"katapult\", \"robotName\", mobilebase.getScriptingName());\n\t\tConfigurationDatabase.setObject(\"katapult\", \"robotGit\", mobilebase.getGitSelfSource()[0]);\n\t\tConfigurationDatabase.setObject(\"katapult\", \"robotGitFile\", mobilebase.getGitSelfSource()[1]);\n\t\tConfigurationDatabase.setObject(\"katapult\", \"linkDeviceName\",\n\t\t\t\tmobilebase.getAllDHChains().get(0).getLinkConfiguration(0).getDeviceScriptingName());\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tList<String> asList = (List<String>) ConfigurationDatabase.getObject(\"katapult\", \"gameControllerNames\",\n\t\t\t\tArrays.asList(\"Dragon\", \"X-Box\", \"Game\", \"Play\"));\n\n\t\tArrayList<String> fromLookup = BowlerJInputDevice.getControllers();\n\t\tfromLookup.addAll(asList);\n\t\tSet<String> uniques = new HashSet<String>(asList);\n\n\t\tConfigurationDatabase.setObject(\"katapult\", \"gameControllerNames\", Arrays.asList(uniques.toArray()));\n\t}\n\n\tprivate void reset() {\n\t\trunning = false;\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tgame.setText(\"Run Game Controller\");\n\t\t\tBowlerStudio.setToRunButton(game);\n\t\t\tgame.setGraphic(AssetFactory.loadIcon(\"Add-Game-Controller.png\"));\n\t\t});\n\n\t}\n\n\tpublic void stop() {\n\t\treset();\n\t\tThread tmp = scriptRunner;\n\t\tif (tmp != null)\n\t\t\twhile (tmp.isAlive()) {\n\n\t\t\t\tLog.debug(\"Interrupting\");\n\t\t\t\tThreadUtil.wait(10);\n\t\t\t\ttry {\n\t\t\t\t\ttmp.interrupt();\n\t\t\t\t\ttmp.join();\n\t\t\t\t} catch (InterruptedException e) {\n\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\n\t\t\t}\n\n\t\tscriptRunner = null;\n\t}\n\n\tprivate void start() {\n\n\t\trunning = true;\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tgame.setText(\"Stop Game Controller\");\n\t\t\t// game.setGraphic(AssetFactory.loadIcon(\"Stop.png\"));\n\t\t\tBowlerStudio.setToStopButton(game);\n\t\t});\n\t\tscriptRunner = new Thread() {\n\n\t\t\tpublic void run() {\n\t\t\t\ttry {\n\t\t\t\t\tScriptingEngine.inlineFileScriptRun(CSGDatabase.getInstance(), currentFile, null);\n\t\t\t\t\treset();\n\n\t\t\t\t} catch (Throwable ex) {\n\n\t\t\t\t\treset();\n\t\t\t\t}\n\n\t\t\t}\n\t\t};\n\n\t\ttry {\n\n\t\t\tscriptRunner.start();\n\t\t} catch (Exception e) {\n\t\t\t// Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t}\n\n\t}\n\n\tprivate void handle(final Button button) {\n\t\tJogThread.setProvider(this, mobilebase);\n\t\tif (!button.isPressed()) {\n\t\t\t// button released\n\t\t\t// Log.info(button.getText()+\" Button released \");\n\t\t\t// try {\n\t\t\t// TransformNR t = getKin().getCurrentTaskSpaceTransform();\n\t\t\t// if(getKin().checkTaskSpaceTransform(t))\n\t\t\t// getKin().setDesiredTaskSpaceTransform(t, 0);\n\t\t\t// } catch (Exception e) {\n\t\t\t// e.printStackTrace();\n\t\t\t// }\n\t\t\tif (button == px) {\n\t\t\t\tx = 0;\n\t\t\t}\n\t\t\tif (button == nx) {\n\t\t\t\tx = 0;\n\t\t\t}\n\t\t\tif (mobilebase == null) {\n\t\t\t\tif (button == py) {\n\t\t\t\t\ty = 0;\n\t\t\t\t}\n\t\t\t\tif (button == ny) {\n\t\t\t\t\ty = 0;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (button == py) {\n\t\t\t\t\trz = 0;\n\t\t\t\t}\n\t\t\t\tif (button == ny) {\n\t\t\t\t\trz = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (button == pz) {\n\t\t\t\tslider = 0;\n\t\t\t}\n\t\t\tif (button == nz) {\n\t\t\t\tslider = 0;\n\t\t\t}\n\t\t\tstop = true;\n\t\t\treturn;\n\t\t} else {\n\t\t\tLog.warning(button.getText() + \" Button pressed \");\n\t\t}\n\t\tif (button == px) {\n\t\t\tx = 1;\n\t\t}\n\t\tif (button == nx) {\n\t\t\tx = -1;\n\t\t}\n\t\tif (mobilebase == null) {\n\t\t\tif (button == py) {\n\t\t\t\ty = 1;\n\t\t\t}\n\t\t\tif (button == ny) {\n\t\t\t\ty = -1;\n\t\t\t}\n\t\t} else {\n\t\t\tif (button == py) {\n\t\t\t\trz = 1;\n\t\t\t}\n\t\t\tif (button == ny) {\n\t\t\t\trz = -1;\n\t\t\t}\n\t\t}\n\t\tif (button == pz) {\n\t\t\tslider = 1;\n\t\t}\n\t\tif (button == nz) {\n\t\t\tslider = -1;\n\t\t}\n\t\tif (button == home) {\n\t\t\thome();\n\t\t\tstop = true;\n\t\t\treturn;\n\t\t}\n\t\tstop = false;\n\t}\n\n\tpublic void home() {\n\n\t\tgetMobilebase().setGlobalToFiducialTransform(new TransformNR());\n\t\thomeBase(getMobilebase());\n\n\t}\n\n\tprivate void homeBase(MobileBase mb) {\n\t\tfor (DHParameterKinematics c : mb.getAllDHChains()) {\n\t\t\thomeLimb(c);\n\t\t}\n\t}\n\n\tprivate void homeLimb(DHParameterKinematics c) {\n\t\tdouble[] joints = c.getCurrentJointSpaceVector();\n\t\tfor (int i = 0; i < c.getNumberOfLinks(); i++) {\n\t\t\tjoints[i] = 0;\n\t\t\tif (c.getFollowerMobileBase(i) != null) {\n\t\t\t\thomeBase(c.getFollowerMobileBase(i));\n\t\t\t}\n\t\t}\n\t\ttry {\n\t\t\tdouble time = c.getBestTime(joints);\n\t\t\tc.setDesiredJointSpaceVector(joints, time);\n\t\t} catch (Exception e) {\n\t\t\t// Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t}\n\n\t}\n\n\t@Override\n\tpublic void onEvent(String name, float value) {\n\t\tJogThread.setProvider(this, mobilebase);\n\t\tif (name.toLowerCase().contentEquals((String) ConfigurationDatabase.getObject(paramsKey, \"jogKiny\", \"y\")))\n\t\t\tx = value;\n\t\tif (name.toLowerCase().contentEquals((String) ConfigurationDatabase.getObject(paramsKey, \"jogKinz\", \"rz\")))\n\t\t\ty = value;\n\t\tif (name.toLowerCase().contentEquals((String) ConfigurationDatabase.getObject(paramsKey, \"jogKinx\", \"x\")))\n\t\t\trz = -value;\n\t\tif (name.toLowerCase()\n\t\t\t\t.contentEquals((String) ConfigurationDatabase.getObject(paramsKey, \"jogKinslider\", \"slider\")))\n\t\t\tslider = -value;\n\t\tif (Math.abs(x) < .01)\n\t\t\tx = 0;\n\t\tif (Math.abs(y) < .01)\n\t\t\ty = 0;\n\t\tif (Math.abs(rz) < .01)\n\t\t\trz = 0;\n\t\tif (Math.abs(slider) < .01)\n\t\t\tslider = 0;\n\t\tif (x == 0.0 && y == 0.0 && rz == 0.0 && slider == 0) {\n\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"Stoping on=\"+comp.getName());\n\t\t\tstop = true;\n\n\t\t} else\n\t\t\tstop = false;\n\n\t}\n\n\tpublic MobileBase getMobilebase() {\n\t\treturn mobilebase;\n\t}\n\n\tpublic void setMobilebase(MobileBase mobilebase) {\n\t\tthis.mobilebase = mobilebase;\n\t}\n\n\t@Override\n\tpublic TransformNR getJogIncrement() {\n\t\tif (!stop) {\n\n\t\t\tdouble inc;\n\t\t\ttry {\n\t\t\t\tinc = Double.parseDouble(increment.getText()) * 10;// convert to mm\n\n\t\t\t} catch (Exception e) {\n\t\t\t\tinc = defauletSpeed;\n\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tincrement.setText(Double.toString(defauletSpeed));\n\t\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\t\tex.printStackTrace();\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t\t// double rxl=0;\n\t\t\tdouble ryl = inc / 20 * slider;\n\t\t\tdouble rzl = inc / 2 * rz;\n\t\t\tTransformNR current = new TransformNR(0, 0, 0, new RotationNR(0, rzl, 0));\n\t\t\tcurrent.translateX(inc * x);\n\t\t\tcurrent.translateY(inc * y);\n\t\t\tcurrent.translateZ(inc * slider);\n\t\t\tTransformNR toSet = current.copy();\n\t\t\treturn toSet;\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/creature/JogThread.java",
    "content": "package com.neuronrobotics.bowlerstudio.creature;\n\nimport java.time.Duration;\nimport java.util.HashMap;\n\nimport com.neuronrobotics.bowlerstudio.BowlerStudioController;\nimport com.neuronrobotics.sdk.addons.kinematics.AbstractKinematicsNR;\nimport com.neuronrobotics.sdk.addons.kinematics.DHParameterKinematics;\nimport com.neuronrobotics.sdk.addons.kinematics.MobileBase;\nimport com.neuronrobotics.sdk.addons.kinematics.math.TransformNR;\nimport com.neuronrobotics.sdk.common.Log;\nimport com.neuronrobotics.sdk.common.TickToc;\nimport com.neuronrobotics.sdk.util.ThreadUtil;\npublic class JogThread {\n\tprivate static jogThread thread = null;\n\tprivate static Thread timer = null;\n\tprivate static HashMap<Duration, Long> startTimes = new HashMap<>();\n\tprivate static HashMap<Duration, Runnable> runnables = new HashMap<>();\n\n\tprivate static boolean controlThreadRunning = false;\n\tprivate static AbstractKinematicsNR source;\n\tprivate static IJogProvider provider = null;\n\tprivate static final double toSeconds = .032;\n\t// public static boolean setTarget(AbstractKinematicsNR source, TransformNR\n\t// toSet, double toSeconds) {\n\t// JogThread.source = source;\n\t// if (thread == null) {\n\t// Log.enableSystemPrint(true);\n\t// thread = new jogThread();\n\t// thread.start();\n\t// }\n\t//\n\t// return thread.setTarget(toSet, toSeconds);\n\t//\n\t// }\n\n\tpublic static boolean isControlThreadRunning() {\n\t\treturn controlThreadRunning;\n\t}\n\n\tprivate static void setControlThreadRunning(boolean controlThreadRunning) {\n\t\tJogThread.controlThreadRunning = controlThreadRunning;\n\t}\n\n\tpublic static IJogProvider getProvider() {\n\t\treturn provider;\n\t}\n\n\tpublic static void setProvider(IJogProvider provider, AbstractKinematicsNR s) {\n\t\tJogThread.provider = provider;\n\t\tsource = s;\n\t\t// new\n\t\t// Exception(s.getClass().getName()+\"\\n\"+provider.getClass().getName()).printStackTrace();\n\n\t\tif (thread == null) {\n\t\t\tthread = new jogThread();\n\t\t\tthread.start();\n\t\t}\n\t}\n\n\tpublic static double getToseconds() {\n\t\treturn toSeconds;\n\t}\n\n\tprivate static class jogThread extends Thread {\n\n\t\tprivate TransformNR toSet;\n\n\t\t// private long time = System.currentTimeMillis();\n\n\t\tpublic void run() {\n\t\t\tsetName(\" Jog Widget thread\");\n\t\t\tlong threadStart = System.currentTimeMillis();\n\t\t\t// long index = 0;\n\t\t\twhile (source.isAvailable()) {\n\t\t\t\tthreadStart = System.currentTimeMillis();\n\t\t\t\tTransformNR tr = provider.getJogIncrement();\n\t\t\t\tif (setTarget(tr)) {\n\t\t\t\t\tdouble bestTime = getToseconds();\n\t\t\t\t\tif (isControlThreadRunning()) {\n\t\t\t\t\t\t// TickToc.setEnabled(true);\n\t\t\t\t\t\tif (MobileBase.class.isInstance(source)) {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t((MobileBase) source).DriveArc(toSet, bestTime);\n\t\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\t\t// e.printStackTrace();\n\t\t\t\t\t\t\t\tBowlerStudioController.highlightException(null, e);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else if (DHParameterKinematics.class.isInstance(source)) {\n\t\t\t\t\t\t\tDHParameterKinematics kin = (DHParameterKinematics) source;\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t// Log.enableDebugPrint();\n\t\t\t\t\t\t\t\tTickToc.tic(\"Jogging \");\n\t\t\t\t\t\t\t\tbestTime = kin.getBestTime(toSet);\n\t\t\t\t\t\t\t\tif (bestTime < getToseconds())\n\t\t\t\t\t\t\t\t\tbestTime = getToseconds();\n\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\n\t\t\t\t\t\t\t\t\t\t\t\"Jog paused for links to catch up \" + bestTime + \" vs \" + getToseconds());\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tTickToc.tic(\"computed best time \" + bestTime);\n\t\t\t\t\t\t\t\tkin.setDesiredTaskSpaceTransform(toSet, bestTime);\n\t\t\t\t\t\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"Joging to \"+toSet);\n\t\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Exception in Jog Thread \" + e.getMessage());\n\t\t\t\t\t\t\t\t// BowlerStudioController.highlightException(null, e);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tsetControlThreadRunning(false);\n\t\t\t\t\t} else\n\t\t\t\t\t\tTickToc.setEnabled(false);\n\n\t\t\t\t\tdouble ms = bestTime * 1000.0;\n\t\t\t\t\tlong gate = ((long) ms) + threadStart - System.currentTimeMillis();\n\t\t\t\t\tTickToc.tic(\"Jog Thread set Done \" + System.currentTimeMillis() + \" waiting for \" + gate);\n\t\t\t\t\tif (gate > 0)\n\t\t\t\t\t\tThreadUtil.wait((int) gate);\n\t\t\t\t\tTickToc.toc();\n\t\t\t\t} else {\n\t\t\t\t\tThreadUtil.wait(1);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthread = null;\n\t\t}\n\n\t\tprivate boolean setTarget(TransformNR toSet) {\n\t\t\tif (toSet == null)\n\t\t\t\treturn false;\n\t\t\tthis.toSet = toSet.copy();\n\t\t\tif (DHParameterKinematics.class.isInstance(source))\n\t\t\t\tif (!((DHParameterKinematics) source).checkTaskSpaceTransform(toSet)) {\n\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"\\n\\nERROR Target unreachable \" + toSet);\n\t\t\t\t\tint level = Log.getMinimumPrintLevel();\n\t\t\t\t\tLog.enableErrorPrint();\n\t\t\t\t\t((DHParameterKinematics) source).checkTaskSpaceTransform(toSet);\n\t\t\t\t\tLog.setMinimumPrintLevel(level);\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t// this.toSeconds = toSeconds;\n\t\t\tsetControlThreadRunning(true);\n\n\t\t\treturn true;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/creature/JogWidget.java",
    "content": "package com.neuronrobotics.bowlerstudio.creature;\n\nimport com.neuronrobotics.bowlerstudio.ConnectionManager;\nimport com.neuronrobotics.bowlerstudio.assets.AssetFactory;\nimport com.neuronrobotics.bowlerstudio.assets.ConfigurationDatabase;\nimport com.neuronrobotics.sdk.addons.gamepad.BowlerJInputDevice;\nimport com.neuronrobotics.sdk.addons.gamepad.IGameControlEvent;\nimport com.neuronrobotics.sdk.addons.gamepad.JogTrainerWidget;\nimport com.neuronrobotics.sdk.addons.kinematics.AbstractKinematicsNR;\nimport com.neuronrobotics.sdk.addons.kinematics.DHParameterKinematics;\nimport com.neuronrobotics.sdk.addons.kinematics.IJointSpaceUpdateListenerNR;\nimport com.neuronrobotics.sdk.addons.kinematics.ITaskSpaceUpdateListenerNR;\nimport com.neuronrobotics.sdk.addons.kinematics.JointLimit;\nimport com.neuronrobotics.sdk.addons.kinematics.MobileBase;\nimport com.neuronrobotics.sdk.addons.kinematics.math.TransformNR;\nimport com.neuronrobotics.sdk.common.BowlerAbstractDevice;\nimport com.neuronrobotics.sdk.common.DeviceManager;\nimport com.neuronrobotics.sdk.common.IDeviceConnectionEventListener;\nimport javafx.event.EventHandler;\nimport javafx.scene.control.*;\nimport javafx.scene.input.MouseEvent;\nimport javafx.scene.layout.ColumnConstraints;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.RowConstraints;\nimport javafx.stage.Stage;\n\nimport java.util.ArrayList;\n\npublic class JogWidget extends GridPane\n\t\timplements\n\t\t\tITaskSpaceUpdateListenerNR,\n\t\t\tIOnTransformChange,\n\t\t\tIGameControlEvent,\n\t\t\tEventHandler<MouseEvent>,\n\t\t\tIJogProvider {\n\tdouble defauletSpeed = 0.2;\n\tprivate DHParameterKinematics kinematics;\n\tButton px = new Button(\"\", AssetFactory.loadIcon(\"Plus-X.png\"));\n\tButton nx = new Button(\"\", AssetFactory.loadIcon(\"Minus-X.png\"));\n\tButton py = new Button(\"\", AssetFactory.loadIcon(\"Plus-Y.png\"));\n\tButton ny = new Button(\"\", AssetFactory.loadIcon(\"Minus-Y.png\"));\n\tButton pz = new Button(\"\", AssetFactory.loadIcon(\"Plus-Z.png\"));\n\tButton nz = new Button(\"\", AssetFactory.loadIcon(\"Minus-Z.png\"));\n\tButton home = new Button(\"\", AssetFactory.loadIcon(\"Home.png\"));\n\tButton game = new Button(\"Add Game Controller\", AssetFactory.loadIcon(\"Add-Game-Controller.png\"));\n\tButton conf = new Button(\"Configure...\", AssetFactory.loadIcon(\"Configure-Game-Controller.png\"));\n\tTextField increment = new TextField(Double.toString(defauletSpeed));\n\tprivate TransformWidget transformCurrent;\n\tprivate TransformWidget transformTarget;\n\tprivate BowlerJInputDevice gameController = null;\n\tdouble x, y, rz, slider = 0;\n\tprivate boolean stop = true;\n\tprivate String paramsKey;\n\tprivate GridPane buttons;\n\tprivate static ArrayList<JogWidget> allWidgets = new ArrayList<JogWidget>();\n\tprivate MobileBase source;\n\tprivate TransformNR tmpSet = null;\n\tpublic JogWidget(DHParameterKinematics k, MobileBase source) {\n\t\tthis.source = source;\n\t\tallWidgets.add(this);\n\t\tJogWidget w = this;\n\t\tsource.addConnectionEventListener(new IDeviceConnectionEventListener() {\n\n\t\t\t@Override\n\t\t\tpublic void onDisconnect(BowlerAbstractDevice source) {\n\t\t\t\tallWidgets.remove(w);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onConnect(BowlerAbstractDevice source) {\n\t\t\t\t// Auto-generated method stub\n\n\t\t\t}\n\t\t});\n\t\tthis.setKin(k);\n\t\t// paralell group listener\n\t\tgetKin().addPoseUpdateListener(this);\n\t\t// just a limb listener. if is not paralell group will not add internally\n\t\tk.addPoseUpdateListener(this);\n\t\tk.addJointSpaceListener(new IJointSpaceUpdateListenerNR() {\n\n\t\t\t@Override\n\t\t\tpublic void onJointSpaceUpdate(AbstractKinematicsNR source, double[] joints) {\n\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onJointSpaceTargetUpdate(AbstractKinematicsNR source, double[] joints) {\n\t\t\t\tupdatePose(joints);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onJointSpaceLimit(AbstractKinematicsNR source, int axis, JointLimit event) {\n\t\t\t\t// Auto-generated method stub\n\n\t\t\t}\n\t\t});\n\n\t\tpx.setOnMousePressed(this);\n\t\tnx.setOnMousePressed(this);\n\t\tpy.setOnMousePressed(this);\n\t\tny.setOnMousePressed(this);\n\t\tpz.setOnMousePressed(this);\n\t\tnz.setOnMousePressed(this);\n\t\thome.setOnMousePressed(this);\n\n\t\tpx.setOnMouseReleased(this);\n\t\tnx.setOnMouseReleased(this);\n\t\tpy.setOnMouseReleased(this);\n\t\tny.setOnMouseReleased(this);\n\t\tpz.setOnMouseReleased(this);\n\t\tnz.setOnMouseReleased(this);\n\t\thome.setOnMouseReleased(this);\n\t\tgame.setOnAction(event -> {\n\t\t\tif (getGameController() == null) {\n\t\t\t\tsetGameController((BowlerJInputDevice) DeviceManager.getSpecificDevice(BowlerJInputDevice.class,\n\t\t\t\t\t\t\"jogController\"));\n\t\t\t\tif (getGameController() == null) {\n\t\t\t\t\tConnectionManager.onConnectGamePad();\n\t\t\t\t\tsetGameController((BowlerJInputDevice) DeviceManager.getSpecificDevice(BowlerJInputDevice.class,\n\t\t\t\t\t\t\t\"jogController\"));\n\t\t\t\t}\n\n\t\t\t} else {\n\t\t\t\tRemoveGameController();\n\t\t\t}\n\t\t});\n\t\tconf.setOnAction(event -> {\n\t\t\tif (getGameController() != null) {\n\t\t\t\trunControllerMap();\n\t\t\t}\n\t\t});\n\n\t\tbuttons = new GridPane();\n\t\tbuttons.getColumnConstraints().add(new ColumnConstraints(80)); // column 1 is 75 wide\n\t\tbuttons.getColumnConstraints().add(new ColumnConstraints(80)); // column 2 is 300 wide\n\t\tbuttons.getColumnConstraints().add(new ColumnConstraints(80)); // column 2 is 100 wide\n\n\t\tbuttons.getRowConstraints().add(new RowConstraints(40)); //\n\t\tbuttons.getRowConstraints().add(new RowConstraints(40)); //\n\t\tbuttons.getRowConstraints().add(new RowConstraints(40)); //\n\t\tbuttons.getRowConstraints().add(new RowConstraints(40)); //\n\n\t\tbuttons.add(py, 0, 1);\n\t\tbuttons.add(home, 1, 1);\n\t\tbuttons.add(ny, 2, 1);\n\n\t\tbuttons.add(px, 1, 0);\n\n\t\tbuttons.add(nx, 1, 2);\n\t\tbuttons.add(increment, 0, 3);\n\t\tbuttons.add(new Label(\"m/s\"), 1, 3);\n\n\t\tbuttons.add(pz, 3, 0);\n\t\tbuttons.add(nz, 3, 1);\n\n\t\tadd(buttons, 0, 0);\n\t\ttransformCurrent = new TransformWidget(\"Current Pose\", getKin().getCurrentTaskSpaceTransform(), this);\n\t\ttransformCurrent.setDisable(true);\n\t\ttransformTarget = new TransformWidget(\"Current Target\", getKin().getCurrentPoseTarget(), this);\n\n\t\tAccordion advancedPanel = new Accordion();\n\t\tadvancedPanel.getPanes().add(new TitledPane(\"Current Pose\", transformCurrent));\n\t\tadvancedPanel.getPanes().add(new TitledPane(\"Current Target\", transformTarget));\n\t\tadd(advancedPanel, 0, 1);\n\t\ttmpSet = getKin().getCurrentTaskSpaceTransform();\n\t\thandleButton(home);\n\n\t}\n\n\t@Override\n\tpublic void onTransformChaging(TransformNR newTrans) {\n\t\tsetNewTarget(newTrans);\n\t}\n\n\tprivate void setNewTarget(TransformNR newTrans) {\n\t\tJogThread.setProvider(this, getKin());\n\t\tif (getKin().checkTaskSpaceTransform(newTrans))\n\t\t\ttmpSet = newTrans;\n\t}\n\n\t@Override\n\tpublic void onTransformFinished(TransformNR newTrans) {\n\t\tif (getKin().checkTaskSpaceTransform(newTrans))\n\t\t\tsetNewTarget(newTrans);\n\t\telse\n\t\t\ttransformTarget.updatePose(getKin().getCurrentPoseTarget());\n\t}\n\n\tpublic void setCurrent(TransformNR currentPoseTarget) {\n\t\tsetNewTarget(currentPoseTarget);\n\t}\n\n\tpublic DHParameterKinematics getKin() {\n\t\tif (source.getParallelGroup(kinematics) != null) {\n\t\t\treturn source.getParallelGroup(kinematics);\n\t\t}\n\t\treturn kinematics;\n\t}\n\n\tpublic void setKin(DHParameterKinematics kin) {\n\t\tif (!kin.isAvailable())\n\t\t\tkin.connect();\n\t\tthis.kinematics = kin;\n\t}\n\n\tprivate void handleButton(final Button button) {\n\t\tJogThread.setProvider(this, getKin());\n\t\tif (!button.isPressed()) {\n\n\t\t\tif (button == px) {\n\t\t\t\tx = 0;\n\t\t\t}\n\t\t\tif (button == nx) {\n\t\t\t\tx = 0;\n\t\t\t}\n\n\t\t\tif (button == py) {\n\t\t\t\ty = 0;\n\t\t\t}\n\t\t\tif (button == ny) {\n\t\t\t\ty = 0;\n\t\t\t}\n\n\t\t\tif (button == pz) {\n\t\t\t\tslider = 0;\n\t\t\t}\n\t\t\tif (button == nz) {\n\t\t\t\tslider = 0;\n\t\t\t}\n\t\t\tstop = true;\n\t\t\treturn;\n\t\t}\n\t\tif (button == px) {\n\t\t\tx = 1;\n\t\t}\n\t\tif (button == nx) {\n\t\t\tx = -1;\n\t\t}\n\t\tif (button == py) {\n\t\t\ty = 1;\n\t\t}\n\t\tif (button == ny) {\n\t\t\ty = -1;\n\t\t}\n\n\t\tif (button == pz) {\n\t\t\tslider = 1;\n\t\t}\n\t\tif (button == nz) {\n\t\t\tslider = -1;\n\t\t}\n\t\tif (button == home) {\n\t\t\thome();\n\t\t\tstop = true;\n\t\t\treturn;\n\t\t}\n\t\tstop = false;\n\t}\n\n\tpublic void home() {\n\t\tnew Thread(() -> {\n\t\t\thomeLimb(getKin());\n\t\t}).start();\n\t}\n\n\tprivate void homeBase(MobileBase mb) {\n\t\tfor (DHParameterKinematics c : mb.getAllDHChains()) {\n\t\t\thomeLimb(c);\n\t\t}\n\t}\n\n\tprivate void homeLimb(DHParameterKinematics c) {\n\t\tdouble[] joints = c.getCurrentJointSpaceVector();\n\t\tfor (int i = 0; i < c.getNumberOfLinks(); i++) {\n\t\t\tjoints[i] = 0;\n\t\t}\n\t\ttry {\n\t\t\tc.setDesiredJointSpaceVector(joints, c.getBestTime(joints));\n\t\t} catch (Exception e) {\n\t\t\t// Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t}\n\t\tfor (int i = 0; i < c.getNumberOfLinks(); i++) {\n\t\t\ttry {\n\t\t\t\tif (c.getFollowerMobileBase(i) != null) {\n\t\t\t\t\thomeBase(c.getFollowerMobileBase(i));\n\t\t\t\t}\n\t\t\t} catch (Exception ex) {\n\t\t\t\tex.printStackTrace();\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onTaskSpaceUpdate(AbstractKinematicsNR source, TransformNR pose) {\n\t\tupdatePose(getKin().getCurrentJointSpaceVector());\n\t}\n\n\tprivate void updatePose(double[] joints) {\n\t\tTransformNR currentTaskSpaceTransform = getKin().forwardOffset(getKin().forwardKinematics(joints));\n\t\tif (joints != null && transformCurrent != null)\n\t\t\tMobileBaseCadManager.runLater(new Runnable() {\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\ttransformCurrent.updatePose(currentTaskSpaceTransform);\n\t\t\t\t}\n\t\t\t});\n\t}\n\n\t@Override\n\tpublic void onTargetTaskSpaceUpdate(AbstractKinematicsNR source, TransformNR pose) {\n\t\tif (transformTarget != null && pose != null)\n\t\t\ttransformTarget.updatePose(pose);\n\t}\n\n\t@Override\n\tpublic void onEvent(String name, float value) {\n\t\tJogThread.setProvider(this, getKin());\n\t\tif (name.toLowerCase().contentEquals((String) ConfigurationDatabase.getObject(paramsKey, \"jogKiny\", \"y\")))\n\t\t\tx = value;\n\t\tif (name.toLowerCase().contentEquals((String) ConfigurationDatabase.getObject(paramsKey, \"jogKinz\", \"rz\")))\n\t\t\ty = value;\n\t\tif (name.toLowerCase().contentEquals((String) ConfigurationDatabase.getObject(paramsKey, \"jogKinx\", \"x\")))\n\t\t\trz = -value;\n\t\tif (name.toLowerCase()\n\t\t\t\t.contentEquals((String) ConfigurationDatabase.getObject(paramsKey, \"jogKinslider\", \"slider\")))\n\t\t\tslider = -value;\n\t\tif (Math.abs(x) < .01)\n\t\t\tx = 0;\n\t\tif (Math.abs(y) < .01)\n\t\t\ty = 0;\n\t\tif (Math.abs(rz) < .01)\n\t\t\trz = 0;\n\t\tif (Math.abs(slider) < .01)\n\t\t\tslider = 0;\n\t\tif (x == 0.0 && y == 0.0 && rz == 0.0 && slider == 0) {\n\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"Stoping on=\"+comp.getName());\n\t\t\tstop = true;\n\t\t\ttry {\n\t\t\t\tgetKin().setDesiredTaskSpaceTransform(getKin().getCurrentTaskSpaceTransform(), 0);\n\t\t\t} catch (Exception e) {\n\t\t\t\t// Auto-generated catch block\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t} else\n\t\t\tstop = false;\n\n\t}\n\n\tpublic BowlerJInputDevice getGameController() {\n\t\treturn gameController;\n\t}\n\n\tpublic void setGameController(BowlerJInputDevice gameController) {\n\t\tthis.gameController = gameController;\n\t\tif (gameController != null) {\n\t\t\tgetGameController().clearListeners();\n\t\t\tgetGameController().addListeners(this);\n\t\t\tgame.setText(\"Remove Game Controller\");\n\n\t\t\tparamsKey = gameController.getControllerName();\n\t\t\t// HashMap<String, Object> map = ConfigurationDatabase.getParamMap(paramsKey);\n\t\t\tboolean hasmap = false;\n\t\t\tif (ConfigurationDatabase.containsKey(paramsKey, \"jogKinx\")\n\t\t\t\t\t&& ConfigurationDatabase.containsKey(paramsKey, \"jogKiny\")\n\t\t\t\t\t&& ConfigurationDatabase.containsKey(paramsKey, \"jogKinz\")\n\t\t\t\t\t&& ConfigurationDatabase.containsKey(paramsKey, \"jogKinslider\")) {\n\t\t\t\thasmap = true;\n\t\t\t}\n\n\t\t\tif (!hasmap) {\n\t\t\t\trunControllerMap();\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate BowlerJInputDevice RemoveGameController() {\n\t\tBowlerJInputDevice stale = getGameController();\n\t\tgetGameController().removeListeners(this);\n\t\tgame.setText(\"Add Game Controller\");\n\t\tsetGameController(null);\n\t\treturn stale;\n\t}\n\n\tprivate void runControllerMap() {\n\t\tStage s = new Stage();\n\t\tnew Thread() {\n\t\t\tpublic void run() {\n\t\t\t\tJogTrainerWidget controller = new JogTrainerWidget(gameController);\n\t\t\t\ttry {\n\t\t\t\t\tcontroller.start(s);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\n\t\t\t}\n\t\t}.start();\n\t}\n\n\t@Override\n\tpublic void handle(MouseEvent event) {\n\n\t\ttry {\n\t\t\thandleButton((Button) event.getSource());\n\t\t} catch (Throwable T) {\n\t\t\tT.printStackTrace();\n\t\t}\n\t}\n\n\t@Override\n\tpublic TransformNR getJogIncrement() {\n\t\tif (tmpSet != null) {\n\t\t\tTransformNR ret = tmpSet;\n\t\t\ttmpSet = null;\n\t\t\treturn ret;\n\t\t}\n\t\tif (!stop) {\n\t\t\tTransformNR current = getKin().getCurrentPoseTarget().copy();\n\n\t\t\ttry {\n\t\t\t\tdouble mmPerSecond = Double.parseDouble(increment.getText()) * 1000;\n\t\t\t\tdouble translationx = JogThread.getToseconds() * x * mmPerSecond;\n\t\t\t\tdouble translationy = JogThread.getToseconds() * y * mmPerSecond;\n\t\t\t\tdouble translationz = JogThread.getToseconds() * slider * mmPerSecond;\n\n\t\t\t\tcurrent.translateX(translationx);\n\t\t\t\tcurrent.translateY(translationy);\n\t\t\t\tcurrent.translateZ(translationz);\n\t\t\t\t// current.setRotation(new RotationNR());\n\t\t\t\tif (((DHParameterKinematics) getKin()).checkTaskSpaceTransform(current)) {\n\t\t\t\t\treturn current;\n\t\t\t\t}\n\n\t\t\t} catch (Exception e) {\n\t\t\t\t// Auto-generated catch block\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/creature/LinkConfigurationWidget.java",
    "content": "package com.neuronrobotics.bowlerstudio.creature;\n\nimport java.util.Map;\n\nimport com.neuronrobotics.bowlerstudio.BowlerStudio;\nimport com.neuronrobotics.bowlerstudio.vitamins.Vitamins;\nimport com.neuronrobotics.sdk.addons.kinematics.AbstractLink;\nimport com.neuronrobotics.sdk.addons.kinematics.LinkConfiguration;\nimport com.neuronrobotics.sdk.addons.kinematics.LinkFactory;\nimport com.neuronrobotics.sdk.addons.kinematics.LinkType;\nimport com.neuronrobotics.sdk.addons.kinematics.math.TransformNR;\n\nimport eu.mihosoft.vrl.v3d.parametrics.CSGDatabase;\nimport javafx.event.ActionEvent;\nimport javafx.event.EventHandler;\nimport javafx.scene.control.ComboBox;\nimport javafx.scene.control.TextField;\nimport javafx.scene.layout.ColumnConstraints;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.text.Text;\nimport java.time.Duration;\nimport javafx.scene.control.CheckBox;\n\n@SuppressWarnings(\"restriction\")\npublic class LinkConfigurationWidget extends GridPane implements ITrimControl {\n\n\t// private int index;\n\tprivate LinkConfiguration conf;\n\tprivate EngineeringUnitsSliderWidget zero;\n\tprivate EngineeringUnitsSliderWidget lowerBound;\n\tprivate EngineeringUnitsSliderWidget upperBound;\n\tprivate AbstractLink activLink;\n\tprivate MobileBaseCadManager manager;\n\t// private EngineeringUnitsSliderWidget setpointSLider;\n\n\tprivate double zeroValue = 0;\n\n\tdouble textToNum(TextField mass) {\n\t\ttry {\n\t\t\treturn Double.parseDouble(mass.getText().trim());\n\t\t} catch (Throwable t) {\n\t\t\tmass.setText(\"0\");\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tpublic LinkConfigurationWidget(LinkConfiguration congiuration, LinkFactory factory, MobileBaseCadManager manager) {\n\t\t// this.index = index;\n\t\t// this.congiuration = congiuration;\n\t\tconf = congiuration;\n\t\t// this.setpointSLider = slide;\n\t\tthis.manager = manager;\n\t\tactivLink = factory.getLink(conf);\n\t\tgetColumnConstraints().add(new ColumnConstraints(150)); // column 1 is 75 wide\n\t\tgetColumnConstraints().add(new ColumnConstraints(200)); // column 2 is 300 wide\n\t\tgetColumnConstraints().add(new ColumnConstraints(200)); // column 2 is 300 wide\n\t\tsetHgap(20);\n\n\t\tTextField mass = new TextField(CreatureLab.getFormatted(conf.getMassKg()));\n\t\tmass.setOnAction(event -> {\n\t\t\tconf.setMassKg(textToNum(mass));\n\t\t\tactivLink.setTargetEngineeringUnits(0);\n\t\t\tactivLink.flush(0);\n\t\t\tif (manager != null)\n\t\t\t\tmanager.generateCad(CSGDatabase.getInstance());\n\t\t});\n\t\tTransformNR currentCentroid = conf.getCenterOfMassFromCentroid();\n\t\tTextField massx = new TextField(CreatureLab.getFormatted(currentCentroid.getX()));\n\t\tmassx.setOnAction(event -> {\n\t\t\tcurrentCentroid.setX(textToNum(massx));\n\t\t\tconf.setCenterOfMassFromCentroid(currentCentroid);;\n\t\t\tactivLink.setTargetEngineeringUnits(0);\n\t\t\tactivLink.flush(0);\n\t\t\tif (manager != null)\n\t\t\t\tmanager.generateCad(CSGDatabase.getInstance());\n\n\t\t});\n\n\t\tTextField massy = new TextField(CreatureLab.getFormatted(currentCentroid.getY()));\n\t\tmassy.setOnAction(event -> {\n\t\t\tcurrentCentroid.setY(textToNum(massy));\n\t\t\tconf.setCenterOfMassFromCentroid(currentCentroid);;\n\t\t\tactivLink.setTargetEngineeringUnits(0);\n\t\t\tactivLink.flush(0);\n\t\t\tif (manager != null)\n\t\t\t\tmanager.generateCad(CSGDatabase.getInstance());\n\n\t\t});\n\n\t\tTextField massz = new TextField(CreatureLab.getFormatted(currentCentroid.getZ()));\n\t\tmassz.setOnAction(event -> {\n\t\t\tcurrentCentroid.setZ(textToNum(massz));\n\t\t\tconf.setCenterOfMassFromCentroid(currentCentroid);;\n\t\t\tactivLink.setTargetEngineeringUnits(0);\n\t\t\tactivLink.flush(0);\n\t\t\tif (manager != null)\n\t\t\t\tmanager.generateCad(CSGDatabase.getInstance());\n\n\t\t});\n\n\t\tTextField scale = new TextField(CreatureLab.getFormatted(conf.getScale()));\n\t\tscale.setOnAction(event -> {\n\t\t\tconf.setScale(textToNum(scale));\n\t\t\tactivLink.setTargetEngineeringUnits(0);\n\t\t\tactivLink.flush(0);\n\t\t\tif (manager != null)\n\t\t\t\tmanager.generateCad(CSGDatabase.getInstance());\n\n\t\t});\n\n\t\tfinal ComboBox<String> shaftSize = new ComboBox<>();\n\t\tfinal ComboBox<String> shaftType = new ComboBox<>();\n\t\tfor (String s : Vitamins.listVitaminSizes(conf.getShaftType())) {\n\t\t\tshaftSize.getItems().add(s);\n\t\t}\n\t\tshaftSize.setOnAction(event -> {\n\t\t\tString motorsize = shaftSize.getSelectionModel().getSelectedItem();\n\t\t\tString motortype = shaftType.getSelectionModel().getSelectedItem();\n\t\t\tif (motorsize == null || motortype == null)\n\t\t\t\treturn;\n\t\t\tconf.setShaftSize(motorsize);\n\t\t\tconf.setShaftType(motortype);\n\t\t\tsetShaftSize(motorsize);\n\t\t\tif (manager != null)\n\t\t\t\tmanager.generateCad(CSGDatabase.getInstance());\n\n\t\t});\n\t\tshaftSize.getSelectionModel().select(conf.getShaftSize());\n\n\t\tfor (String vitaminsType : Vitamins.listVitaminTypes()) {\n\t\t\tMap<String, Object> meta = Vitamins.getMeta(vitaminsType);\n\t\t\tif (meta != null && meta.containsKey(\"shaft\"))\n\t\t\t\tshaftType.getItems().add(vitaminsType);\n\t\t}\n\n\t\tshaftType.setOnAction(event -> {\n\t\t\tString selectedItem = shaftType.getSelectionModel().getSelectedItem();\n\t\t\tsetShaftType(shaftSize, selectedItem);\n\t\t});\n\t\tshaftType.getSelectionModel().select(conf.getShaftType());\n\t\tfinal ComboBox<String> emHardwareType = new ComboBox<>();\n\t\tfinal ComboBox<String> emHardwareSize = new ComboBox<>();\n\t\tfor (String s : Vitamins.listVitaminSizes(conf.getElectroMechanicalType())) {\n\t\t\temHardwareSize.getItems().add(s);\n\t\t}\n\t\temHardwareSize.setOnAction(new EventHandler<ActionEvent>() {\n\t\t\t@Override\n\t\t\tpublic void handle(ActionEvent event) {\n\t\t\t\tString motortype = emHardwareType.getSelectionModel().getSelectedItem();\n\t\t\t\tString motorsize = emHardwareSize.getSelectionModel().getSelectedItem();\n\t\t\t\tif (motorsize == null || motortype == null)\n\t\t\t\t\treturn;\n\t\t\t\tconf.setElectroMechanicalType(motortype);\n\t\t\t\tconf.setElectroMechanicalSize(motorsize);\n\t\t\t\t// newHardware.setText(\"New \" + conf.getElectroMechanicalType());\n\t\t\t\t// editHardware.setText(\"Edit \" + conf.getElectroMechanicalSize());\n\t\t\t\t// Map<String, Object> vitaminData =\n\t\t\t\t// Vitamins.getConfiguration(conf.getElectroMechanicalType(),\n\t\t\t\t// conf.getElectroMechanicalSize());\n\t\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"New size \" + vitaminData);\n\t\t\t\tString shafttype = (String) Vitamins.getMeasurement(conf.getElectroMechanicalType(),\n\t\t\t\t\t\tconf.getElectroMechanicalSize(), \"shaftType\");\n\t\t\t\tString shaftsize = (String) Vitamins.getMeasurement(conf.getElectroMechanicalType(),\n\t\t\t\t\t\tconf.getElectroMechanicalSize(), \"shaftSize\");\n\n\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\tsetShaftType(shaftSize, shafttype);\n\t\t\t\t\tBowlerStudio.runLater(Duration.ofMillis(20), () -> {\n\t\t\t\t\t\tsetShaftSize(shaftsize);\n\t\t\t\t\t\tBowlerStudio.runLater(Duration.ofMillis(200), new Runnable() {\n\t\t\t\t\t\t\t@Override\n\t\t\t\t\t\t\tpublic void run() {\n\n\t\t\t\t\t\t\t\tcom.neuronrobotics.sdk.common.Log\n\t\t\t\t\t\t\t\t\t\t.error(\"Settting shaft size: \" + shaftsize + \" of \" + shafttype);\n\n\t\t\t\t\t\t\t\tBowlerStudio.runLater(() -> shaftType.getSelectionModel().select(shafttype));\n\t\t\t\t\t\t\t\tBowlerStudio.runLater(() -> shaftSize.getSelectionModel().select(shaftsize));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t\tconf.setShaftSize(shaftsize);\n\t\t\t\tconf.setShaftType(shafttype);\n\t\t\t\tif (manager != null)\n\t\t\t\t\tmanager.generateCad(CSGDatabase.getInstance());\n\n\t\t\t}\n\t\t});\n\t\temHardwareSize.getSelectionModel().select(conf.getElectroMechanicalSize());\n\n\t\tfor (String vitaminsType : Vitamins.listVitaminTypes()) {\n\t\t\tMap<String, Object> meta = Vitamins.getMeta(vitaminsType);\n\t\t\tif (meta != null && meta.containsKey(\"actuator\"))\n\t\t\t\temHardwareType.getItems().add(vitaminsType);\n\t\t}\n\t\temHardwareType.setOnAction(event -> {\n\t\t\tString selectedItem = emHardwareType.getSelectionModel().getSelectedItem();\n\t\t\tif (selectedItem == null)\n\t\t\t\treturn;\n\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"New hwType \" + selectedItem);\n\n\t\t\temHardwareSize.getItems().clear();\n\t\t\tfor (String s : Vitamins.listVitaminSizes(selectedItem)) {\n\t\t\t\temHardwareSize.getItems().add(s);\n\t\t\t}\n\t\t\t// newHardware.setText(\"New \" + conf.getElectroMechanicalType());\n\n\t\t});\n\t\temHardwareType.getSelectionModel().select(conf.getElectroMechanicalType());\n\n\t\tTextField deviceName = new TextField(congiuration.getDeviceScriptingName());\n\t\tdeviceName.setOnAction(event -> {\n\t\t\tconf.setDeviceScriptingName(deviceName.getText());\n\t\t\tfactory.refreshHardwareLayer(conf);\n\t\t\tactivLink = factory.getLink(conf);\n\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Link device to \" + conf.getDeviceScriptingName());\n\t\t\tif (manager != null)\n\t\t\t\tmanager.generateCad(CSGDatabase.getInstance());\n\n\t\t});\n\n\t\tadd(new Text(\"Scale To Degrees \"), 0, 0);\n\t\tadd(scale, 1, 0);\n\t\tadd(new Text(\"(unitless)\"), 2, 0);\n\n\t\tdouble min = activLink.getDeviceMinimumValue();\n\t\tlowerBound = new EngineeringUnitsSliderWidget(new IOnEngineeringUnitsChange() {\n\n\t\t\t@Override\n\t\t\tpublic void onSliderMoving(EngineeringUnitsSliderWidget source, double newAngleDegrees) {\n\t\t\t\tdouble eng = setLowerBound(newAngleDegrees);\n\t\t\t\tactivLink.setUseLimits(false);\n\t\t\t\tactivLink.setTargetEngineeringUnits(eng);\n\t\t\t\tactivLink.flush(0);\n\t\t\t\tactivLink.setUseLimits(true);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onSliderDoneMoving(EngineeringUnitsSliderWidget source, double newAngleDegrees) {\n\t\t\t\ttry {\n\t\t\t\t\tactivLink.setUseLimits(false);\n\t\t\t\t\tactivLink.setTargetEngineeringUnits(setLowerBound(newAngleDegrees));\n\t\t\t\t\tactivLink.flush(0);\n\t\t\t\t\tactivLink.setUseLimits(true);\n\t\t\t\t\tif (manager != null)\n\t\t\t\t\t\tmanager.generateCad(CSGDatabase.getInstance());\n\t\t\t\t\tzero.setLowerBound(newAngleDegrees);\n\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\tBowlerStudio.printStackTrace(ex);\n\t\t\t\t}\n\t\t\t}\n\t\t}, (int) min, // min\n\t\t\t\t(int) conf.getStaticOffset(), // max\n\t\t\t\t(int) conf.getLowerLimit(), // current\n\t\t\t\t150, \"device units\", true);\n\n\t\tdouble max = activLink.getDeviceMaximumValue();\n\t\tupperBound = new EngineeringUnitsSliderWidget(new IOnEngineeringUnitsChange() {\n\n\t\t\t@Override\n\t\t\tpublic void onSliderMoving(EngineeringUnitsSliderWidget source, double newAngleDegrees) {\n\t\t\t\tdouble eng = setUpperBound(newAngleDegrees);\n\t\t\t\tactivLink.setUseLimits(false);\n\t\t\t\tactivLink.setTargetEngineeringUnits(eng);\n\t\t\t\tactivLink.flush(0);\n\t\t\t\tactivLink.setUseLimits(true);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onSliderDoneMoving(EngineeringUnitsSliderWidget source, double newAngleDegrees) {\n\t\t\t\tactivLink.setUseLimits(false);\n\t\t\t\tactivLink.setTargetEngineeringUnits(setUpperBound(newAngleDegrees));\n\t\t\t\tactivLink.flush(0);\n\t\t\t\tactivLink.setUseLimits(true);\n\t\t\t\tzero.setUpperBound(newAngleDegrees);\n\t\t\t\tif (manager != null)\n\t\t\t\t\tmanager.generateCad(CSGDatabase.getInstance());\n\n\t\t\t}\n\t\t}, (int) conf.getStaticOffset(), (int) max, (int) conf.getUpperLimit(), 150, \"device units\", true);\n\n\t\tzeroValue = conf.getStaticOffset();\n\n\t\tzero = new EngineeringUnitsSliderWidget(new IOnEngineeringUnitsChange() {\n\n\t\t\t@Override\n\t\t\tpublic void onSliderMoving(EngineeringUnitsSliderWidget source, double newAngleDegrees) {\n\t\t\t\tif (newAngleDegrees < conf.getLowerLimit() || newAngleDegrees > conf.getUpperLimit())\n\t\t\t\t\treturn;\n\t\t\t\tconf.setStaticOffset(newAngleDegrees);\n\t\t\t\tupdateZeroValue(newAngleDegrees);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onSliderDoneMoving(EngineeringUnitsSliderWidget source, double newAngleDegrees) {\n\t\t\t\tif (newAngleDegrees < conf.getLowerLimit() || newAngleDegrees > conf.getUpperLimit())\n\t\t\t\t\treturn;\n\t\t\t\tupdateZeroValue(newAngleDegrees);\n\t\t\t\tif (manager != null)\n\t\t\t\t\tmanager.generateCad(CSGDatabase.getInstance());\n\n\t\t\t}\n\t\t}, (int) conf.getLowerLimit(), (int) conf.getUpperLimit(), (int) conf.getStaticOffset(), 150, \"device units\",\n\t\t\t\ttrue);\n\t\tzero.setAllowResize(false);\n\t\tzero.showSlider(false);\n\t\tupperBound.setAllowResize(false);\n\t\tlowerBound.setAllowResize(false);\n\t\tfinal ComboBox<String> channel = new ComboBox<>();\n\t\tfor (int i = 0; i < 24; i++) {\n\t\t\tchannel.getItems().add(Integer.toString(i));\n\t\t}\n\t\tchannel.setOnAction(new EventHandler<ActionEvent>() {\n\n\t\t\t@Override\n\t\t\tpublic void handle(ActionEvent event) {\n\t\t\t\tconf.setHardwareIndex(Integer.parseInt(channel.getSelectionModel().getSelectedItem()));\n\t\t\t\tfactory.refreshHardwareLayer(conf);\n\t\t\t\tactivLink = factory.getLink(conf);\n\t\t\t\tactivLink.flush(0);\n\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Link channel to \" + conf.getTypeString());\n\t\t\t\tif (manager != null)\n\t\t\t\t\tmanager.generateCad(CSGDatabase.getInstance());\n\n\t\t\t}\n\t\t});\n\t\tchannel.getSelectionModel().select(conf.getHardwareIndex());\n\n\t\tfinal ComboBox<String> comboBox = new ComboBox<>();\n\t\tfor (String type : LinkType.getUserDefined()) {\n\t\t\tcomboBox.getItems().add(type);\n\t\t}\n\t\tcomboBox.getItems().add(\"virtual\");\n\t\tcomboBox.getItems().add(\"pid-prismatic\");\n\t\tcomboBox.setOnAction(new EventHandler<ActionEvent>() {\n\n\t\t\t@Override\n\t\t\tpublic void handle(ActionEvent event) {\n\t\t\t\tconf.setTypeString(comboBox.getSelectionModel().getSelectedItem());\n\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Link type changed to \" + conf.getTypeString());\n\t\t\t\tif (manager != null)\n\t\t\t\t\tmanager.generateCad(CSGDatabase.getInstance());\n\n\t\t\t}\n\t\t});\n\t\tcomboBox.getSelectionModel().select(conf.getTypeString().toString());\n\n\t\tadd(new Text(\"Zero Degrees Value\"), 0, 1);\n\t\tadd(zero, 1, 1);\n\n\t\tadd(new Text(\"Upper bound\"), 0, 2);\n\t\tadd(upperBound, 1, 2);\n\n\t\tadd(new Text(\"Lower bound\"), 0, 3);\n\t\tadd(lowerBound, 1, 3);\n\n\t\tadd(new Text(\"Link Type\"), 0, 4);\n\t\tadd(comboBox, 1, 4);\n\t\tadd(new Text(\"Link Hardware Index\"), 0, 5);\n\t\tadd(channel, 1, 5);\n\n\t\tadd(new Text(\"Device Scripting Name\"), 0, 6);\n\t\tadd(deviceName, 1, 6);\n\n\t\tadd(new Text(\"Mass\"), 0, 7);\n\t\tadd(mass, 1, 7);\n\n\t\tadd(new Text(\"Mass Centroid x\"), 0, 8);\n\t\tadd(massx, 1, 8);\n\n\t\tadd(new Text(\"Mass Centroid y\"), 0, 9);\n\t\tadd(massy, 1, 9);\n\t\tadd(new Text(\"Mass Centroid z\"), 0, 10);\n\t\tadd(massz, 1, 10);\n\t\t// link hardware\n\t\tadd(new Text(\"Hardware Type\"), 0, 11);\n\t\tadd(emHardwareType, 1, 11);\n\t\tadd(new Text(\"Hardware Size\"), 0, 12);\n\t\tadd(emHardwareSize, 1, 12);\n\t\t// add(newHardware, 1, 13);\n\n\t\t// link shaft\n\t\tadd(new Text(\"Shaft Type\"), 0, 14);\n\t\tadd(shaftType, 1, 14);\n\t\tadd(new Text(\"Shaft Size\"), 0, 15);\n\t\tadd(shaftSize, 1, 15);\n\t\t// add(newShaft, 1, 16);\n\t\tCheckBox isPassive = new CheckBox();\n\t\tisPassive.setSelected(conf.isPassive());\n\t\tisPassive.setOnAction(action -> {\n\t\t\tconf.setPassive(isPassive.isSelected());\n\t\t});\n\t\tadd(new Text(\"Link Is Passive\"), 0, 16);\n\t\tadd(isPassive, 1, 16);\n\n\t}\n\n\tpublic void trimPlus() {\n\t\tif (conf.getScale() > 0)\n\t\t\tzero.jogPlusOne();\n\t\telse\n\t\t\tzero.jogMinusOne();\n\t}\n\n\tpublic void trimMinus() {\n\t\tif (conf.getScale() < 0)\n\t\t\tzero.jogPlusOne();\n\t\telse\n\t\t\tzero.jogMinusOne();\n\t}\n\n\tprivate void updateZeroValue(double newAngleDegrees) {\n\t\tdouble diff = zeroValue - newAngleDegrees;\n\t\tzeroValue = newAngleDegrees;\n\t\tsetLowerBound(conf.getLowerLimit() - diff);\n\t\tsetUpperBound(conf.getUpperLimit() - diff);\n\t\t// myLinkSliderWidget.getSetpoint().setValue(0);\n\t\tupperBound.setLowerBound((int) newAngleDegrees);\n\t\tlowerBound.setUpperBound((int) newAngleDegrees);\n\t\ttry {\n\t\t\tactivLink.setTargetEngineeringUnits(0);\n\t\t\tactivLink.flush(0);\n\t\t} catch (Exception ex) {\n\t\t}\n\t}\n\n\tpublic double setUpperBound(double newAngleDegrees) {\n\n\t\tdouble upperLimit = newAngleDegrees <= activLink.getDeviceMaximumValue()\n\t\t\t\t? newAngleDegrees\n\t\t\t\t: activLink.getDeviceMaximumValue();\n\t\tconf.setUpperLimit(upperLimit);\n\t\tupperBound.setValue(upperLimit);\n\t\tdouble eng = 0;\n\t\tif (conf.getScale() < 0) {\n\t\t\teng = (activLink.getMinEngineeringUnits());\n\t\t\t// myLinkSliderWidget.getSetpoint().setLowerBound(eng);\n\t\t} else {\n\t\t\teng = (activLink.getMaxEngineeringUnits());\n\t\t\t// myLinkSliderWidget.getSetpoint().setUpperBound(eng);\n\t\t}\n\t\tzero.setUpperBound((int) newAngleDegrees);\n\t\treturn eng;\n\t}\n\n\tpublic double setLowerBound(double newAngleDegrees) {\n\t\tdouble lowerLimit = newAngleDegrees >= activLink.getDeviceMinimumValue()\n\t\t\t\t? newAngleDegrees\n\t\t\t\t: activLink.getDeviceMinimumValue();\n\t\tconf.setLowerLimit(lowerLimit);\n\t\tlowerBound.setValue(lowerLimit);\n\n\t\tdouble eng = 0;\n\t\tif (conf.getScale() > 0) {\n\t\t\teng = (activLink.getMinEngineeringUnits());\n\t\t\t// myLinkSliderWidget.getSetpoint().setLowerBound(eng);\n\t\t} else {\n\t\t\teng = (activLink.getMaxEngineeringUnits());\n\t\t\t// myLinkSliderWidget.getSetpoint().setUpperBound(eng);\n\t\t}\n\t\tzero.setLowerBound((int) newAngleDegrees);\n\t\treturn eng;\n\n\t}\n\n\tprivate void setShaftSize(String selectedItem) {\n\t\tif (selectedItem == null) {\n\t\t\treturn;\n\t\t}\n\t}\n\n\tprivate void setShaftType(final ComboBox<String> shaftSize, String selectedItem) {\n\t\tshaftSize.getItems().clear();\n\t\tif (selectedItem == null)\n\t\t\treturn;\n\t\tfor (String s : Vitamins.listVitaminSizes(selectedItem)) {\n\t\t\tshaftSize.getItems().add(s);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/creature/LinkGaugeController.java",
    "content": "package com.neuronrobotics.bowlerstudio.creature;\n\nimport com.neuronrobotics.bowlerstudio.BowlerStudio;\nimport com.neuronrobotics.sdk.addons.kinematics.AbstractLink;\nimport com.neuronrobotics.sdk.addons.kinematics.ILinkConfigurationChangeListener;\nimport com.neuronrobotics.sdk.addons.kinematics.ILinkListener;\nimport com.neuronrobotics.sdk.addons.kinematics.LinkConfiguration;\nimport com.neuronrobotics.sdk.pid.PIDLimitEvent;\n\nimport eu.hansolo.medusa.Gauge;\nimport eu.hansolo.medusa.GaugeBuilder;\nimport eu.hansolo.medusa.Section;\nimport eu.hansolo.medusa.TickLabelLocation;\nimport eu.hansolo.medusa.TickLabelOrientation;\nimport eu.hansolo.medusa.TickMarkType;\nimport eu.hansolo.medusa.Gauge.KnobType;\nimport eu.hansolo.medusa.Gauge.NeedleShape;\nimport javafx.scene.Node;\nimport javafx.scene.Parent;\nimport javafx.scene.paint.Color;\n\npublic class LinkGaugeController implements ILinkListener, ILinkConfigurationChangeListener {\n\tprivate int SIZE_OF_GAUGE = 200;\n\tprivate Gauge gauge;\n\tprivate Section bounds;\n\tprivate Section boundsPossible;\n\tprivate LinkConfiguration conf;\n\tprivate AbstractLink link;\n\tprivate boolean isNowVis = false;\n\tpublic Gauge getGauge() {\n\t\tif (gauge == null) {\n\t\t\tdouble spread = 60;\n\t\t\tbounds = new Section(0, 0, Color.rgb(60, 130, 145, 0.7));\n\t\t\tboundsPossible = new Section(0, 0, Color.ORANGE);\n\n\t\t\tgauge = GaugeBuilder.create().decimals(2).foregroundBaseColor(Color.BLACK).prefSize(getSIZE(), getSIZE())\n\t\t\t\t\t.startAngle(360 - (spread / 2)).angleRange(360 - spread).minValue(-180 + (spread / 2))\n\t\t\t\t\t.maxValue(180 - (spread / 2)).tickLabelLocation(TickLabelLocation.OUTSIDE)\n\t\t\t\t\t.tickLabelOrientation(TickLabelOrientation.ORTHOGONAL).minorTickMarksVisible(false)\n\t\t\t\t\t// .tickLabelsVisible(false)\n\t\t\t\t\t.majorTickMarkType(TickMarkType.BOX).valueVisible(true).knobType(KnobType.FLAT)\n\t\t\t\t\t.needleShape(NeedleShape.FLAT).needleColor(Color.RED).tickLabelsVisible(true).sectionsVisible(true)\n\t\t\t\t\t.sections(boundsPossible, bounds).build();\n\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\tgauge.setInteractive(false);\n\t\t\t\tgauge.setTitle(\"\");\n\t\t\t\tturnOffPickOnBoundsFor(gauge);\n\t\t\t});\n\t\t\tgauge.parentProperty().addListener((observable, oldValue, newValue) -> {\n\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\tisNowVis = newValue != null;\n\t\t\t\t\tif (isNowVis) {\n\t\t\t\t\t\tevent(conf);\n\t\t\t\t\t\tif (gauge != null && link != null)\n\t\t\t\t\t\t\tgauge.setValue(link.getCurrentEngineeringUnits());\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t});\n\n\t\t}\n\t\treturn gauge;\n\t}\n\n\tprivate boolean turnOffPickOnBoundsFor(Node n) {\n\t\tboolean result = false;\n\t\tn.setPickOnBounds(false);\n\t\tif (n instanceof Parent) {\n\t\t\tfor (Node c : ((Parent) n).getChildrenUnmodifiable()) {\n\t\t\t\tif (turnOffPickOnBoundsFor(c)) {\n\t\t\t\t\tresult = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tn.setMouseTransparent(!result);\n\t\treturn result;\n\t}\n\n\tpublic void setLink(LinkConfiguration lf, AbstractLink l) {\n\t\tif (link != null)\n\t\t\tlink.removeLinkListener(this);\n\t\tthis.link = l;\n\t\tif (conf != null)\n\t\t\tconf.removeChangeListener(this);\n\t\tthis.conf = lf;\n\t\tconf.addChangeListener(this);\n\t\tlink.addLinkListener(this);\n\t\tevent(conf);\n\t}\n\n\tprivate AbstractLink getAbstractLink() {\n\t\treturn link;\n\t}\n\n\t@Override\n\tpublic void event(LinkConfiguration newConf) {\n\t\tif (!isNowVis || getAbstractLink() == null)\n\t\t\treturn;\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tbounds.setStart(getAbstractLink().getMinEngineeringUnits());\n\t\t\tbounds.setStop(getAbstractLink().getMaxEngineeringUnits());\n\t\t\tboundsPossible.setStart(getAbstractLink().getDeviceMinEngineeringUnits());\n\t\t\tboundsPossible.setStop(getAbstractLink().getDeviceMaxEngineeringUnits());\n\t\t\tgauge.setTitle(\"Link Bounds\");\n\t\t});\n\t}\n\n\t@Override\n\tpublic void onLinkPositionUpdate(AbstractLink source, double engineeringUnitsValue) {\n\t\tif (!isNowVis)\n\t\t\treturn;\n\t\tBowlerStudio.runLater(() -> gauge.setValue(engineeringUnitsValue));\n\t}\n\n\t@Override\n\tpublic void onLinkLimit(AbstractLink source, PIDLimitEvent event) {\n\t}\n\n\tpublic int getSIZE() {\n\t\treturn SIZE_OF_GAUGE;\n\t}\n\n\tpublic void setSIZE(int sIZE_OF_GAUGE) {\n\t\tSIZE_OF_GAUGE = sIZE_OF_GAUGE;\n\t\tgetGauge().setPrefSize(getSIZE(), getSIZE());\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/creature/LinkSliderWidget.java",
    "content": "package com.neuronrobotics.bowlerstudio.creature;\n\nimport java.time.Duration;\nimport java.util.Locale;\n\nimport com.neuronrobotics.bowlerstudio.BowlerStudio;\nimport com.neuronrobotics.bowlerstudio.BowlerStudioController;\nimport com.neuronrobotics.bowlerstudio.assets.ConfigurationDatabase;\nimport com.neuronrobotics.bowlerstudio.physics.TransformFactory;\nimport com.neuronrobotics.sdk.addons.gamepad.BowlerJInputDevice;\nimport com.neuronrobotics.sdk.addons.gamepad.IGameControlEvent;\nimport com.neuronrobotics.sdk.addons.kinematics.AbstractKinematicsNR;\nimport com.neuronrobotics.sdk.addons.kinematics.AbstractLink;\nimport com.neuronrobotics.sdk.addons.kinematics.DHParameterKinematics;\nimport com.neuronrobotics.sdk.addons.kinematics.DhLinkType;\nimport com.neuronrobotics.sdk.addons.kinematics.IJointSpaceUpdateListenerNR;\nimport com.neuronrobotics.sdk.addons.kinematics.ILinkConfigurationChangeListener;\nimport com.neuronrobotics.sdk.addons.kinematics.ILinkListener;\nimport com.neuronrobotics.sdk.addons.kinematics.JointLimit;\nimport com.neuronrobotics.sdk.addons.kinematics.LinkConfiguration;\nimport com.neuronrobotics.sdk.addons.kinematics.MobileBase;\nimport com.neuronrobotics.sdk.addons.kinematics.math.RotationNR;\nimport com.neuronrobotics.sdk.addons.kinematics.math.TransformNR;\nimport com.neuronrobotics.sdk.pid.PIDLimitEvent;\nimport com.neuronrobotics.sdk.util.ThreadUtil;\n\nimport eu.mihosoft.vrl.v3d.parametrics.CSGDatabase;\nimport javafx.scene.Group;\nimport javafx.scene.Node;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.TextField;\nimport javafx.scene.layout.ColumnConstraints;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.HBox;\nimport javafx.scene.layout.RowConstraints;\nimport javafx.scene.layout.VBox;\nimport javafx.scene.text.Text;\nimport javafx.scene.transform.Affine;\n\n@SuppressWarnings(\"restriction\")\npublic class LinkSliderWidget extends Group\n\t\timplements\n\t\t\tIGameControlEvent,\n\t\t\tIOnEngineeringUnitsChange,\n\t\t\tILinkListener,\n\t\t\tILinkConfigurationChangeListener,\n\t\t\tIJointSpaceUpdateListenerNR {\n\tprivate AbstractKinematicsNR device;\n\tprivate DHParameterKinematics dhdevice;\n\n\tprivate int linkIndex;\n\tprivate EngineeringUnitsSliderWidget setpoint;\n\tprivate BowlerJInputDevice controller;\n\tprivate jogThread jogTHreadHandle;\n\tprivate double slider;\n\tprivate boolean stop;\n\tprivate double seconds;\n\tprivate String paramsKey;\n\tprivate ITrimControl trimController = null;\n\t// private EngineeringUnitsSliderWidget slide;\n\tprivate Button jogplus = new Button(\"+\");\n\tprivate Button jogminus = new Button(\"-\");\n\tprivate LinkConfiguration conf;\n\n\tprivate LinkConfigurationWidget theWidget;\n\tprivate TextField engineeringUpper = new TextField(\"0\");\n\tprivate TextField engineeringVelUpper = new TextField(\"0\");\n\tprivate TextField engineeringLower = new TextField(\"0\");\n\tprivate Label engineeringTotalLimited = new Label(\"0\");\n\tprivate Label engineeringTotalPossible = new Label(\"0\");\n\tprivate Label engineeringUpperPossible = new Label(\"0\");\n\tprivate Label engineeringLowerPossible = new Label(\"0\");\n\tprivate Node gauge;\n\tprivate static LinkGaugeController linkGaugeController3d = null;// = new LinkGaugeController();\n\tprivate static Affine offsetGauge = null;\n\tprivate static Affine offsetGaugeTranslate = null;\n\tprivate boolean isNowVis = false;\n\tprivate TransformWidget poseOfLink;\n\tpublic LinkSliderWidget(int linkIndex, DHParameterKinematics d, MobileBase base, boolean addLimits,\n\t\t\tboolean displayLinkCOnfiguration) {\n\n\t\tthis.linkIndex = linkIndex;\n\t\tthis.device = d;\n\t\tthis.conf = d.getLinkConfiguration(linkIndex);\n\t\tthis.theWidget = new LinkConfigurationWidget(conf, d.getFactory(),\n\t\t\t\tMobileBaseCadManager.get(CSGDatabase.getInstance(), base));\n\t\tsetTrimController(this.theWidget);\n\t\tconf.addChangeListener(this);\n\t\tif (DHParameterKinematics.class.isInstance(device)) {\n\t\t\tdhdevice = (DHParameterKinematics) device;\n\t\t}\n\n\t\tTextField name = new TextField(getAbstractLink().getLinkConfiguration().getName());\n\t\tname.setMaxWidth(100.0);\n\t\tname.setOnAction(event -> {\n\t\t\tgetAbstractLink().getLinkConfiguration().setName(name.getText());\n\t\t});\n\n\t\tsetSetpoint(new EngineeringUnitsSliderWidget(this, getAbstractLink().getMinEngineeringUnits(),\n\t\t\t\tgetAbstractLink().getMaxEngineeringUnits(), device.getCurrentJointSpaceVector()[linkIndex], 180,\n\t\t\t\td.getDhChain().getLinks().get(linkIndex).getLinkType() == DhLinkType.ROTORY ? \"degrees\" : \"mm\"));\n\n\t\tGridPane panel = new GridPane();\n\n\t\tpanel.getColumnConstraints().add(new ColumnConstraints(30)); // column 1\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// is 75\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// wide\n\t\tpanel.getColumnConstraints().add(new ColumnConstraints(120)); // column\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// 1 is\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// 75\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// wide\n\t\tpanel.getColumnConstraints().add(new ColumnConstraints(120)); // column\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// 2 is\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// 300\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// wide\n\t\tjogminus.setOnAction(event -> {\n\t\t\tif (theWidget != null)\n\t\t\t\tgetTrimController().trimMinus();\n\t\t});\n\t\tjogplus.setOnAction(event -> {\n\t\t\tif (theWidget != null)\n\t\t\t\tgetTrimController().trimPlus();\n\t\t});\n\n\t\tLinkGaugeController linkGaugeController = new LinkGaugeController();\n\t\tgauge = linkGaugeController.getGauge();\n\t\tlinkGaugeController.setLink(conf, getAbstractLink());\n\n\t\tengineeringUpper.setOnAction(event -> {\n\t\t\ttry {\n\t\t\t\tdouble num = Double.parseDouble(engineeringUpper.getText());\n\t\t\t\tif (num > getAbstractLink().getDeviceMaxEngineeringUnits()) {\n\t\t\t\t\tthrow new RuntimeException();\n\t\t\t\t}\n\t\t\t\tdouble linkUnits = getAbstractLink().toLinkUnits(num);\n\t\t\t\tif (conf.getScale() > 0)\n\t\t\t\t\tif (theWidget != null)\n\t\t\t\t\t\ttheWidget.setUpperBound(linkUnits);\n\t\t\t\t\telse if (theWidget != null)\n\t\t\t\t\t\ttheWidget.setLowerBound(linkUnits);\n\t\t\t} catch (Exception e) {\n\t\t\t\tBowlerStudio.runLater(() -> engineeringUpper\n\t\t\t\t\t\t.setText(String.format(Locale.US, \"%.2f\", getAbstractLink().getMaxEngineeringUnits())));\n\t\t\t}\n\t\t});\n\t\tengineeringLower.setOnAction(event -> {\n\t\t\ttry {\n\t\t\t\tdouble num = Double.parseDouble(engineeringLower.getText());\n\t\t\t\tif (num < getAbstractLink().getDeviceMinEngineeringUnits()) {\n\t\t\t\t\tthrow new RuntimeException();\n\t\t\t\t}\n\t\t\t\tdouble linkUnits = getAbstractLink().toLinkUnits(num);\n\t\t\t\tif (conf.getScale() < 0)\n\t\t\t\t\tif (theWidget != null)\n\t\t\t\t\t\ttheWidget.setUpperBound(linkUnits);\n\t\t\t\t\telse if (theWidget != null)\n\t\t\t\t\t\ttheWidget.setLowerBound(linkUnits);\n\t\t\t} catch (Exception e) {\n\t\t\t\tBowlerStudio.runLater(() -> engineeringLower\n\t\t\t\t\t\t.setText(String.format(\"%.2f\", getAbstractLink().getMinEngineeringUnits())));\n\t\t\t}\n\t\t});\n\n\t\tengineeringVelUpper.setOnAction(event -> {\n\t\t\ttry {\n\t\t\t\tdouble num = Double.parseDouble(engineeringVelUpper.getText());\n\t\t\t\tgetAbstractLink().setMaxVelocityEngineeringUnits(num);\n\t\t\t} catch (Exception e) {\n\t\t\t\tBowlerStudio.runLater(() -> engineeringVelUpper\n\t\t\t\t\t\t.setText(String.format(\"%.2f\", getAbstractLink().getMaxVelocityEngineeringUnits())));\n\t\t\t}\n\t\t});\n\n\t\tHBox upperLimBox1 = new HBox();\n\t\tHBox lowerLimBox1 = new HBox();\n\t\tVBox limits1 = new VBox();\n\t\tengineeringUpper.setPrefWidth(80);\n\t\tengineeringLower.setPrefWidth(80);\n\t\tupperLimBox1.getChildren().addAll(new Label(\"Upper: \"), engineeringUpperPossible);\n\t\tlowerLimBox1.getChildren().addAll(new Label(\"Lower: \"), engineeringLowerPossible);\n\t\tlimits1.getChildren().addAll(new Label(\"Possible Range\"), upperLimBox1, lowerLimBox1, engineeringTotalPossible);\n\n\t\tHBox trimBox = new HBox();\n\t\tHBox upperLimBox = new HBox();\n\t\tHBox lowerLimBox = new HBox();\n\t\tVBox limits = new VBox();\n\t\tengineeringUpper.setPrefWidth(80);\n\t\tengineeringLower.setPrefWidth(80);\n\t\tupperLimBox.getChildren().addAll(new Label(\"Upper: \"), engineeringUpper);\n\t\tlowerLimBox.getChildren().addAll(new Label(\"Lower: \"), engineeringLower);\n\t\tlimits.getChildren().addAll(new Label(\"Desired Limits\"), upperLimBox, lowerLimBox, engineeringTotalLimited);\n\n\t\ttrimBox.getChildren().add(new Label(\"Trim\"));\n\t\ttrimBox.getChildren().add(jogminus);\n\t\ttrimBox.getChildren().add(jogplus);\n\t\tpanel.setHgap(5);\n\t\tpanel.setVgap(5);\n\t\tpanel.add(new Text(\"#\" + linkIndex), 0, 0);\n\t\tpanel.add(name, 1, 0);\n\t\tpanel.add(getSetpoint(), 2, 0);\n\n\t\tGridPane calibration = new GridPane();\n\t\tcalibration.setHgap(5);\n\t\tcalibration.setVgap(5);\n\t\tcalibration.getColumnConstraints().add(new ColumnConstraints(180));\n\t\tcalibration.getColumnConstraints().add(new ColumnConstraints(180));\n\t\tcalibration.getColumnConstraints().add(new ColumnConstraints(120));\n\t\tcalibration.getRowConstraints().add(new RowConstraints(120));\n\t\tcalibration.getRowConstraints().add(new RowConstraints(60));\n\t\tif (displayLinkCOnfiguration)\n\t\t\tcalibration.getRowConstraints().add(new RowConstraints(150));\n\n\t\tHBox velocityLim = new HBox();\n\t\tVBox velocitylimits = new VBox();\n\t\tvelocityLim.getChildren().add(new Label(\"Velocity Limit\"));\n\t\tvelocitylimits.getChildren().addAll(engineeringVelUpper, new Label(\"deg/sec\"));\n\t\tvelocityLim.getChildren().add(velocitylimits);\n\n\t\tcalibration.add(limits, 0, 0);\n\t\tcalibration.add(limits1, 1, 0);\n\t\tcalibration.add(velocityLim, 0, 1);\n\t\tif (displayLinkCOnfiguration)\n\t\t\tcalibration.add(trimBox, 1, 2);\n\t\tif (displayLinkCOnfiguration)\n\t\t\tcalibration.add(gauge, 0, 2);\n\n\t\tVBox allParts = new VBox();\n\t\tallParts.getChildren().addAll(panel);\n\t\tif (addLimits)\n\t\t\tallParts.getChildren().addAll(calibration);\n\t\tif (displayLinkCOnfiguration)\n\t\t\tallParts.getChildren().addAll(theWidget);\n\t\tposeOfLink = new TransformWidget(\"Link Tip Pose\", new TransformNR(), new IOnTransformChange() {\n\n\t\t\t@Override\n\t\t\tpublic void onTransformFinished(TransformNR newTrans) {\n\t\t\t\t// Auto-generated method stub\n\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onTransformChaging(TransformNR newTrans) {\n\t\t\t\t// Auto-generated method stub\n\n\t\t\t}\n\t\t});\n\t\tparentProperty().addListener((observable, oldValue, newValue) -> {\n\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Changed visibility of linkslider \" + newValue);\n\t\t\tisNowVis = newValue != null;\n\t\t\tif (isNowVis) {\n\t\t\t\tevent(conf);\n\t\t\t\tdouble[] currentJointSpaceVector = device.getCurrentJointSpaceVector();\n\n\t\t\t\tupdateLinkPose(linkIndex, dhdevice, poseOfLink, currentJointSpaceVector);\n\t\t\t\ttry {\n\t\t\t\t\tgetSetpoint().setValue(currentJointSpaceVector[linkIndex]);\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t}\n\t\t});\n\t\td.addJointSpaceListener(this);\n\t\tposeOfLink.setDisable(true);\n\t\tif (displayLinkCOnfiguration)\n\t\t\tallParts.getChildren().addAll(poseOfLink);\n\t\tgetChildren().add(allParts);\n\t\tgetAbstractLink().addLinkListener(this);\n\t\t// device.addJointSpaceListener(this);\n\t\tevent(conf);\n\t}\n\n\t@Override\n\tpublic void onJointSpaceUpdate(AbstractKinematicsNR source, double[] joints) {\n\t\tif (!isNowVis)\n\t\t\treturn;\n\t\tupdateLinkPose(linkIndex, dhdevice, poseOfLink, joints);\n\t}\n\n\t@Override\n\tpublic void onJointSpaceTargetUpdate(AbstractKinematicsNR source, double[] joints) {\n\n\t}\n\n\t@Override\n\tpublic void onJointSpaceLimit(AbstractKinematicsNR source, int axis, JointLimit event) {\n\t\t// Auto-generated method stub\n\n\t}\n\n\tprivate void updateLinkPose(int linkIndex, DHParameterKinematics d, TransformWidget poseOfLink, double[] joints) {\n\t\tif (linkIndex >= joints.length) {\n\t\t\td.removeJointSpaceUpdateListener(this);\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tTransformNR linkTip;\n\t\t\ttry {\n\t\t\t\tlinkTip = d.getLinkTip(linkIndex);\n\t\t\t\tif (linkTip == null)\n\t\t\t\t\tthrow new RuntimeException();\n\t\t\t} catch (Exception e) {\n\t\t\t\tlinkTip = d.getChain().getChain(d.getCurrentJointSpaceVector()).get(linkIndex);\n\t\t\t}\n\t\t\tif (poseOfLink != null && linkTip != null)\n\t\t\t\tposeOfLink.updatePose(linkTip);\n\t\t} catch (Throwable t) {\n\t\t\tt.printStackTrace();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void event(LinkConfiguration newConf) {\n\t\tconf = newConf;\n\t\tdouble rANGE = getAbstractLink().getMaxEngineeringUnits() - getAbstractLink().getMinEngineeringUnits();\n\t\tdouble theoreticalRange = getAbstractLink().getDeviceMaxEngineeringUnits()\n\t\t\t\t- getAbstractLink().getDeviceMinEngineeringUnits();\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tengineeringTotalPossible.setText(\"Possible Range \" + String.format(\"%.2f\", theoreticalRange));\n\t\t\tengineeringTotalLimited.setText(\"Link Range \" + String.format(\"%.2f\", rANGE));\n\t\t\tengineeringUpper.setText(String.format(\"%.2f\", getAbstractLink().getMaxEngineeringUnits()));\n\t\t\tengineeringLower.setText(String.format(\"%.2f\", getAbstractLink().getMinEngineeringUnits()));\n\t\t\tengineeringUpperPossible.setText(String.format(\"%.2f\", getAbstractLink().getDeviceMaxEngineeringUnits()));\n\t\t\tengineeringLowerPossible.setText(String.format(\"%.2f\", getAbstractLink().getDeviceMinEngineeringUnits()));\n\t\t\tengineeringVelUpper.setText(String.format(\"%.1f\", getAbstractLink().getMaxVelocityEngineeringUnits()));\n\n\t\t});\n\t\tgetSetpoint().setLowerBound(getAbstractLink().getMinEngineeringUnits());\n\t\tgetSetpoint().setUpperBound(getAbstractLink().getMaxEngineeringUnits());\n\n\t}\n\n\tpublic void setUpperBound(double newBound) {\n\t\tgetSetpoint().setUpperBound(newBound);\n\t}\n\n\tpublic void setLowerBound(double newBound) {\n\t\tgetSetpoint().setLowerBound(newBound);\n\t}\n\n\tprivate void controllerLoop() {\n\t\tseconds = .1;\n\t\tif (getGameController() != null || stop == false) {\n\n\t\t\tif (!stop) {\n\t\t\t\tjogTHreadHandle.setToSet(slider + getSetpoint().getValue(), seconds);\n\t\t\t}\n\n\t\t\tBowlerStudio.runLater(Duration.ofMillis((int) (seconds * 1000.0)), new Runnable() {\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\tcontrollerLoop();\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate class jogThread extends Thread {\n\t\tprivate boolean controlThreadRunning = false;\n\n\t\tprivate double toSeconds = seconds;\n\n\t\tprivate double newValue;\n\n\t\tpublic void run() {\n\t\t\tsetName(\"Jog Link Slider\");\n\t\t\twhile (device.isAvailable()) {\n\t\t\t\tif (controlThreadRunning) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tdevice.setDesiredJointAxisValue(linkIndex, newValue, toSeconds);\n\t\t\t\t\t\tgetSetpoint().setValue(newValue);\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\t\te.printStackTrace();\n\t\t\t\t\t}\n\t\t\t\t\tcontrolThreadRunning = false;\n\t\t\t\t}\n\t\t\t\tThreadUtil.wait((int) (toSeconds * 1000));\n\t\t\t}\n\t\t}\n\n\t\tpublic void setToSet(double newValue, double toSeconds) {\n\n\t\t\tthis.newValue = newValue;\n\t\t\tthis.toSeconds = toSeconds;\n\t\t\tcontrolThreadRunning = true;\n\t\t}\n\n\t}\n\n\tpublic void setGameController(BowlerJInputDevice controller) {\n\t\tthis.controller = controller;\n\t\tif (controller != null && jogTHreadHandle == null) {\n\t\t\tjogTHreadHandle = new jogThread();\n\t\t\tjogTHreadHandle.start();\n\t\t}\n\n\t\tif (controller != null) {\n\t\t\tparamsKey = controller.getControllerName();\n\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Controller key: \" + paramsKey);\n\t\t\tgetGameController().clearListeners();\n\t\t\tgetGameController().addListeners(this);\n\t\t\tcontrollerLoop();\n\t\t}\n\t}\n\n\tpublic BowlerJInputDevice getGameController() {\n\t\treturn controller;\n\t}\n\n\t@Override\n\tpublic void onEvent(String name, float value) {\n\n\t\tif (name.toLowerCase().contentEquals((String) ConfigurationDatabase.getObject(paramsKey, \"jogLink\", \"x\")))\n\t\t\tslider = -value;\n\n\t\tif (Math.abs(slider) < .01)\n\t\t\tslider = 0;\n\t\tif (slider == 0) {\n\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"Stoping on=\"+comp.getName());\n\t\t\tstop = true;\n\t\t} else\n\t\t\tstop = false;\n\t}\n\n\t@Override\n\tpublic void onSliderMoving(EngineeringUnitsSliderWidget source, double newAngleDegrees) {\n\t\t// Auto-generated method stub\n\t\ttry {\n\t\t\tif (newAngleDegrees > device.getAbstractLink(linkIndex).getMaxEngineeringUnits()) {\n\t\t\t\tnewAngleDegrees = device.getAbstractLink(linkIndex).getMaxEngineeringUnits();\n\t\t\t}\n\t\t\tif (newAngleDegrees < device.getAbstractLink(linkIndex).getMinEngineeringUnits()) {\n\t\t\t\tnewAngleDegrees = device.getAbstractLink(linkIndex).getMinEngineeringUnits();\n\t\t\t}\n\t\t\tdevice.setDesiredJointAxisValue(linkIndex, newAngleDegrees, 0);\n\n\t\t} catch (Exception e) {\n\t\t\t// Auto-generated catch block\n\t\t\t// e.printStackTrace();\n\t\t} ;\n\n\t}\n\n\t@Override\n\tpublic void onSliderDoneMoving(EngineeringUnitsSliderWidget source, double newAngleDegrees) {\n\n\t}\n\n\t@Override\n\tpublic void onLinkLimit(AbstractLink arg0, PIDLimitEvent arg1) {\n\t\t// Auto-generated method stub\n\n\t}\n\n\t@Override\n\tpublic void onLinkPositionUpdate(AbstractLink arg0, double arg1) {\n\t\tif (!isNowVis)\n\t\t\treturn;\n\t\tif (getSetpoint().isEditing())\n\t\t\treturn;\n\t\t// Auto-generated method stub\n\t\ttry {\n\t\t\tgetSetpoint().setValue(arg1);\n\t\t} catch (Exception ex) {\n\t\t\treturn;\n\t\t}\n\t}\n\n\tpublic EngineeringUnitsSliderWidget getSetpoint() {\n\t\treturn setpoint;\n\t}\n\n\tpublic void setSetpoint(EngineeringUnitsSliderWidget setpoint) {\n\t\tthis.setpoint = setpoint;\n\t}\n\n\tpublic ITrimControl getTrimController() {\n\t\treturn trimController;\n\t}\n\n\tpublic void setTrimController(ITrimControl trimController) {\n\t\tthis.trimController = trimController;\n\t}\n\n\tpublic AbstractLink getAbstractLink() {\n\t\treturn device.getAbstractLink(linkIndex);\n\t}\n\n\tpublic void enable() {\n\n\t\tif (linkGaugeController3d == null) {\n\t\t\tlinkGaugeController3d = new LinkGaugeController();\n\t\t\tBowlerStudioController.addUserNode(linkGaugeController3d.getGauge());\n\t\t\toffsetGauge = new Affine();\n\t\t\toffsetGaugeTranslate = new Affine();\n\t\t\tlinkGaugeController3d.setSIZE(60);\n\t\t}\n\n\t\tdouble d = (((double) linkGaugeController3d.getSIZE())) / 2.0;\n\t\tTransformNR offsetter2 = new TransformNR().translateX(-d).translateY(-d);\n\t\tBowlerStudio.runLater(() -> TransformFactory.nrToAffine(offsetter2, offsetGaugeTranslate));\n\n\t\tTransformNR offsetter = new TransformNR();\n\n\t\tdouble theta = Math.toDegrees(device.getDhParametersChain().getLinks().get(linkIndex).getTheta());\n\t\toffsetter.setRotation(new RotationNR(0, 90 + theta, 0));\n\n\t\tBowlerStudio.runLater(() -> TransformFactory.nrToAffine(offsetter, offsetGauge));\n\n\t\tlinkGaugeController3d.getGauge().getTransforms().clear();\n\t\tlinkGaugeController3d.setLink(conf, getAbstractLink());\n\t\tif (linkIndex == 0)\n\t\t\tlinkGaugeController3d.getGauge().getTransforms().add((Affine) device.getRootListener());\n\t\telse\n\t\t\tlinkGaugeController3d.getGauge().getTransforms()\n\t\t\t\t\t.add((Affine) device.getAbstractLink(linkIndex - 1).getGlobalPositionListener());\n\n\t\tlinkGaugeController3d.getGauge().getTransforms().add(offsetGauge);\n\t\tlinkGaugeController3d.getGauge().getTransforms().add(offsetGaugeTranslate);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/creature/MobleBaseMenueFactory.java",
    "content": "package com.neuronrobotics.bowlerstudio.creature;\n\nimport com.neuronrobotics.bowlerstudio.BowlerStudio;\nimport com.neuronrobotics.bowlerstudio.BowlerStudioController;\nimport com.neuronrobotics.bowlerstudio.BowlerStudioMenuWorkspace;\nimport com.neuronrobotics.bowlerstudio.BowlerStudioModularFrame;\nimport com.neuronrobotics.bowlerstudio.ConnectionManager;\nimport com.neuronrobotics.bowlerstudio.IssueReportingExceptionHandler;\nimport com.neuronrobotics.bowlerstudio.assets.AssetFactory;\nimport com.neuronrobotics.bowlerstudio.assets.FontSizeManager;\nimport com.neuronrobotics.bowlerstudio.physics.TransformFactory;\nimport com.neuronrobotics.bowlerstudio.printbed.PrintBedManager;\nimport com.neuronrobotics.bowlerstudio.scripting.PasswordManager;\nimport com.neuronrobotics.bowlerstudio.scripting.ScriptingEngine;\nimport com.neuronrobotics.bowlerstudio.scripting.ScriptingFileWidget;\nimport com.neuronrobotics.nrconsole.util.CommitWidget;\nimport com.neuronrobotics.nrconsole.util.PromptForGit;\nimport com.neuronrobotics.sdk.addons.gamepad.BowlerJInputDevice;\nimport com.neuronrobotics.sdk.addons.kinematics.*;\nimport com.neuronrobotics.sdk.addons.kinematics.math.TransformNR;\nimport com.neuronrobotics.sdk.addons.kinematics.parallel.ParallelGroup;\nimport com.neuronrobotics.sdk.common.Log;\nimport com.neuronrobotics.sdk.util.ThreadUtil;\n\nimport eu.mihosoft.vrl.v3d.parametrics.CSGDatabase;\nimport javafx.fxml.FXMLLoader;\nimport javafx.scene.Group;\nimport javafx.scene.Node;\nimport javafx.scene.Parent;\nimport javafx.scene.control.*;\nimport javafx.scene.control.Alert.AlertType;\nimport javafx.scene.layout.VBox;\nimport javafx.scene.transform.Affine;\nimport javafx.stage.DirectoryChooser;\nimport javafx.stage.Modality;\nimport javafx.stage.Stage;\n\nimport org.apache.commons.io.FileUtils;\nimport org.apache.commons.io.IOUtils;\nimport org.eclipse.jgit.api.errors.GitAPIException;\nimport org.eclipse.jgit.api.errors.InvalidRemoteException;\nimport org.eclipse.jgit.api.errors.TransportException;\n//import org.jfree.util.Log;\nimport org.kohsuke.github.GHCreateRepositoryBuilder;\nimport org.kohsuke.github.GHRepository;\nimport org.kohsuke.github.GitHub;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.nio.charset.Charset;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\n\npublic class MobleBaseMenueFactory {\n\n\tprivate static File baseDirForFiles = null;\n\n\tprivate MobleBaseMenueFactory() {\n\t}\n\n\tpublic static String[] copyGitFile(String sourceGit, String targetGit, String filename) {\n\t\treturn ScriptingEngine.copyGitFile(sourceGit, targetGit, filename);\n\t}\n\n\tpublic static void addVitamins(IVitaminHolder vitamins, TreeItem<String> rootItem,\n\t\t\tHashMap<TreeItem<String>, Runnable> callbackMapForTreeitems,\n\t\t\tHashMap<TreeItem<String>, Parent> widgetMapForTreeitems, ITransformProvider tfp, Affine manipulator,\n\t\t\tAffine lastLink, TransformNR offset) {\n\t\tTreeItem<String> vitaminsMenu = new TreeItem<String>(\"Vitamins Add/Remove\",\n\t\t\t\tAssetFactory.loadIcon(\"Vitamins.png\"));\n\t\tHashMap<Parent, VitatminWidget> widget = new HashMap<>();\n\t\tcallbackMapForTreeitems.put(vitaminsMenu, () -> {\n\t\t\tif (widgetMapForTreeitems.get(vitaminsMenu) == null) {\n\t\t\t\tFXMLLoader loader;\n\t\t\t\ttry {\n\t\t\t\t\tloader = AssetFactory.loadLayout(\"layout/AddRemoveVitamins.fxml\", true);\n\t\t\t\t\t// loader.setClassLoader(VitatminWidget.class.getClassLoader());\n\t\t\t\t\tParent w = loader.load();\n\t\t\t\t\tVitatminWidget tw = loader.getController();\n\t\t\t\t\ttw.setVitaminProvider(vitamins, tfp);\n\t\t\t\t\ttw.setManipulator(manipulator);\n\t\t\t\t\ttw.setLastLinkAffine(lastLink);\n\t\t\t\t\ttw.setOffset(offset);\n\t\t\t\t\t// Group value = new Group(w);\n\t\t\t\t\twidgetMapForTreeitems.put(vitaminsMenu, w);\n\t\t\t\t\twidget.put(w, tw);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(e);\n\t\t\t\t}\n\t\t\t}\n\t\t\tVitatminWidget tw = widget.get(widgetMapForTreeitems.get(vitaminsMenu));\n\t\t\tif (tw != null)\n\t\t\t\ttw.fireVitaminSelectedUpdate();\n\t\t});\n\t\trootItem.getChildren().add(vitaminsMenu);\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tpublic static void load(MobileBase device, TreeView<String> view, TreeItem<String> rootItem,\n\t\t\tHashMap<TreeItem<String>, Runnable> callbackMapForTreeitems,\n\t\t\tHashMap<TreeItem<String>, Parent> widgetMapForTreeitems, CreatureLab creatureLab, boolean root,\n\t\t\tboolean creatureIsOwnedByUser) {\n\n\t\t// boolean creatureIsOwnedByUser = false;\n\t\tcallbackMapForTreeitems.put(rootItem, () -> {\n\t\t\tBowlerStudio.select(device);\n\t\t});\n\t\tTreeItem<String> editXml = new TreeItem<String>(\"Edit Robot XML..\",\n\t\t\t\tAssetFactory.loadIcon(\"Script-Tab-RobotXML.png\"));\n\t\tcallbackMapForTreeitems.put(editXml, () -> {\n\t\t\ttry {\n\t\t\t\tFile code = ScriptingEngine.fileFromGit(device.getGitSelfSource()[0], device.getGitSelfSource()[1]);\n\t\t\t\tBowlerStudio.createFileTab(code);\n\t\t\t} catch (Exception e) {\n\t\t\t\t// Auto-generated catch block\n\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(e);\n\t\t\t}\n\t\t});\n\t\tTreeItem<String> physics = new TreeItem<String>(\"Physics Simulation\",\n\t\t\t\tAssetFactory.loadIcon(\"Physics-Creature-Simulation.png\"));\n\n\t\tcallbackMapForTreeitems.put(physics, () -> {\n\t\t\tif (widgetMapForTreeitems.get(physics) == null) {\n\t\t\t\twidgetMapForTreeitems.put(physics, new Group(new PhysicsWidget(device)));\n\n\t\t\t}\n\t\t});\n\t\tTreeItem<String> save;\n\t\tsave = new TreeItem<String>(\"Save to XML\", AssetFactory.loadIcon(\"Save.png\"));\n\n\t\tif (!(device.getGitSelfSource()[0] == null || device.getGitSelfSource()[1] == null)) {\n\t\t\ttry {\n\n\t\t\t\tcallbackMapForTreeitems.put(save, () -> {\n\t\t\t\t\tsaveToXML(device);\n\t\t\t\t});\n\t\t\t} catch (Exception e) {\n\t\t\t\tLog.error(device.getGitSelfSource()[0] + \" \" + device.getGitSelfSource()[1] + \" failed to load\");\n\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(e);\n\t\t\t}\n\t\t}\n\t\tTreeItem<String> publish;\n\t\tpublish = new TreeItem<String>(\"Publish\", AssetFactory.loadIcon(\"Publish.png\"));\n\n\t\tif (!(device.getGitSelfSource()[0] == null || device.getGitSelfSource()[1] == null)) {\n\t\t\ttry {\n\t\t\t\tFile source = ScriptingEngine.fileFromGit(device.getGitSelfSource()[0], device.getGitSelfSource()[1]);\n\n\t\t\t\tcallbackMapForTreeitems.put(publish, () -> {\n\n\t\t\t\t\tCommitWidget.commit(source, device.getXml());\n\n\t\t\t\t});\n\t\t\t} catch (Exception e) {\n\t\t\t\tLog.error(device.getGitSelfSource()[0] + \" \" + device.getGitSelfSource()[1] + \" failed to load\");\n\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(e);\n\t\t\t}\n\t\t}\n\t\trootItem.getChildren().addAll(save);\n\t\tif (creatureIsOwnedByUser) {\n\t\t\tif (root) {\n\t\t\t\trootItem.getChildren().addAll(publish);\n\t\t\t}\n\n\t\t}\n\n\t\tTreeItem<String> makeCopy = new TreeItem<>(\"Make Copy of Creature\",\n\t\t\t\tAssetFactory.loadIcon(\"Make-Copy-of-Creature.png\"));\n\t\tif (root)\n\t\t\trootItem.getChildren().addAll(makeCopy);\n\t\trootItem.getChildren().addAll(physics);\n\t\tcallbackMapForTreeitems.put(makeCopy, () -> {\n\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\tString oldname = device.getScriptingName();\n\t\t\t\tTextInputDialog alert = new TextInputDialog(oldname + \"_copy\");\n\t\t\t\talert.setTitle(\"Making a copy of \" + oldname);\n\t\t\t\talert.setHeaderText(\"Set the scripting name for this creature\");\n\t\t\t\talert.setContentText(\"Set the name of the new creature:\");\n\t\t\t\tNode r = alert.getDialogPane();\n\t\t\t\tStage stage = (Stage) alert.getDialogPane().getScene().getWindow();\n\t\t\t\tstage.setOnCloseRequest(ev -> alert.hide());\n\t\t\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\t\t\tint tmp = fontNum - 10;\n\t\t\t\t\tif (tmp < 12)\n\t\t\t\t\t\ttmp = 12;\n\t\t\t\t\tr.setStyle(\"-fx-font-size: \" + tmp + \"pt\");\n\t\t\t\t\talert.getDialogPane().applyCss();\n\t\t\t\t\talert.getDialogPane().layout();\n\t\t\t\t\tstage.sizeToScene();\n\t\t\t\t});\n\t\t\t\t// Traditional way to get the response value.\n\t\t\t\tOptional<String> result = alert.showAndWait();\n\t\t\t\tif (result.isPresent()) {\n\t\t\t\t\tview.getSelectionModel().select(rootItem);\n\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Your new creature: \" + result.get());\n\t\t\t\t\tString newName = result.get();\n\t\t\t\t\tmakeACopyOfACreature(device, oldname, newName).start();\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\t\ttry {\n\t\t\tTreeItem<String> legs = loadLimbs(device, view, device.getLegs(), \"Legs\", rootItem, callbackMapForTreeitems,\n\t\t\t\t\twidgetMapForTreeitems, creatureLab, creatureIsOwnedByUser);\n\t\t\tTreeItem<String> arms = loadLimbs(device, view, device.getAppendages(), \"Arms\", rootItem,\n\t\t\t\t\tcallbackMapForTreeitems, widgetMapForTreeitems, creatureLab, creatureIsOwnedByUser);\n\t\t\tTreeItem<String> steer = loadLimbs(device, view, device.getSteerable(), \"Steerable Wheels\", rootItem,\n\t\t\t\t\tcallbackMapForTreeitems, widgetMapForTreeitems, creatureLab, creatureIsOwnedByUser);\n\t\t\tTreeItem<String> drive = loadLimbs(device, view, device.getDrivable(), \"Fixed Wheels\", rootItem,\n\t\t\t\t\tcallbackMapForTreeitems, widgetMapForTreeitems, creatureLab, creatureIsOwnedByUser);\n\t\t\tArrayList<DHParameterKinematics> paraGroups = device.getAllParallelGroups();\n\t\t\tTreeItem<String> paralell = loadLimbs(device, view, paraGroups, \"Paralell\", rootItem,\n\t\t\t\t\tcallbackMapForTreeitems, widgetMapForTreeitems, creatureLab, creatureIsOwnedByUser);\n\t\t\tboolean creatureIsOwnedByUserTmp = creatureIsOwnedByUser;\n\n\t\t\tTreeItem<String> addleg;\n\t\t\ttry {\n\t\t\t\taddleg = new TreeItem<String>(\"Add Leg\", AssetFactory.loadIcon(\"Add-Leg.png\"));\n\t\t\t} catch (Exception e1) {\n\t\t\t\taddleg = new TreeItem<String>(\"Add Leg\");\n\t\t\t}\n\n\t\t\t// Replace the leg addition callback\n\t\t\tcallbackMapForTreeitems.put(addleg, () -> {\n\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Adding Leg\");\n\n\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\tTextInputDialog alert = new TextInputDialog(\"leg_\" + device.getLegs().size());\n\t\t\t\t\talert.setTitle(\"Add a new leg\");\n\t\t\t\t\talert.setHeaderText(\"Set the scripting name for this leg\");\n\t\t\t\t\talert.setContentText(\"Please enter the name of the new leg:\");\n\n\t\t\t\t\tOptional<String> result = alert.showAndWait();\n\t\t\t\t\tif (result.isPresent()) {\n\t\t\t\t\t\tnew Thread(() -> {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t// Use builder to add leg to existing device\n\t\t\t\t\t\t\t\tMobileBaseBuilder builder = new MobileBaseBuilder(CSGDatabase.getInstance(), device)\n\t\t\t\t\t\t\t\t\t\t.addDefaultLeg(result.get());\n\n\t\t\t\t\t\t\t\t// Rebuild and reload\n\t\t\t\t\t\t\t\tMobileBase updatedDevice = builder.build(CSGDatabase.getInstance());\n\t\t\t\t\t\t\t\treload(updatedDevice);\n\n\t\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(e);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}).start();\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t});\n\t\t\t// boolean creatureIsOwnedByUserTmp = creatureIsOwnedByUser;\n\t\t\t// callbackMapForTreeitems.put(addleg, () -> {\n\t\t\t// // Auto-generated method stub\n\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"Adding Leg\");\n\t\t\t// String xmlContent;\n\t\t\t// try {\n\t\t\t// xmlContent =\n\t\t\t// ScriptingEngine.codeFromGit(\"https://github.com/CommonWealthRobotics/BowlerStudioExampleRobots.git\",\n\t\t\t// \"defaultleg.xml\")[0];\n\t\t\t// DHParameterKinematics newLeg = new DHParameterKinematics(null,\n\t\t\t// IOUtils.toInputStream(xmlContent, \"UTF-8\"));\n\t\t\t// String[] gitCadEngine = device.getGitCadEngine();\n\t\t\t// newLeg.setGitCadEngine(gitCadEngine);\n\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"Leg has \" +\n\t\t\t// newLeg.getNumberOfLinks() + \" links\");\n\t\t\t// addAppendage(device, view, device.getLegs(), newLeg, legs, rootItem,\n\t\t\t// callbackMapForTreeitems,\n\t\t\t// widgetMapForTreeitems, creatureLab, creatureIsOwnedByUserTmp);\n\t\t\t//\n\t\t\t//\n\t\t\t// } catch (Exception e) {\n\t\t\t// // Auto-generated catch block\n\t\t\t// com.neuronrobotics.sdk.common.Log.error(e);\n\t\t\t// }\n\t\t\t//\n\t\t\t// });\n\t\t\t// TreeItem<String> regnerate = new TreeItem<String>(\"Generate Cad\",\n\t\t\t// AssetFactory.loadIcon(\"Generate-Cad.png\"));\n\t\t\t//\n\t\t\t// callbackMapForTreeitems.put(regnerate, () -> {\n\t\t\t// creatureLab.generateCad(CSGDatabase.getInstance());\n\t\t\t//\n\t\t\t// });\n\t\t\tTreeItem<String> kinematics = new TreeItem<String>(\"Kinematic STL\",\n\t\t\t\t\tAssetFactory.loadIcon(\"Printable-Cad.png\"));\n\n\t\t\tcallbackMapForTreeitems.put(kinematics, () -> {\n\t\t\t\tFile defaultStlDir = new File(System.getProperty(\"user.home\") + \"/bowler-workspace/STL/\");\n\t\t\t\tif (!defaultStlDir.exists()) {\n\t\t\t\t\tdefaultStlDir.mkdirs();\n\t\t\t\t}\n\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\tDirectoryChooser chooser = new DirectoryChooser();\n\t\t\t\t\tchooser.setTitle(\"Select Output Directory For .STL files\");\n\n\t\t\t\t\tchooser.setInitialDirectory(defaultStlDir);\n\t\t\t\t\tFile baseDirForFiles = chooser.showDialog(BowlerStudioModularFrame.getPrimaryStage());\n\t\t\t\t\tnew Thread() {\n\n\t\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\t\tMobileBaseCadManager baseManager = MobileBaseCadManager.searchForCadManager(device);\n\t\t\t\t\t\t\tif (null == baseManager) {\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tArrayList<File> files;\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tfiles = baseManager.generateStls((MobileBase) device, baseDirForFiles, true);\n\t\t\t\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\t\t\t\tAlert alert = new Alert(AlertType.INFORMATION);\n\t\t\t\t\t\t\t\t\talert.setTitle(\"Stl Export Success!\");\n\t\t\t\t\t\t\t\t\talert.setHeaderText(\"Stl Export Success\");\n\t\t\t\t\t\t\t\t\talert.setContentText(\"All SLT's for the Creature Generated at\\n\"\n\t\t\t\t\t\t\t\t\t\t\t+ files.get(0).getAbsolutePath());\n\t\t\t\t\t\t\t\t\talert.setWidth(500);\n\t\t\t\t\t\t\t\t\talert.initModality(Modality.APPLICATION_MODAL);\n\t\t\t\t\t\t\t\t\talert.show();\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\t\tBowlerStudioController.highlightException(\n\t\t\t\t\t\t\t\t\t\tbaseManager.getCadScriptFromMobileBase((MobileBase) device), e);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\t\t\t\t\t}.start();\n\t\t\t\t});\n\n\t\t\t});\n\t\t\tTreeItem<String> arrangeBed = new TreeItem<String>(\"Arrange Print Bed\",\n\t\t\t\t\tAssetFactory.loadIcon(\"Edit-CAD-Engine.png\"));\n\t\t\tcallbackMapForTreeitems.put(arrangeBed, () -> {\n\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\tnew Thread() {\n\t\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\t\tMobileBaseCadManager baseManager = MobileBaseCadManager.get(CSGDatabase.getInstance(),\n\t\t\t\t\t\t\t\t\tdevice);\n\t\t\t\t\t\t\ttry {\n\n\t\t\t\t\t\t\t\tPrintBedManager manager = new PrintBedManager(device.getGitSelfSource()[0],\n\t\t\t\t\t\t\t\t\t\tbaseManager.getAllCad());\n\t\t\t\t\t\t\t\tBowlerStudioController.setCsg(manager.get());\n\t\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\t\tBowlerStudioController.highlightException(\n\t\t\t\t\t\t\t\t\t\tbaseManager.getCadScriptFromMobileBase((MobileBase) device), e);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}.start();\n\t\t\t\t});\n\t\t\t});\n\t\t\tTreeItem<String> printable = new TreeItem<String>(\"Printable Cad\",\n\t\t\t\t\tAssetFactory.loadIcon(\"Printable-Cad.png\"));\n\n\t\t\tcallbackMapForTreeitems.put(printable, () -> {\n\t\t\t\tFile defaultStlDir = getBaseDirForFiles();\n\n\t\t\t\tFile dir = defaultStlDir;\n\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\tDirectoryChooser chooser = new DirectoryChooser();\n\t\t\t\t\tchooser.setTitle(\"Select Output Directory For .STL files\");\n\n\t\t\t\t\tchooser.setInitialDirectory(dir);\n\t\t\t\t\tsetBaseDirForFiles(chooser.showDialog(BowlerStudioModularFrame.getPrimaryStage()));\n\t\t\t\t\tnew Thread() {\n\n\t\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\t\tMobileBaseCadManager baseManager = MobileBaseCadManager.get(CSGDatabase.getInstance(),\n\t\t\t\t\t\t\t\t\tdevice);\n\t\t\t\t\t\t\tif (getBaseDirForFiles() == null) {\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tArrayList<File> files;\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tfiles = baseManager.generateStls((MobileBase) device, getBaseDirForFiles(), false);\n\t\t\t\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\t\t\t\tAlert alert = new Alert(AlertType.INFORMATION);\n\t\t\t\t\t\t\t\t\talert.setTitle(\"Stl Export Success!\");\n\t\t\t\t\t\t\t\t\talert.setHeaderText(\"Stl Export Success\");\n\t\t\t\t\t\t\t\t\talert.setContentText(\"All SLT's for the Creature Generated at\\n\"\n\t\t\t\t\t\t\t\t\t\t\t+ files.get(0).getAbsolutePath());\n\t\t\t\t\t\t\t\t\talert.setWidth(500);\n\t\t\t\t\t\t\t\t\talert.initModality(Modality.APPLICATION_MODAL);\n\t\t\t\t\t\t\t\t\talert.show();\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\t\tBowlerStudioController.highlightException(\n\t\t\t\t\t\t\t\t\t\tbaseManager.getCadScriptFromMobileBase((MobileBase) device), e);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\t\t\t\t\t}.start();\n\t\t\t\t});\n\n\t\t\t});\n\t\t\tTreeItem<String> setCAD = new TreeItem<>(\"Set CAD Engine...\", AssetFactory.loadIcon(\"Set-CAD-Engine.png\"));\n\t\t\tcallbackMapForTreeitems.put(setCAD, () -> {\n\t\t\t\tPromptForGit.prompt(\"Select a CAD Engine From a Gist\", device.getGitCadEngine()[0], (gitsId, file) -> {\n\t\t\t\t\tLog.warning(\"Loading cad engine\");\n\t\t\t\t\ttry {\n\t\t\t\t\t\tcreatureLab.setGitCadEngine(gitsId, file, device);\n\t\t\t\t\t\tFile code = ScriptingEngine.fileFromGit(gitsId, file);\n\t\t\t\t\t\tBowlerStudio.createFileTab(code);\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(e);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t});\n\t\t\tTreeItem<String> editCAD = new TreeItem<>(\"Edit CAD Engine...\",\n\t\t\t\t\tAssetFactory.loadIcon(\"Edit-CAD-Engine.png\"));\n\t\t\tcallbackMapForTreeitems.put(editCAD, () -> {\n\t\t\t\ttry {\n\t\t\t\t\tFile code = ScriptingEngine.fileFromGit(device.getGitCadEngine()[0], device.getGitCadEngine()[1]);\n\t\t\t\t\tBowlerStudio.createFileTab(code);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(e);\n\t\t\t\t}\n\t\t\t});\n\t\t\tTreeItem<String> resetWalking = new TreeItem<>(\"Set Walking Engine...\",\n\t\t\t\t\tAssetFactory.loadIcon(\"Set-Walking-Engine.png\"));\n\t\t\tcallbackMapForTreeitems.put(resetWalking, () -> {\n\t\t\t\tPromptForGit.prompt(\"Select a Walking Engine From a Gist\", device.getGitWalkingEngine()[0],\n\t\t\t\t\t\t(gitsId, file) -> {\n\t\t\t\t\t\t\tLog.warning(\"Loading walking engine\");\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tcreatureLab.setGitWalkingEngine(gitsId, file, device);\n\t\t\t\t\t\t\t\tFile code = ScriptingEngine.fileFromGit(gitsId, file);\n\t\t\t\t\t\t\t\tBowlerStudio.createFileTab(code);\n\t\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(e);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t});\n\t\t\tTreeItem<String> editWalking = new TreeItem<>(\"Edit Walking Engine...\",\n\t\t\t\t\tAssetFactory.loadIcon(\"Edit-Walking-Engine.png\"));\n\t\t\tcallbackMapForTreeitems.put(editWalking, () -> {\n\t\t\t\ttry {\n\t\t\t\t\tFile code = ScriptingEngine.fileFromGit(device.getGitWalkingEngine()[0],\n\t\t\t\t\t\t\tdevice.getGitWalkingEngine()[1]);\n\t\t\t\t\tBowlerStudio.createFileTab(code);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(e);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// Replace the fixed wheel addition callback with options\n\t\t\tTreeItem<String> addFixed = new TreeItem<>(\"Add Fixed Wheel\", AssetFactory.loadIcon(\"Add-Fixed-Wheel.png\"));\n\t\t\tcallbackMapForTreeitems.put(addFixed, () -> {\n\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Adding Wheel\");\n\n\t\t\t\ttry {\n\t\t\t\t\t@SuppressWarnings(\"unchecked\")\n\t\t\t\t\tHashMap<String, HashMap<String, Object>> options = (HashMap<String, HashMap<String, Object>>) ScriptingEngine\n\t\t\t\t\t\t\t.gitScriptRun(CSGDatabase.getInstance(),\n\t\t\t\t\t\t\t\t\t\"https://github.com/CommonWealthRobotics/BowlerStudioExampleRobots.git\",\n\t\t\t\t\t\t\t\t\t\"wheelOptions.json\");\n\n\t\t\t\t\tSet<String> optionsKeys = options.keySet();\n\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\tChoiceDialog<String> alert = new ChoiceDialog<String>(optionsKeys.toArray()[0].toString(),\n\t\t\t\t\t\t\t\toptionsKeys);\n\t\t\t\t\t\talert.setTitle(\"Select Wheel Type\");\n\t\t\t\t\t\talert.setHeaderText(\"Choose the type of wheel to add\");\n\n\t\t\t\t\t\tOptional<String> result = alert.showAndWait();\n\t\t\t\t\t\tif (result.isPresent()) {\n\t\t\t\t\t\t\tnew Thread(() -> {\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tMobileBaseBuilder builder = new MobileBaseBuilder(CSGDatabase.getInstance(), device)\n\t\t\t\t\t\t\t\t\t\t\t.addFixedWheelFromOptions(CSGDatabase.getInstance(), result.get());\n\n\t\t\t\t\t\t\t\t\tMobileBase updatedDevice = builder.build(CSGDatabase.getInstance());\n\t\t\t\t\t\t\t\t\treload(updatedDevice);\n\n\t\t\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(e);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}).start();\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(e);\n\t\t\t\t}\n\t\t\t});\n\t\t\t// TreeItem<String> addFixed = new TreeItem<>(\"Add Fixed Wheel\",\n\t\t\t// AssetFactory.loadIcon(\"Add-Fixed-Wheel.png\"));\n\t\t\t//\n\t\t\t// callbackMapForTreeitems.put(addFixed, () -> {\n\t\t\t// // Auto-generated method stub\n\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"Adding Wheel\");\n\t\t\t//\n\t\t\t//\n\t\t\t// HashMap<String, HashMap<String, Object>> options;\n\t\t\t// try {\n\t\t\t// options = (HashMap<String, HashMap<String, Object>>) ScriptingEngine\n\t\t\t// .gitScriptRun(\"https://github.com/CommonWealthRobotics/BowlerStudioExampleRobots.git\",\n\t\t\t// \"wheelOptions.json\");\n\t\t\t// } catch (Exception e) {\n\t\t\t// // Auto-generated catch block\n\t\t\t// com.neuronrobotics.sdk.common.Log.error(e);\n\t\t\t// return;\n\t\t\t// }\n\t\t\t// Set<String> optionsKeys = options.keySet();\n\t\t\t// BowlerStudio.runLater(() -> {\n\t\t\t// ChoiceDialog<String> alert = new\n\t\t\t// ChoiceDialog<String>(optionsKeys.toArray()[0].toString(), optionsKeys);\n\t\t\t// Node r = alert.getDialogPane();\n\t\t\t// Stage stage = (Stage) alert.getDialogPane().getScene().getWindow();\n\t\t\t// stage.setOnCloseRequest(ev -> alert.hide());\n\t\t\t// FontSizeManager.addListener(fontNum -> {\n\t\t\t// int tmp = fontNum - 10;\n\t\t\t// if (tmp < 12)\n\t\t\t// tmp = 12;\n\t\t\t// r.setStyle(\"-fx-font-size: \" + tmp + \"pt\");\n\t\t\t// alert.getDialogPane().applyCss();\n\t\t\t// alert.getDialogPane().layout();\n\t\t\t// stage.sizeToScene();\n\t\t\t// });\n\t\t\t// Optional<String> result = alert.showAndWait();\n\t\t\t// if (result.isPresent())\n\t\t\t// new Thread(() -> {\n\t\t\t// String back = result.get();\n\t\t\t// HashMap<String,Object> values = options.get(back);\n\t\t\t// if (back.toLowerCase().contains(\"fixed\")) {\n\t\t\t// try {\n\t\t\t//\n\t\t\t// String xmlContent = ScriptingEngine.codeFromGit(\n\t\t\t// values.get(\"scriptGit\").toString(),\n\t\t\t// values.get(\"scriptFile\").toString())[0];\n\t\t\t// DHParameterKinematics newArm = new DHParameterKinematics(null,\n\t\t\t// IOUtils.toInputStream(xmlContent, \"UTF-8\"));\n\t\t\t// newArm.setGitCadEngine(device.getGitCadEngine());\n\t\t\t//\n\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"Wheel has \" +\n\t\t\t// newArm.getNumberOfLinks() + \" links\");\n\t\t\t// addAppendage(device, view, device.getDrivable(), newArm, drive, rootItem,\n\t\t\t// callbackMapForTreeitems, widgetMapForTreeitems, creatureLab,\n\t\t\t// creatureIsOwnedByUserTmp);\n\t\t\t// } catch (Exception e) {\n\t\t\t// // Auto-generated catch block\n\t\t\t// com.neuronrobotics.sdk.common.Log.error(e);\n\t\t\t// }\n\t\t\t//\n\t\t\t// }else {\n\t\t\t// try {\n\t\t\t// MobileBase base =\n\t\t\t// (MobileBase)ScriptingEngine.gitScriptRun(values.get(\"scriptGit\").toString(),\n\t\t\t// values.get(\"scriptFile\").toString());\n\t\t\t// DHParameterKinematics newArm = base.getDrivable().get(0);\n\t\t\t// newArm.setGitCadEngine(device.getGitCadEngine());\n\t\t\t// addAppendage(device, view, device.getDrivable(), newArm, drive, rootItem,\n\t\t\t// callbackMapForTreeitems, widgetMapForTreeitems, creatureLab,\n\t\t\t// creatureIsOwnedByUserTmp);\n\t\t\t// } catch (Exception e) {\n\t\t\t// // Auto-generated catch block\n\t\t\t// com.neuronrobotics.sdk.common.Log.error(e);\n\t\t\t// return;\n\t\t\t// }\n\t\t\t// }\n\t\t\t// }).start();\n\t\t\t// });\n\t\t\t//\n\t\t\t//\n\t\t\t// });\n\n\t\t\t// Replace the steerable wheel addition callback\n\t\t\tTreeItem<String> addsteerable = new TreeItem<>(\"Add Steerable Wheel\",\n\t\t\t\t\tAssetFactory.loadIcon(\"Add-Steerable-Wheel.png\"));\n\t\t\tcallbackMapForTreeitems.put(addsteerable, () -> {\n\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Adding Steerable Wheel\");\n\n\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\tTextInputDialog alert = new TextInputDialog(\"steerable_\" + device.getSteerable().size());\n\t\t\t\t\talert.setTitle(\"Add a new steerable wheel\");\n\t\t\t\t\talert.setHeaderText(\"Set the scripting name for this wheel\");\n\t\t\t\t\talert.setContentText(\"Please enter the name of the new steerable wheel:\");\n\n\t\t\t\t\tOptional<String> result = alert.showAndWait();\n\t\t\t\t\tif (result.isPresent()) {\n\t\t\t\t\t\tnew Thread(() -> {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tMobileBaseBuilder builder = new MobileBaseBuilder(CSGDatabase.getInstance(), device)\n\t\t\t\t\t\t\t\t\t\t.addDefaultSteerableWheel(result.get());\n\n\t\t\t\t\t\t\t\tMobileBase updatedDevice = builder.build(CSGDatabase.getInstance());\n\t\t\t\t\t\t\t\treload(updatedDevice);\n\n\t\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(e);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}).start();\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t});\n\n\t\t\t// TreeItem<String> addsteerable = new TreeItem<>(\"Add Steerable Wheel\",\n\t\t\t// AssetFactory.loadIcon(\"Add-Steerable-Wheel.png\"));\n\t\t\t//\n\t\t\t// callbackMapForTreeitems.put(addsteerable, () -> {\n\t\t\t// // Auto-generated method stub\n\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"Adding Steerable Wheel\");\n\t\t\t// try {\n\t\t\t// String xmlContent =\n\t\t\t// ScriptingEngine.codeFromGit(\"https://github.com/CommonWealthRobotics/BowlerStudioExampleRobots.git\",\n\t\t\t// \"defaultSteerable.xml\")[0];\n\t\t\t// DHParameterKinematics newArm = new DHParameterKinematics(null,\n\t\t\t// IOUtils.toInputStream(xmlContent, \"UTF-8\"));\n\t\t\t// newArm.setGitCadEngine(device.getGitCadEngine());\n\t\t\t//\n\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"Steerable has \" +\n\t\t\t// newArm.getNumberOfLinks() + \" links\");\n\t\t\t// addAppendage(device, view, device.getSteerable(), newArm, steer, rootItem,\n\t\t\t// callbackMapForTreeitems,\n\t\t\t// widgetMapForTreeitems, creatureLab, creatureIsOwnedByUserTmp);\n\t\t\t//\n\t\t\t// } catch (Exception e) {\n\t\t\t// // Auto-generated catch block\n\t\t\t// com.neuronrobotics.sdk.common.Log.error(e);\n\t\t\t// }\n\t\t\t//\n\t\t\t// });\n\t\t\tTreeItem<String> imuCenter = new TreeItem<>(\"Imu center\",\n\t\t\t\t\tAssetFactory.loadIcon(\"Advanced-Configuration.png\"));\n\n\t\t\tcallbackMapForTreeitems.put(imuCenter, () -> {\n\t\t\t\tTransformWidget imu = new TransformWidget(\"IMU center\", device.getIMUFromCentroid(),\n\t\t\t\t\t\tnew IOnTransformChange() {\n\n\t\t\t\t\t\t\t@Override\n\t\t\t\t\t\t\tpublic void onTransformChaging(TransformNR newTrans) {\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t@Override\n\t\t\t\t\t\t\tpublic void onTransformFinished(TransformNR newTrans) {\n\t\t\t\t\t\t\t\tMobileBaseCadManager manager = MobileBaseCadManager.get(CSGDatabase.getInstance(),\n\t\t\t\t\t\t\t\t\t\tdevice);\n\t\t\t\t\t\t\t\tif (manager != null)\n\t\t\t\t\t\t\t\t\tmanager.generateCad(CSGDatabase.getInstance());\n\t\t\t\t\t\t\t\tdevice.setIMUFromCentroid(newTrans);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t});\n\t\t\t\tif (widgetMapForTreeitems.get(imuCenter) == null) {\n\t\t\t\t\twidgetMapForTreeitems.put(imuCenter, new Group(imu));\n\n\t\t\t\t}\n\n\t\t\t});\n\t\t\tTreeItem<String> bodymass = new TreeItem<>(\"Adjust Body Mass\",\n\t\t\t\t\tAssetFactory.loadIcon(\"Advanced-Configuration.png\"));\n\n\t\t\tcallbackMapForTreeitems.put(bodymass, () -> {\n\t\t\t\tif (widgetMapForTreeitems.get(bodymass) == null) {\n\t\t\t\t\twidgetMapForTreeitems.put(bodymass, new AdjustbodyMassWidget(device));\n\n\t\t\t\t}\n\n\t\t\t});\n\n\t\t\t// Replace the arm addition callback\n\t\t\tTreeItem<String> addArm = new TreeItem<>(\"Add Arm\", AssetFactory.loadIcon(\"Add-Arm.png\"));\n\t\t\tcallbackMapForTreeitems.put(addArm, () -> {\n\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Adding Arm\");\n\n\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\tTextInputDialog alert = new TextInputDialog(\"arm_\" + device.getAppendages().size());\n\t\t\t\t\talert.setTitle(\"Add a new arm\");\n\t\t\t\t\talert.setHeaderText(\"Set the scripting name for this arm\");\n\t\t\t\t\talert.setContentText(\"Please enter the name of the new arm:\");\n\n\t\t\t\t\tOptional<String> result = alert.showAndWait();\n\t\t\t\t\tif (result.isPresent()) {\n\t\t\t\t\t\tnew Thread(() -> {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tMobileBaseBuilder builder = new MobileBaseBuilder(CSGDatabase.getInstance(), device)\n\t\t\t\t\t\t\t\t\t\t.addDefaultArm(result.get());\n\n\t\t\t\t\t\t\t\tMobileBase updatedDevice = builder.build(CSGDatabase.getInstance());\n\t\t\t\t\t\t\t\treload(updatedDevice);\n\n\t\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(e);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}).start();\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t});\n\t\t\tTreeItem<String> PlaceLimb = new TreeItem<>(\"Move MobileBase \",\n\t\t\t\t\tAssetFactory.loadIcon(\"Design-Parameter-Adjustment.png\"));\n\n\t\t\tcallbackMapForTreeitems.put(PlaceLimb, () -> {\n\t\t\t\tif (widgetMapForTreeitems.get(PlaceLimb) == null) {\n\t\t\t\t\t// create the widget for the leg when looking at it for the\n\t\t\t\t\t// first time\n\t\t\t\t\ttry {\n\t\t\t\t\t\twidgetMapForTreeitems.put(PlaceLimb,\n\t\t\t\t\t\t\t\tnew Group(new TransformWidget(\"Move Relative MobileBase Location\",\n\t\t\t\t\t\t\t\t\t\tdevice.getRobotToFiducialTransform(), new IOnTransformChange() {\n\n\t\t\t\t\t\t\t\t\t\t\t@Override\n\t\t\t\t\t\t\t\t\t\t\tpublic void onTransformFinished(TransformNR newTrans) {\n\t\t\t\t\t\t\t\t\t\t\t\tcom.neuronrobotics.sdk.common.Log\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t.error(\"Limb to base\" + newTrans.toString());\n\t\t\t\t\t\t\t\t\t\t\t\tdevice.setRobotToFiducialTransform(newTrans);\n\t\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t\t@Override\n\t\t\t\t\t\t\t\t\t\t\tpublic void onTransformChaging(TransformNR newTrans) {\n\t\t\t\t\t\t\t\t\t\t\t\tdevice.setRobotToFiducialTransform(newTrans);\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t})));\n\t\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\t\tBowlerStudio.printStackTrace(ex);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t});\n\n\t\t\trootItem.getChildren().addAll(bodymass, imuCenter, PlaceLimb);\n\t\t\taddVitamins(device, rootItem, callbackMapForTreeitems, widgetMapForTreeitems, selected -> {\n\t\t\t\treturn device.forwardOffset(new TransformNR());\n\t\t\t}, (Affine) device.getRootListener(), (Affine) device.getRootListener(), new TransformNR());\n\t\t\tif (root)\n\t\t\t\trootItem.getChildren().addAll(printable, arrangeBed, kinematics);\n\t\t\trootItem.getChildren().addAll(addArm, addleg, addFixed, addsteerable);\n\t\t\tif (creatureIsOwnedByUser) {\n\t\t\t\tif (root)\n\t\t\t\t\trootItem.getChildren().addAll(editXml);\n\t\t\t\trootItem.getChildren().addAll(editWalking, editCAD, resetWalking, setCAD);\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tnew IssueReportingExceptionHandler().except(e);\n\t\t}\n\t}\n\n\tprivate static void reload(MobileBase device) {\n\t\tsaveToXML(device);\n\t\tString[] source = device.getGitSelfSource();\n\t\tdevice.disconnect();\n\t\ttry {\n\t\t\tMobileBase reloaded = MobileBaseLoader.fromGit(CSGDatabase.getInstance(), source[0], source[1]);\n\t\t\tBowlerStudio.loadMobilBaseIntoUI(reloaded);\n\t\t} catch (Exception e) {\n\t\t\t// Auto-generated catch block\n\t\t\tcom.neuronrobotics.sdk.common.Log.error(e);\n\t\t}\n\n\t}\n\n\tpublic static void saveToXML(MobileBase device) {\n\t\tOutputStream out = null;\n\t\ttry {\n\t\t\tFile source = ScriptingEngine.fileFromGit(device.getGitSelfSource()[0], device.getGitSelfSource()[1]);\n\n\t\t\tout = FileUtils.openOutputStream(source, false);\n\t\t\tIOUtils.write(device.getXml(), out, Charset.defaultCharset());\n\t\t\tout.close(); // don't swallow close Exception if copy completes\n\t\t\t// normally\n\t\t} catch (Throwable t) {\n\t\t\tcom.neuronrobotics.sdk.common.Log.error(t);\n\t\t} finally {\n\t\t\ttry {\n\t\t\t\tout.close();\n\t\t\t} catch (Exception e) {\n\n\t\t\t}\n\t\t}\n\t}\n\n\t// Replace the makeACopyOfACreature method\n\tprivate static Thread makeACopyOfACreature(MobileBase device, String oldname, String newName) {\n\t\treturn new Thread() {\n\t\t\tpublic void run() {\n\t\t\t\ttry {\n\t\t\t\t\t// Create GitHub repository\n\t\t\t\t\tGitHub github = PasswordManager.getGithub();\n\t\t\t\t\tGHCreateRepositoryBuilder builder = github.createRepository(newName);\n\t\t\t\t\tbuilder.description(newName + \" copy of \" + oldname);\n\n\t\t\t\t\tGHRepository gist = null;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tgist = builder.create();\n\t\t\t\t\t} catch (org.kohsuke.github.HttpException ex) {\n\t\t\t\t\t\tif (ex.getMessage().contains(\"name already exists on this account\")) {\n\t\t\t\t\t\t\tgist = github.getRepository(PasswordManager.getLoginID() + \"/\" + newName);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tString gitURL = gist.getHtmlUrl().toExternalForm() + \".git\";\n\n\t\t\t\t\t// Wait for repo to be ready\n\t\t\t\t\tString filename = newName + \".xml\";\n\t\t\t\t\twhile (true) {\n\t\t\t\t\t\tThreadUtil.wait(500);\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tScriptingEngine.fileFromGit(gitURL, filename);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.debug(\"Waiting for repo \" + e.getMessage());\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Use builder to create copy\n\t\t\t\t\tMobileBase newDevice = new MobileBaseBuilder(CSGDatabase.getInstance(), gitURL, newName)\n\t\t\t\t\t\t\t.copyFrom(device, newName).build(CSGDatabase.getInstance());\n\n\t\t\t\t\t// Shut down old robot and add new one\n\t\t\t\t\tConnectionManager.disconnectAll();\n\t\t\t\t\tThreadUtil.wait(3000);\n\t\t\t\t\tBowlerStudioMenuWorkspace.add(gitURL);\n\t\t\t\t\tThreadUtil.wait(1000);\n\n\t\t\t\t\tMobileBase mb = MobileBaseLoader.fromGit(CSGDatabase.getInstance(), gitURL, newName + \".xml\");\n\t\t\t\t\tThreadUtil.wait(1000);\n\t\t\t\t\tBowlerStudio.createFileTab(ScriptingEngine.fileFromGit(gitURL, newName + \".xml\"));\n\t\t\t\t\tThreadUtil.wait(1000);\n\t\t\t\t\tConnectionManager.addConnection(mb, mb.getScriptingName());\n\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(e);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t}\n\t// private static Thread makeACopyOfACreature(MobileBase device, String oldname,\n\t// String newName) {\n\t// return new Thread() {\n\t// public void run() {\n\t//\n\t// device.setScriptingName(newName);\n\t// String filename = newName + \".xml\";\n\t// GitHub github = PasswordManager.getGithub();\n\t//\n\t// GHCreateRepositoryBuilder builder = github.createRepository(newName);\n\t// try {\n\t// builder.description(newName + \" copy of \" + oldname);\n\t// } catch (Exception e1) {\n\t// // Auto-generated catch block\n\t// e1.printStackTrace();\n\t// }\n\t// GHRepository gist = null;\n\t// try {\n\t// try {\n\t// gist = builder.create();\n\t// } catch (org.kohsuke.github.HttpException ex) {\n\t// if (ex.getMessage().contains(\"name already exists on this account\")) {\n\t// gist = github.getRepository(PasswordManager.getLoginID() + \"/\" + newName);\n\t// }\n\t// }\n\t// String gitURL = gist.getHtmlUrl().toExternalForm() + \".git\";\n\t//\n\t// com.neuronrobotics.sdk.common.Log.error(\"Creating new Robot repo\");\n\t// while (true) {\n\t// ThreadUtil.wait(500);\n\t// Log.warning(gist + \" not built yet\");\n\t// try {\n\t// ScriptingEngine.fileFromGit(gitURL, filename);\n\t// break;\n\t// } catch (Exception e) {\n\t//\n\t// com.neuronrobotics.sdk.common.Log.debug(\"Waiting for repo \"+e.getMessage());\n\t// com.neuronrobotics.sdk.common.Log.error(e);\n\t// }\n\t//\n\t// }\n\t// // BowlerStudio.openUrlInNewTab(gist.getHtmlUrl());\n\t// com.neuronrobotics.sdk.common.Log.error(\"Creating gist at: \" + gitURL);\n\t//\n\t// com.neuronrobotics.sdk.common.Log.error(\"copy Cad engine \");\n\t// device.setGitCadEngine(\n\t// copyGitFile(device.getGitCadEngine()[0], gitURL,\n\t// device.getGitCadEngine()[1]));\n\t// com.neuronrobotics.sdk.common.Log.error(\"copy walking engine Was: \" +\n\t// device.getGitWalkingEngine()[0] + \" \"\n\t// + device.getGitWalkingEngine()[1]);\n\t// device.setGitWalkingEngine(\n\t// copyGitFile(device.getGitWalkingEngine()[0], gitURL,\n\t// device.getGitWalkingEngine()[1]));\n\t// // com.neuronrobotics.sdk.common.Log.error(\"is now\n\t// \"+device.getGitWalkingEngine());\n\t// for (DHParameterKinematics dh : device.getAllDHChains()) {\n\t// // com.neuronrobotics.sdk.common.Log.error(\"copy Leg Cad engine\n\t// \"+dh.getGitCadEngine());\n\t// dh.setGitCadEngine(copyGitFile(dh.getGitCadEngine()[0], gitURL,\n\t// dh.getGitCadEngine()[1]));\n\t//\n\t// // com.neuronrobotics.sdk.common.Log.error(\"copy Leg Dh engine \");\n\t// dh.setGitDhEngine(copyGitFile(dh.getGitDhEngine()[0], gitURL,\n\t// dh.getGitDhEngine()[1]));\n\t// }\n\t// device.setScriptingName(newName);\n\t// String xml = device.getXml();\n\t//\n\t// ScriptingEngine.pushCodeToGit(gitURL, ScriptingEngine.getFullBranch(gitURL),\n\t// filename, xml,\n\t// \"new Robot content\");\n\t// // Shut down the old robot\n\t// ConnectionManager.disconnectAll();\n\t//\n\t// ThreadUtil.wait(3000);\n\t// // add new robot to the workspace\n\t// BowlerStudioMenuWorkspace.add(gitURL);\n\t// ThreadUtil.wait(1000);\n\t// MobileBase mb = MobileBaseLoader.fromGit(gitURL, newName + \".xml\");\n\t// ThreadUtil.wait(1000);\n\t// BowlerStudio.createFileTab(ScriptingEngine.fileFromGit(gitURL, newName +\n\t// \".xml\"));\n\t// ThreadUtil.wait(1000);\n\t// ConnectionManager.addConnection(mb, mb.getScriptingName());\n\t//\n\t// } catch (Exception e) {\n\t// // Auto-generated catch block\n\t// com.neuronrobotics.sdk.common.Log.error(e);\n\t// }\n\t//\n\t// // DeviceManager.addConnection(newDevice,\n\t// // newDevice.getScriptingName());\n\t// }\n\t// };\n\t// }\n\n\tprivate static void getNextChannel(MobileBase base, LinkConfiguration confOfChannel) {\n\t\tHashMap<String, HashMap<Integer, Boolean>> deviceMap = new HashMap<>();\n\n\t\tsearchForAllLinks(base, deviceMap);\n\t\tfor (Map.Entry<String, HashMap<Integer, Boolean>> entry : deviceMap.entrySet()) {\n\t\t\tHashMap<Integer, Boolean> chans = entry.getValue();\n\t\t\tfor (int i = 0; i < 48; i++) {\n\n\t\t\t\tif (chans.get(i) == null) {\n\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Channel free: \" + i + \" on device \" + entry.getKey());\n\t\t\t\t\tconfOfChannel.setDeviceScriptingName(entry.getKey());\n\t\t\t\t\tconfOfChannel.setHardwareIndex(i);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthrow new RuntimeException(\"No channels are available on given devices\");\n\t}\n\n\tprivate static void searchForAllLinks(MobileBase base, HashMap<String, HashMap<Integer, Boolean>> deviceMap) {\n\t\tfor (DHParameterKinematics dh : base.getAllDHChains()) {\n\t\t\tfor (LinkConfiguration conf : dh.getLinkConfigurations()) {\n\t\t\t\tHashMap<Integer, Boolean> channelMap;\n\t\t\t\tif (deviceMap.get(conf.getDeviceScriptingName()) == null) {\n\t\t\t\t\tdeviceMap.put(conf.getDeviceScriptingName(), new HashMap<Integer, Boolean>());\n\t\t\t\t}\n\t\t\t\tchannelMap = deviceMap.get(conf.getDeviceScriptingName());\n\t\t\t\tchannelMap.put(conf.getHardwareIndex(), true);\n\t\t\t\tfor (LinkConfiguration sl : conf.getSlaveLinks()) {\n\t\t\t\t\tHashMap<Integer, Boolean> slavechannelMap;\n\t\t\t\t\tif (deviceMap.get(sl.getDeviceScriptingName()) == null) {\n\t\t\t\t\t\tdeviceMap.put(sl.getDeviceScriptingName(), new HashMap<Integer, Boolean>());\n\t\t\t\t\t}\n\t\t\t\t\tslavechannelMap = deviceMap.get(sl.getDeviceScriptingName());\n\t\t\t\t\tslavechannelMap.put(sl.getHardwareIndex(), true);\n\t\t\t\t}\n\t\t\t\tif (dh.getFollowerMobileBase(conf) != null) {\n\t\t\t\t\tsearchForAllLinks(dh.getFollowerMobileBase(conf), deviceMap);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void addAppendage(MobileBase base, TreeView<String> view,\n\t\t\tArrayList<DHParameterKinematics> deviceList, DHParameterKinematics newDevice, TreeItem<String> rootItem,\n\t\t\tTreeItem<String> topLevel, HashMap<TreeItem<String>, Runnable> callbackMapForTreeitems,\n\t\t\tHashMap<TreeItem<String>, Parent> widgetMapForTreeitems, CreatureLab creatureLab,\n\t\t\tboolean creatureIsOwnedByUser) {\n\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tTextInputDialog alert = new TextInputDialog(newDevice.getScriptingName());\n\t\t\talert.setTitle(\"Add a new limb of\");\n\t\t\talert.setHeaderText(\"Set the scripting name for this limb\");\n\t\t\talert.setContentText(\"Please the name of the new limb:\");\n\t\t\tNode root = alert.getDialogPane();\n\t\t\tStage stage = (Stage) alert.getDialogPane().getScene().getWindow();\n\t\t\tstage.setOnCloseRequest(ev -> alert.hide());\n\t\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\t\tint tmp = fontNum - 10;\n\t\t\t\tif (tmp < 12)\n\t\t\t\t\ttmp = 12;\n\t\t\t\troot.setStyle(\"-fx-font-size: \" + tmp + \"pt\");\n\t\t\t\talert.getDialogPane().applyCss();\n\t\t\t\talert.getDialogPane().layout();\n\t\t\t\tstage.sizeToScene();\n\t\t\t});\n\t\t\t// Traditional way to get the response value.\n\t\t\tOptional<String> result = alert.showAndWait();\n\t\t\tif (result.isPresent()) {\n\t\t\t\tview.getSelectionModel().select(rootItem);\n\t\t\t\tnew Thread() {\n\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Your new limb: \" + result.get());\n\t\t\t\t\t\tsetDeviceName(newDevice, result.get());\n\t\t\t\t\t\tConnectionManager.addConnection(newDevice, newDevice.getScriptingName());\n\t\t\t\t\t\tdeviceList.add(newDevice);\n\t\t\t\t\t\tfor (LinkConfiguration conf : newDevice.getLinkConfigurations()) {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tgetNextChannel(base, conf);\n\t\t\t\t\t\t\t} catch (RuntimeException exc) {\n\t\t\t\t\t\t\t\tString newname = conf.getDeviceScriptingName() + \"_new\";\n\t\t\t\t\t\t\t\tcom.neuronrobotics.sdk.common.Log\n\t\t\t\t\t\t\t\t\t\t.error(\"Adding new device to provide new channels: \" + newname);\n\t\t\t\t\t\t\t\tconf.setDeviceScriptingName(newname);\n\t\t\t\t\t\t\t\tgetNextChannel(base, conf);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tnewDevice.getFactory().refreshHardwareLayer(conf);\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treload(base);\n\t\t\t\t\t}\n\n\t\t\t\t\tprivate void setDeviceName(DHParameterKinematics newDevice, String name) {\n\t\t\t\t\t\tnewDevice.setScriptingName(name);\n\t\t\t\t\t\tfor (int i = 0; i < newDevice.getNumberOfLinks(); i++) {\n\t\t\t\t\t\t\tLinkConfiguration conf = newDevice.getLinkConfiguration(i);\n\t\t\t\t\t\t\tconf.setName(result.get() + \"_\" + conf.getName());\n\t\t\t\t\t\t\tMobileBase base = newDevice.getFollowerMobileBase(i);\n\t\t\t\t\t\t\tif (base != null) {\n\t\t\t\t\t\t\t\tbase.setScriptingName(name + \"_\" + base.getScriptingName());\n\t\t\t\t\t\t\t\tfor (DHParameterKinematics kin : base.getAllDHChains()) {\n\t\t\t\t\t\t\t\t\tsetDeviceName(kin, name + \"_\" + kin.getScriptingName());\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}.start();\n\t\t\t}\n\t\t});\n\n\t}\n\n\tprivate static TreeItem<String> loadLimbs(MobileBase base, TreeView<String> view,\n\t\t\tArrayList<DHParameterKinematics> drivable, String label, TreeItem<String> rootItem,\n\t\t\tHashMap<TreeItem<String>, Runnable> callbackMapForTreeitems,\n\t\t\tHashMap<TreeItem<String>, Parent> widgetMapForTreeitems, CreatureLab creatureLab,\n\t\t\tboolean creatureIsOwnedByUser) throws Exception {\n\n\t\tTreeItem<String> apps = new TreeItem<>(label,\n\t\t\t\tAssetFactory.loadIcon(\"Load-Limb-\" + label.replace(' ', '-') + \".png\"));\n\t\trootItem.getChildren().add(apps);\n\t\tif (drivable.isEmpty())\n\t\t\treturn apps;\n\t\tfor (DHParameterKinematics dh : drivable) {\n\t\t\tloadSingleLimb(base, view, dh, apps, callbackMapForTreeitems, widgetMapForTreeitems, creatureLab,\n\t\t\t\t\tcreatureIsOwnedByUser);\n\t\t}\n\n\t\treturn apps;\n\t}\n\n\t@SuppressWarnings(\"restriction\")\n\tprivate static LinkConfigurationWidget setHardwareConfig(MobileBase myBase, LinkConfiguration MyConf,\n\t\t\tLinkFactory myLinkFactory, TreeItem<String> rootItem1,\n\t\t\tHashMap<TreeItem<String>, Runnable> callbackMapForTreeitems1,\n\t\t\tHashMap<TreeItem<String>, Parent> widgetMapForTreeitems1) throws Exception {\n\n\t\tTreeItem<String> hwConf = new TreeItem<>(\"Hardware Config \" + MyConf.getName(),\n\t\t\t\tAssetFactory.loadIcon(\"Hardware-Config.png\"));\n\t\tLinkConfigurationWidget theWidget = new LinkConfigurationWidget(MyConf, myLinkFactory,\n\t\t\t\tMobileBaseCadManager.get(CSGDatabase.getInstance(), myBase));\n\t\tcallbackMapForTreeitems1.put(hwConf, () -> {\n\t\t\tif (widgetMapForTreeitems1.get(hwConf) == null) {\n\t\t\t\t// create the widget for the leg when looking at it for the\n\t\t\t\t// first time\n\t\t\t\twidgetMapForTreeitems1.put(hwConf, new Group(theWidget));\n\t\t\t}\n\t\t\t// BowlerStudio.select(myBase, MyConf);\n\t\t});\n\t\trootItem1.getChildren().add(hwConf);\n\t\treturn theWidget;\n\t}\n\n\t@SuppressWarnings(\"restriction\")\n\tprivate static void loadSingleLink(int linkIndex, MobileBase base, TreeView<String> view, LinkConfiguration conf,\n\t\t\tDHParameterKinematics dh, TreeItem<String> rootItem,\n\t\t\tHashMap<TreeItem<String>, Runnable> callbackMapForTreeitems,\n\t\t\tHashMap<TreeItem<String>, Parent> widgetMapForTreeitems, CreatureLab creatureLab, boolean isOwner)\n\t\t\tthrows Exception {\n\t\tTreeItem<String> link = new TreeItem<>(conf.getName(), AssetFactory.loadIcon(\"Move-Single-Motor.png\"));\n\t\tDHLink dhLink;\n\t\ttry {\n\t\t\tdhLink = dh.getChain().getLinks().get(linkIndex);\n\t\t} catch (java.lang.IndexOutOfBoundsException ex) {\n\t\t\treturn;\n\t\t}\n\n\t\tString linkName = dh.getLinkConfiguration(linkIndex).getName();\n\n\t\t// LinkConfigurationWidget confWidget =setHardwareConfig(base, conf,\n\t\t// dh.getFactory(), link, callbackMapForTreeitems, widgetMapForTreeitems);\n\t\t// lsw.setTrimController(confWidget);\n\n\t\tTreeItem<String> hwConf = new TreeItem<>(\"Hardware Config \" + conf.getName(),\n\t\t\t\tAssetFactory.loadIcon(\"Hardware-Config.png\"));\n\n\t\tlink.getChildren().add(hwConf);\n\n\t\tcallbackMapForTreeitems.put(hwConf, () -> {\n\t\t\tif (widgetMapForTreeitems.get(hwConf) == null) {\n\t\t\t\t// create the widget for the leg when looking at it for the\n\t\t\t\t// first time\n\t\t\t\tLinkSliderWidget lsw = new LinkSliderWidget(linkIndex, dh, base, true, true);\n\t\t\t\twidgetMapForTreeitems.put(hwConf, lsw);\n\t\t\t\tlsw.enable();\n\t\t\t}\n\t\t\tBowlerJInputDevice controller = creatureLab.getController();\n\t\t\tif (controller != null) {\n\t\t\t\t((LinkSliderWidget) widgetMapForTreeitems.get(hwConf)).setGameController(controller);\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tif (linkIndex == 0)\n\t\t\t\t\tBowlerStudio.select(base, dh);\n\t\t\t\telse\n\t\t\t\t\tBowlerStudio.select((javafx.scene.transform.Affine) dh.getAbstractLink(linkIndex - 1)\n\t\t\t\t\t\t\t.getGlobalPositionListener());\n\t\t\t} catch (Exception ex) {\n\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Limb not loaded yet\");\n\t\t\t}\n\t\t\t((LinkSliderWidget) widgetMapForTreeitems.get(hwConf)).enable();\n\t\t\t// select( base, dh);\n\t\t\t// activate controller\n\t\t});\n\n\t\tTreeItem<String> slaves = new TreeItem<>(\"Followers of \" + conf.getName(),\n\t\t\t\tAssetFactory.loadIcon(\"Slave-Links.png\"));\n\t\tLinkFactory slaveFactory = dh.getFactory().getLink(conf).getSlaveFactory();\n\t\tfor (LinkConfiguration co : conf.getSlaveLinks()) {\n\n\t\t\tsetHardwareConfig(base, co, slaveFactory, slaves, callbackMapForTreeitems, widgetMapForTreeitems);\n\t\t}\n\n\t\tTreeItem<String> removeMobileBase = new TreeItem<>(\"Remove \" + conf.getName(),\n\t\t\t\tAssetFactory.loadIcon(\"creature.png\"));\n\t\tTreeItem<String> addMobileBase = new TreeItem<>(\"Add MobileBase to \" + conf.getName(),\n\t\t\t\tAssetFactory.loadIcon(\"creature.png\"));\n\t\tTreeItem<String> addSlaves = new TreeItem<>(\"Add following Link to \" + conf.getName(),\n\t\t\t\tAssetFactory.loadIcon(\"Add-Slave-Links.png\"));\n\n\t\tcallbackMapForTreeitems.put(removeMobileBase, () -> {\n\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\tAlert alert = new Alert(AlertType.CONFIRMATION);\n\t\t\t\talert.setTitle(\"Confirm removing MobileBase\");\n\t\t\t\talert.setHeaderText(\"This will remove \" + dhLink.getSlaveMobileBase().getScriptingName());\n\t\t\t\talert.setContentText(\"Are sure you wish to remove this MobileBase?\");\n\t\t\t\tNode root = alert.getDialogPane();\n\t\t\t\tStage stage = (Stage) alert.getDialogPane().getScene().getWindow();\n\t\t\t\tstage.setOnCloseRequest(ev -> alert.hide());\n\t\t\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\t\t\tint tmp = fontNum - 10;\n\t\t\t\t\tif (tmp < 12)\n\t\t\t\t\t\ttmp = 12;\n\t\t\t\t\troot.setStyle(\"-fx-font-size: \" + tmp + \"pt\");\n\t\t\t\t\talert.getDialogPane().applyCss();\n\t\t\t\t\talert.getDialogPane().layout();\n\t\t\t\t\tstage.sizeToScene();\n\t\t\t\t});\n\t\t\t\tOptional<ButtonType> result = alert.showAndWait();\n\t\t\t\tview.getSelectionModel().select(rootItem);\n\t\t\t\tif (result.get() == ButtonType.OK) {\n\t\t\t\t\tview.getSelectionModel().select(rootItem);\n\t\t\t\t\tnew Thread() {\n\t\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\t\tdhLink.setMobileBaseXml(null);\n\t\t\t\t\t\t}\n\t\t\t\t\t}.start();\n\t\t\t\t\tslaves.getChildren().clear();\n\t\t\t\t\tslaves.getChildren().add(addSlaves);\n\t\t\t\t\tslaves.getChildren().add(addMobileBase);\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\n\t\tcallbackMapForTreeitems.put(addMobileBase, () -> {\n\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\tTextInputDialog alert = new TextInputDialog(\n\t\t\t\t\t\tconf.getName() + \"_MobileBase_\" + conf.getSlaveLinks().size());\n\t\t\t\talert.setTitle(\"Add a new Follower mobilebase of\");\n\t\t\t\talert.setHeaderText(\"Set the scripting name for this Follower link\");\n\t\t\t\talert.setContentText(\"Please the name of the new Follower link:\");\n\t\t\t\tNode root = alert.getDialogPane();\n\t\t\t\tStage stage = (Stage) alert.getDialogPane().getScene().getWindow();\n\t\t\t\tstage.setOnCloseRequest(ev -> alert.hide());\n\t\t\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\t\t\tint tmp = fontNum - 10;\n\t\t\t\t\tif (tmp < 12)\n\t\t\t\t\t\ttmp = 12;\n\t\t\t\t\troot.setStyle(\"-fx-font-size: \" + tmp + \"pt\");\n\t\t\t\t\talert.getDialogPane().applyCss();\n\t\t\t\t\talert.getDialogPane().layout();\n\t\t\t\t\tstage.sizeToScene();\n\t\t\t\t});\n\t\t\t\t// Traditional way to get the response value.\n\t\t\t\tOptional<String> result = alert.showAndWait();\n\t\t\t\tif (result.isPresent()) {\n\t\t\t\t\tview.getSelectionModel().select(rootItem);\n\t\t\t\t\tnew Thread() {\n\t\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\t\tMobileBase embedableXml = new MobileBase();\n\t\t\t\t\t\t\tString scriptingName = result.get();\n\t\t\t\t\t\t\tembedableXml.setScriptingName(scriptingName);\n\t\t\t\t\t\t\tdhLink.setMobileBaseXml(embedableXml);\n\t\t\t\t\t\t\tremoveMobileBase.setValue(\"Remove \" + scriptingName);\n\t\t\t\t\t\t\tslaves.getChildren().add(0, setUpNewMobileBaseEditor(view, callbackMapForTreeitems,\n\t\t\t\t\t\t\t\t\twidgetMapForTreeitems, creatureLab, isOwner, dhLink));\n\t\t\t\t\t\t}\n\t\t\t\t\t}.start();\n\t\t\t\t\tslaves.getChildren().remove(addMobileBase);\n\t\t\t\t\tslaves.getChildren().add(removeMobileBase);\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\n\t\tcallbackMapForTreeitems.put(addSlaves, () -> {\n\t\t\t// if(widgetMapForTreeitems.get(advanced)==null){\n\t\t\t// //create the widget for the leg when looking at it for the first\n\t\t\t// time\n\t\t\t// widgetMapForTreeitems.put(advanced, new DhChainWidget(dh,\n\t\t\t// creatureLab));\n\t\t\t// }\n\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\tTextInputDialog alert = new TextInputDialog(\n\t\t\t\t\t\tconf.getName() + \"_Follower_\" + conf.getSlaveLinks().size());\n\t\t\t\talert.setTitle(\"Add a new Follower link of\");\n\t\t\t\talert.setHeaderText(\"Set the scripting name for this Follower link\");\n\t\t\t\talert.setContentText(\"Please the name of the new Follower link:\");\n\t\t\t\tNode root = alert.getDialogPane();\n\t\t\t\tStage stage = (Stage) alert.getDialogPane().getScene().getWindow();\n\t\t\t\tstage.setOnCloseRequest(ev -> alert.hide());\n\t\t\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\t\t\tint tmp = fontNum - 10;\n\t\t\t\t\tif (tmp < 12)\n\t\t\t\t\t\ttmp = 12;\n\t\t\t\t\troot.setStyle(\"-fx-font-size: \" + tmp + \"pt\");\n\t\t\t\t\talert.getDialogPane().applyCss();\n\t\t\t\t\talert.getDialogPane().layout();\n\t\t\t\t\tstage.sizeToScene();\n\t\t\t\t});\n\t\t\t\t// Traditional way to get the response value.\n\t\t\t\tOptional<String> result = alert.showAndWait();\n\t\t\t\tif (result.isPresent()) {\n\t\t\t\t\tview.getSelectionModel().select(rootItem);\n\t\t\t\t\tnew Thread() {\n\t\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Your new link: \" + result.get());\n\t\t\t\t\t\t\tLinkConfiguration newLink = new LinkConfiguration();\n\t\t\t\t\t\t\t// newLink.setType(conf.getTypeEnum());\n\t\t\t\t\t\t\tnewLink.setTypeString(conf.getTypeString());\n\t\t\t\t\t\t\tgetNextChannel(base, newLink);\n\t\t\t\t\t\t\tnewLink.setName(result.get());\n\t\t\t\t\t\t\tconf.getSlaveLinks().add(newLink);\n\t\t\t\t\t\t\tslaveFactory.getLink(newLink);\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tsetHardwareConfig(base, newLink, slaveFactory, slaves, callbackMapForTreeitems,\n\t\t\t\t\t\t\t\t\t\twidgetMapForTreeitems);\n\t\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(e);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}.start();\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\n\t\tif (dhLink.getSlaveMobileBase() != null) {\n\t\t\tremoveMobileBase.setValue(\"Remove \" + dhLink.getSlaveMobileBase().getScriptingName());\n\t\t\tslaves.getChildren().add(0, setUpNewMobileBaseEditor(view, callbackMapForTreeitems, widgetMapForTreeitems,\n\t\t\t\t\tcreatureLab, isOwner, dhLink));\n\t\t\tslaves.getChildren().add(removeMobileBase);\n\t\t} else {\n\t\t\tslaves.getChildren().add(addMobileBase);\n\t\t}\n\t\tslaves.getChildren().add(addSlaves);\n\t\tTreeItem<String> remove = new TreeItem<>(\"Remove \" + conf.getName(), AssetFactory.loadIcon(\"Remove-Link.png\"));\n\t\tcallbackMapForTreeitems.put(remove, () -> {\n\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\tAlert alert = new Alert(AlertType.CONFIRMATION);\n\t\t\t\talert.setTitle(\"Confirm removing link\");\n\t\t\t\talert.setHeaderText(\"This will remove \" + conf.getName());\n\t\t\t\talert.setContentText(\"Are sure you wish to remove this link?\");\n\t\t\t\tNode root = alert.getDialogPane();\n\t\t\t\tStage stage = (Stage) alert.getDialogPane().getScene().getWindow();\n\t\t\t\tstage.setOnCloseRequest(ev -> alert.hide());\n\t\t\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\t\t\tint tmp = fontNum - 10;\n\t\t\t\t\tif (tmp < 12)\n\t\t\t\t\t\ttmp = 12;\n\t\t\t\t\troot.setStyle(\"-fx-font-size: \" + tmp + \"pt\");\n\t\t\t\t\talert.getDialogPane().applyCss();\n\t\t\t\t\talert.getDialogPane().layout();\n\t\t\t\t\tstage.sizeToScene();\n\t\t\t\t});\n\t\t\t\tOptional<ButtonType> result = alert.showAndWait();\n\t\t\t\tview.getSelectionModel().select(rootItem);\n\t\t\t\tif (result.get() == ButtonType.OK) {\n\n\t\t\t\t\trootItem.getChildren().remove(link);\n\t\t\t\t\tLinkFactory factory = dh.getFactory();\n\t\t\t\t\t// remove the link listener while the number of links could\n\t\t\t\t\t// chnage\n\t\t\t\t\tfactory.removeLinkListener(dh);\n\t\t\t\t\tDHChain chain = dh.getDhChain();\n\t\t\t\t\tchain.getLinks().remove(linkIndex);\n\t\t\t\t\tfactory.deleteLink(linkIndex);\n\t\t\t\t\t// set the modified kinematics chain\n\t\t\t\t\tdh.setChain(chain);\n\t\t\t\t\t// once the new link configuration is set up, re add the\n\t\t\t\t\t// listener\n\t\t\t\t\tfactory.addLinkListener(dh);\n\t\t\t\t\tcreatureLab.generateCad(CSGDatabase.getInstance());\n\t\t\t\t}\n\t\t\t});\n\n\t\t});\n\n\t\tTreeItem<String> design = new TreeItem<>(\"Design Parameters \" + conf.getName(),\n\t\t\t\tAssetFactory.loadIcon(\"Design-Parameter-Adjustment.png\"));\n\t\tconf.addChangeListener(new ILinkConfigurationChangeListener() {\n\n\t\t\t@Override\n\t\t\tpublic void event(LinkConfiguration newConf) {\n\t\t\t\tBowlerStudio.runLater(() -> {\n\n\t\t\t\t\thwConf.setValue(\"Hardware Config \" + newConf.getName());\n\t\t\t\t\tlink.setValue(newConf.getName());\n\t\t\t\t\tslaves.setValue(\"Followers of \" + newConf.getName());\n\t\t\t\t\tremoveMobileBase.setValue(\"Remove \" + newConf.getName());\n\t\t\t\t\taddMobileBase.setValue(\"Add MobileBase to \" + newConf.getName());\n\t\t\t\t\taddSlaves.setValue(\"Add following Link to \" + newConf.getName());\n\t\t\t\t\tdesign.setValue(\"Design Parameters \" + newConf.getName());\n\t\t\t\t\tremove.setValue(\"Remove \" + newConf.getName());\n\t\t\t\t});\n\n\t\t\t}\n\t\t});\n\t\tcallbackMapForTreeitems.put(design, () -> {\n\t\t\tif (widgetMapForTreeitems.get(design) == null) {\n\t\t\t\t// create the widget for the leg when looking at it for the\n\t\t\t\t// first time\n\t\t\t\twidgetMapForTreeitems.put(design, new DhSettingsWidget(dhLink, dh, new IOnEngineeringUnitsChange() {\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void onSliderMoving(EngineeringUnitsSliderWidget source, double newAngleDegrees) {\n\t\t\t\t\t\t// Auto-generated method stub\n\n\t\t\t\t\t}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void onSliderDoneMoving(EngineeringUnitsSliderWidget source, double newAngleDegrees) {\n\t\t\t\t\t\tcreatureLab.onSliderDoneMoving(source, newAngleDegrees);\n\t\t\t\t\t}\n\t\t\t\t}));\n\t\t\t}\n\t\t\tif (linkIndex == 0)\n\t\t\t\tBowlerStudio.select(base, dh);\n\t\t\telse\n\t\t\t\tBowlerStudio.select(\n\t\t\t\t\t\t(javafx.scene.transform.Affine) dh.getAbstractLink(linkIndex - 1).getGlobalPositionListener());\n\t\t});\n\n\t\tlink.getChildren().addAll(design);\n\t\tAffine manipulator = (Affine) dh.getListener(linkIndex);\n\t\tTransformNR offset = dh.getDHStep(linkIndex).inverse();\n\t\tAffine lastLinkAffine = linkIndex == 0 ? (Affine) dh.getRootListener() : (Affine) dh.getListener(linkIndex - 1);\n\n\t\taddVitamins(dh.getLinkConfiguration(linkIndex), link, callbackMapForTreeitems, widgetMapForTreeitems,\n\t\t\t\tselected -> {\n\t\t\t\t\tAffine linkObjectManipulator = (Affine) dh.getLinkObjectManipulator(linkIndex);\n\t\t\t\t\tTransformNR pose = TransformFactory.affineToNr(linkObjectManipulator);\n\t\t\t\t\tif (selected.getFrame() == VitaminFrame.LinkOrigin) {\n\t\t\t\t\t\tTransformNR step = dh.getDHStep(linkIndex).inverse();\n\t\t\t\t\t\tpose = pose.times(step);\n\t\t\t\t\t}\n\t\t\t\t\tif (selected.getFrame() == VitaminFrame.previousLinkTip) {\n\t\t\t\t\t\tAffine ll;\n\t\t\t\t\t\tif (linkIndex == 0) {\n\t\t\t\t\t\t\tll = (Affine) dh.getRootListener();\n\t\t\t\t\t\t} else\n\t\t\t\t\t\t\tll = (Affine) dh.getLinkObjectManipulator(linkIndex - 1);\n\t\t\t\t\t\tpose = TransformFactory.affineToNr(ll);\n\t\t\t\t\t}\n\t\t\t\t\treturn pose;\n\t\t\t\t}, manipulator, lastLinkAffine, offset);\n\n\t\tlink.getChildren().addAll(slaves, remove);\n\n\t\trootItem.getChildren().add(0, link);\n\n\t}\n\n\tprivate static TreeItem<String> setUpNewMobileBaseEditor(TreeView<String> view,\n\t\t\tHashMap<TreeItem<String>, Runnable> callbackMapForTreeitems,\n\t\t\tHashMap<TreeItem<String>, Parent> widgetMapForTreeitems, CreatureLab creatureLab, boolean isOwner,\n\t\t\tDHLink dhLink) {\n\t\tTreeItem<String> mobile = new TreeItem<>(dhLink.getSlaveMobileBase().getScriptingName(),\n\t\t\t\tAssetFactory.loadIcon(\"creature.png\"));\n\t\tMobleBaseMenueFactory.load(dhLink.getSlaveMobileBase(), view, mobile, callbackMapForTreeitems,\n\t\t\t\twidgetMapForTreeitems, creatureLab, false, isOwner);\n\t\treturn mobile;\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate static void loadSingleLimb(MobileBase base, TreeView<String> view, DHParameterKinematics dh,\n\t\t\tTreeItem<String> rootItem, HashMap<TreeItem<String>, Runnable> callbackMapForTreeitems,\n\t\t\tHashMap<TreeItem<String>, Parent> widgetMapForTreeitems, CreatureLab creatureLab,\n\t\t\tboolean creatureIsOwnedByUser) throws Exception {\n\n\t\tTreeItem<String> dhItem = new TreeItem<>(dh.getScriptingName(), AssetFactory.loadIcon(\"Move-Limb.png\"));\n\t\tJogWidget widget = new JogWidget(dh, base);\n\t\tcallbackMapForTreeitems.put(dhItem, () -> {\n\n\t\t\tif (widgetMapForTreeitems.get(dhItem) == null) {\n\t\t\t\t// create the widget for the leg when looking at it for the\n\t\t\t\t// first time\n\n\t\t\t\tVBox jog = new VBox(10);\n\t\t\t\tjog.getChildren().add(new Label(\"Jog Limb\"));\n\t\t\t\tjog.getChildren().add(widget);\n\t\t\t\twidgetMapForTreeitems.put(dhItem, new Group(jog));\n\t\t\t}\n\t\t\tBowlerJInputDevice controller = creatureLab.getController();\n\t\t\tif (controller != null) {\n\t\t\t\twidget.setGameController(controller);\n\t\t\t}\n\t\t\tParallelGroup parallel = base.getParallelGroup(dh);\n\t\t\tif (parallel == null)\n\t\t\t\twidget.setCurrent(dh.getCurrentTaskSpaceTransform());\n\t\t\telse\n\t\t\t\twidget.setCurrent(parallel.getCurrentPoseTarget());\n\t\t\tBowlerStudio.select(base, dh);\n\n\t\t});\n\n\t\tTreeItem<String> remove = new TreeItem<>(\"Remove \" + dh.getScriptingName(),\n\t\t\t\tAssetFactory.loadIcon(\"Remove-Limb.png\"));\n\n\t\tcallbackMapForTreeitems.put(remove, () -> {\n\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\tAlert alert = new Alert(AlertType.CONFIRMATION);\n\t\t\t\talert.setTitle(\"Confirm removing limb\");\n\t\t\t\talert.setHeaderText(\"This will remove \" + dh.getScriptingName());\n\t\t\t\talert.setContentText(\"Are sure you wish to remove this limb?\");\n\t\t\t\tNode root = alert.getDialogPane();\n\t\t\t\tStage stage = (Stage) alert.getDialogPane().getScene().getWindow();\n\t\t\t\tstage.setOnCloseRequest(ev -> alert.hide());\n\t\t\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\t\t\tint tmp = fontNum - 10;\n\t\t\t\t\tif (tmp < 12)\n\t\t\t\t\t\ttmp = 12;\n\t\t\t\t\troot.setStyle(\"-fx-font-size: \" + tmp + \"pt\");\n\t\t\t\t\talert.getDialogPane().applyCss();\n\t\t\t\t\talert.getDialogPane().layout();\n\t\t\t\t\tstage.sizeToScene();\n\t\t\t\t});\n\t\t\t\tOptional<ButtonType> result = alert.showAndWait();\n\t\t\t\tview.getSelectionModel().select(rootItem);\n\t\t\t\tif (result.get() == ButtonType.OK) {\n\n\t\t\t\t\trootItem.getChildren().remove(dhItem);\n\t\t\t\t\tif (base.getLegs().contains(dh)) {\n\t\t\t\t\t\tbase.getLegs().remove(dh);\n\t\t\t\t\t}\n\t\t\t\t\tif (base.getAppendages().contains(dh)) {\n\t\t\t\t\t\tbase.getAppendages().remove(dh);\n\t\t\t\t\t}\n\t\t\t\t\tif (base.getSteerable().contains(dh)) {\n\t\t\t\t\t\tbase.getSteerable().remove(dh);\n\t\t\t\t\t}\n\t\t\t\t\tif (base.getDrivable().contains(dh)) {\n\t\t\t\t\t\tbase.getDrivable().remove(dh);\n\t\t\t\t\t}\n\t\t\t\t\tif (base.getParallelGroup(dh) != null)\n\t\t\t\t\t\tbase.getParallelGroup(dh).removeLimb(dh);\n\t\t\t\t\tcreatureLab.generateCad(CSGDatabase.getInstance());\n\t\t\t\t}\n\t\t\t});\n\n\t\t});\n\t\tint j = 0;\n\t\ttry {\n\t\t\tfor (LinkConfiguration conf : dh.getFactory().getLinkConfigurations()) {\n\n\t\t\t\tloadSingleLink(j++, base, view, conf, dh, dhItem, callbackMapForTreeitems, widgetMapForTreeitems,\n\t\t\t\t\t\tcreatureLab, creatureIsOwnedByUser);\n\n\t\t\t}\n\n\t\t\tTreeItem<String> addLink = new TreeItem<>(\"Add Link\", AssetFactory.loadIcon(\"Add-Link.png\"));\n\n\t\t\tcallbackMapForTreeitems.put(addLink, () -> {\n\t\t\t\t// if(widgetMapForTreeitems.get(advanced)==null){\n\t\t\t\t// //create the widget for the leg when looking at it for the first\n\t\t\t\t// time\n\t\t\t\t// widgetMapForTreeitems.put(advanced, new DhChainWidget(dh,\n\t\t\t\t// creatureLab));\n\t\t\t\t// }\n\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\tint size = dh.getLinkConfigurations().size();\n\t\t\t\t\tTextInputDialog alert = new TextInputDialog(\"Link_\" + size);\n\t\t\t\t\talert.setTitle(\"Add a new link of\");\n\t\t\t\t\talert.setHeaderText(\"Set the scripting name for this link\");\n\t\t\t\t\talert.setContentText(\"Please the name of the new link:\");\n\t\t\t\t\tNode root = alert.getDialogPane();\n\t\t\t\t\tStage stage = (Stage) alert.getDialogPane().getScene().getWindow();\n\t\t\t\t\tstage.setOnCloseRequest(ev -> alert.hide());\n\t\t\t\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\t\t\t\tint tmp = fontNum - 10;\n\t\t\t\t\t\tif (tmp < 12)\n\t\t\t\t\t\t\ttmp = 12;\n\t\t\t\t\t\troot.setStyle(\"-fx-font-size: \" + tmp + \"pt\");\n\t\t\t\t\t\talert.getDialogPane().applyCss();\n\t\t\t\t\t\talert.getDialogPane().layout();\n\t\t\t\t\t\tstage.sizeToScene();\n\t\t\t\t\t});\n\t\t\t\t\t// Traditional way to get the response value.\n\t\t\t\t\tOptional<String> result = alert.showAndWait();\n\t\t\t\t\tif (result.isPresent()) {\n\t\t\t\t\t\tview.getSelectionModel().select(rootItem);\n\t\t\t\t\t\tnew Thread() {\n\t\t\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Your new link: \" + result.get());\n\t\t\t\t\t\t\t\tLinkConfiguration newLink = new LinkConfiguration(\n\t\t\t\t\t\t\t\t\t\tdh.getLinkConfigurations().get(size - 1));\n\t\t\t\t\t\t\t\tnewLink.setLinkIndex(newLink.getLinkIndex() + 1);\n\t\t\t\t\t\t\t\tArrayList<LinkConfiguration> linkConfigurations = dh.getFactory()\n\t\t\t\t\t\t\t\t\t\t.getLinkConfigurations();\n\n\t\t\t\t\t\t\t\tint numOfLinks = linkConfigurations.size();\n\n\t\t\t\t\t\t\t\t// newLink.setType(typeOfLink);\n\t\t\t\t\t\t\t\tgetNextChannel(base, newLink);\n\t\t\t\t\t\t\t\tnewLink.setName(result.get());\n\t\t\t\t\t\t\t\tDHLink dhl = dh.getDhLink(numOfLinks - 1);\n\t\t\t\t\t\t\t\tif (dh != null) {\n\t\t\t\t\t\t\t\t\tDHLink dhLink = new DHLink(dhl);\n\t\t\t\t\t\t\t\t\tdhLink.setListener(new Affine());\n\t\t\t\t\t\t\t\t\tdh.addNewLink(newLink, dhLink);\n\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\tdh.setDesiredJointSpaceVector(dh.getCurrentJointSpaceTarget(), 0);\n\t\t\t\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\t\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(e);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tloadSingleLink(dh.getLinkConfigurations().size() - 1, base, view, newLink, dh,\n\t\t\t\t\t\t\t\t\t\t\tdhItem, callbackMapForTreeitems, widgetMapForTreeitems, creatureLab,\n\t\t\t\t\t\t\t\t\t\t\tcreatureIsOwnedByUser);\n\t\t\t\t\t\t\t\t\tMobileBaseCadManager.get(CSGDatabase.getInstance(), base)\n\t\t\t\t\t\t\t\t\t\t\t.generateCad(CSGDatabase.getInstance());\n\t\t\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(e);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tcreatureLab.generateCad(CSGDatabase.getInstance());\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}.start();\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t});\n\t\t\tdhItem.getChildren().addAll(addLink, remove);\n\n\t\t} catch (Throwable T) {\n\t\t\tcom.neuronrobotics.sdk.common.Log.error(T);\n\t\t}\n\t\tTreeItem<String> parallel = new TreeItem<>(\"Parallel Settings\",\n\t\t\t\tAssetFactory.loadIcon(\"Design-Parameter-Adjustment.png\"));\n\n\t\tcallbackMapForTreeitems.put(parallel, () -> {\n\t\t\tif (widgetMapForTreeitems.get(parallel) == null) {\n\t\t\t\t// create the widget for the leg when looking at it for the\n\t\t\t\t// first time\n\t\t\t\ttry {\n\t\t\t\t\twidgetMapForTreeitems.put(parallel, new ParallelWidget(base, dh, creatureLab));\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\tBowlerStudio.printStackTrace(ex);\n\t\t\t\t}\n\t\t\t}\n\t\t\t((ParallelWidget) widgetMapForTreeitems.get(parallel)).configure(base, dh, creatureLab);\n\t\t});\n\t\tdhItem.getChildren().addAll(parallel);\n\n\t\tTreeItem<String> PlaceLimb = new TreeItem<>(\"Move Root Of Limb\",\n\t\t\t\tAssetFactory.loadIcon(\"Design-Parameter-Adjustment.png\"));\n\n\t\tcallbackMapForTreeitems.put(PlaceLimb, () -> {\n\t\t\tif (widgetMapForTreeitems.get(PlaceLimb) == null) {\n\t\t\t\t// create the widget for the leg when looking at it for the\n\t\t\t\t// first time\n\t\t\t\ttry {\n\t\t\t\t\twidgetMapForTreeitems.put(PlaceLimb,\n\t\t\t\t\t\t\tnew Group(new TransformWidget(\"Move place where limb is attached to body\",\n\t\t\t\t\t\t\t\t\tdh.getRobotToFiducialTransform(), new IOnTransformChange() {\n\n\t\t\t\t\t\t\t\t\t\t@Override\n\t\t\t\t\t\t\t\t\t\tpublic void onTransformFinished(TransformNR newTrans) {\n\t\t\t\t\t\t\t\t\t\t\t// Force a cad regeneration\n\t\t\t\t\t\t\t\t\t\t\tcreatureLab.onSliderDoneMoving(null, 0);\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t@Override\n\t\t\t\t\t\t\t\t\t\tpublic void onTransformChaging(TransformNR newTrans) {\n\t\t\t\t\t\t\t\t\t\t\tLog.debug(\"Limb to base\" + newTrans.toString());\n\t\t\t\t\t\t\t\t\t\t\tdh.setRobotToFiducialTransform(newTrans);\n\t\t\t\t\t\t\t\t\t\t\tdh.refreshPose();\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t})));\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\tBowlerStudio.printStackTrace(ex);\n\t\t\t\t}\n\t\t\t}\n\n\t\t});\n\t\tdhItem.getChildren().addAll(PlaceLimb);\n\n\t\t// TreeItem<String> advanced = new TreeItem<>(\"Advanced Configuration\",\n\t\t// AssetFactory.loadIcon(\"Advanced-Configuration.png\"));\n\t\t//\n\t\t// callbackMapForTreeitems.put(advanced, () -> {\n\t\t// if (widgetMapForTreeitems.get(advanced) == null) {\n\t\t// // create the widget for the leg when looking at it for the\n\t\t// // first time\n\t\t// try {\n\t\t// widgetMapForTreeitems.put(advanced, new DhChainWidget(dh, creatureLab));\n\t\t// } catch (Exception ex) {\n\t\t// BowlerStudio.printStackTrace(ex);\n\t\t// }\n\t\t// }\n\t\t//\n\t\t// });\n\t\tif (creatureIsOwnedByUser) {\n\t\t\t// TreeItem<String> owner = new\n\t\t\t// TreeItem<>(\"Scripts\",AssetFactory.loadIcon(\"Owner.png\"));\n\t\t\tTreeItem<String> setCAD = new TreeItem<>(\"Set CAD Engine...\", AssetFactory.loadIcon(\"Set-CAD-Engine.png\"));\n\t\t\tcallbackMapForTreeitems.put(setCAD, () -> {\n\t\t\t\tPromptForGit.prompt(\"Select a CAD Engine From Git\", dh.getGitCadEngine()[0], (gitsId, file) -> {\n\t\t\t\t\tLog.warning(\"Loading cad engine\");\n\t\t\t\t\ttry {\n\t\t\t\t\t\tcreatureLab.setGitCadEngine(gitsId, file, dh);\n\t\t\t\t\t\topenCadTab(creatureLab, gitsId, file);\n\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(e);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t});\n\t\t\tTreeItem<String> editCAD = new TreeItem<>(\"Edit CAD Engine...\",\n\t\t\t\t\tAssetFactory.loadIcon(\"Edit-CAD-Engine.png\"));\n\t\t\tcallbackMapForTreeitems.put(editCAD, () -> {\n\t\t\t\ttry {\n\t\t\t\t\topenCadTab(creatureLab, dh.getGitCadEngine()[0], dh.getGitCadEngine()[1]);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(e);\n\t\t\t\t}\n\t\t\t});\n\t\t\tTreeItem<String> resetWalking = new TreeItem<>(\"Set Dh Kinematics Engine...\",\n\t\t\t\t\tAssetFactory.loadIcon(\"Set-DH-Kinematics.png\"));\n\t\t\tcallbackMapForTreeitems.put(resetWalking, () -> {\n\t\t\t\tPromptForGit.prompt(\"Select a DH Solver Engine From Git\", dh.getGitDhEngine()[0], (gitsId, file) -> {\n\t\t\t\t\tLog.warning(\"Loading walking engine\");\n\t\t\t\t\ttry {\n\t\t\t\t\t\tcreatureLab.setGitDhEngine(gitsId, file, dh);\n\t\t\t\t\t\tFile code = ScriptingEngine.fileFromGit(gitsId, file);\n\t\t\t\t\t\tBowlerStudio.createFileTab(code);\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(e);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t});\n\t\t\tTreeItem<String> editWalking = new TreeItem<>(\"Edit Kinematics Engine...\",\n\t\t\t\t\tAssetFactory.loadIcon(\"Edit-Kinematics-Engine.png\"));\n\t\t\tcallbackMapForTreeitems.put(editWalking, () -> {\n\t\t\t\ttry {\n\t\t\t\t\tFile code = ScriptingEngine.fileFromGit(dh.getGitDhEngine()[0], dh.getGitDhEngine()[1]);\n\t\t\t\t\tBowlerStudio.createFileTab(code);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(e);\n\t\t\t\t}\n\t\t\t});\n\t\t\tdhItem.getChildren().addAll(editWalking, editCAD, resetWalking, setCAD);\n\n\t\t}\n\t\trootItem.getChildren().add(dhItem);\n\t\tdouble[] vect = dh.getCurrentJointSpaceVector();\n\t\tfor (int i = 0; i < vect.length; i++) {\n\t\t\tvect[i] = 0;\n\t\t}\n\t\ttry {\n\t\t\tdh.setDesiredJointSpaceVector(vect, 1);\n\t\t} catch (Exception e) {\n\t\t\t// Auto-generated catch block\n\t\t\tcom.neuronrobotics.sdk.common.Log.error(e);\n\t\t}\n\t\tdh.updateCadLocations();\n\n\t}\n\n\tprivate static void openCadTab(CreatureLab creatureLab, String gitsId, String file)\n\t\t\tthrows InvalidRemoteException, TransportException, GitAPIException, IOException {\n\t\tFile code = ScriptingEngine.fileFromGit(gitsId, file);\n\t\tScriptingFileWidget wid = BowlerStudio.createFileTab(code);\n\n\t}\n\n\t/**\n\t * @return the baseDirForFiles\n\t */\n\tpublic static File getBaseDirForFiles() {\n\t\tif (baseDirForFiles == null)\n\t\t\tbaseDirForFiles = new File(System.getProperty(\"user.home\") + \"/bowler-workspace/STL/\");\n\t\tif (!baseDirForFiles.exists()) {\n\t\t\tbaseDirForFiles.mkdirs();\n\t\t}\n\t\treturn baseDirForFiles;\n\t}\n\n\t/**\n\t * @param baseDirForFiles\n\t *            the baseDirForFiles to set\n\t */\n\tpublic static void setBaseDirForFiles(File baseDirForFiles) {\n\t\tMobleBaseMenueFactory.baseDirForFiles = baseDirForFiles;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/creature/ParallelWidget.java",
    "content": "package com.neuronrobotics.bowlerstudio.creature;\n\n//import org.jfree.util.Log;\n\nimport com.neuronrobotics.bowlerstudio.BowlerStudio;\nimport com.neuronrobotics.sdk.addons.kinematics.DHParameterKinematics;\nimport com.neuronrobotics.sdk.addons.kinematics.MobileBase;\nimport com.neuronrobotics.sdk.addons.kinematics.math.TransformNR;\nimport com.neuronrobotics.sdk.addons.kinematics.parallel.ParallelGroup;\n\nimport javafx.scene.*;\nimport javafx.scene.Node;\nimport javafx.scene.control.CheckBox;\nimport javafx.scene.control.ComboBox;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.TextField;\nimport javafx.scene.layout.HBox;\nimport javafx.scene.layout.VBox;\n\npublic class ParallelWidget extends Group {\n\tVBox boxTop = new VBox();\n\tVBox box = new VBox();\n\tVBox relativeToControls = new VBox();\n\tCheckBox useRelative = new CheckBox(\"This limb is relative to another link\");\n\tCheckBox useParallel = new CheckBox(\"This limb is part of a parallel group\");\n\tTransformWidget e;\n\tTextField groupName = new TextField();\n\tComboBox<String> relativeName = new ComboBox<String>();\n\tComboBox<Integer> relIndex = new ComboBox<Integer>();\n\tTransformNR robotToFiducialTransform = new TransformNR();\n\tprivate MobileBase base;\n\tprivate DHParameterKinematics dh;\n\tprivate CreatureLab creatureLab;\n\tboolean resetting = false;\n\n\tprivate HBox row(String label, Node tf) {\n\t\tHBox h = new HBox();\n\t\th.getChildren().add(new Label(label));\n\t\th.getChildren().add(tf);\n\t\treturn h;\n\t}\n\n\tpublic ParallelWidget(MobileBase b, DHParameterKinematics d, CreatureLab c) {\n\t\tthis.base = b;\n\t\tthis.dh = d;\n\t\tthis.creatureLab = c;\n\n\t\tuseParallel.setSelected(false);\n\t\tuseParallel.setOnAction(event -> {\n\t\t\tbox.setDisable(!useParallel.isSelected());\n\t\t\tif (useParallel.isSelected()) {\n\t\t\t\tif (groupName.getText().length() > 0) {\n\t\t\t\t\tsetupAddReferenceSection();\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tgetGroup().removeLimb(dh);\n\t\t\t\tuseRelative.setSelected(false);\n\t\t\t}\n\t\t});\n\n\t\tuseRelative.setSelected(false);\n\t\tuseRelative.setOnAction(event -> {\n\t\t\trelativeToControls.setDisable(!useRelative.isSelected());\n\t\t\tif (useRelative.isSelected()) {\n\t\t\t\tsetupAddReferenceSection();\n\t\t\t} else {\n\t\t\t\tgetGroup().clearReferencedLimb(dh);\n\t\t\t}\n\t\t});\n\n\t\tgroupName.setOnAction(event -> {\n\t\t\tif (resetting)\n\t\t\t\treturn;\n\t\t\tif (getGroup() != null) {\n\t\t\t\tbase.shutDownParallel(getGroup());\n\t\t\t}\n\t\t\tif (groupName.getText().length() > 0) {\n\t\t\t\tsetupAddReferenceSection();\n\t\t\t} else {\n\t\t\t\trelativeToControls.setDisable(true);\n\t\t\t}\n\t\t\thome();\n\t\t});\n\n\t\trelativeName.setOnAction(event -> {\n\t\t\tif (resetting)\n\t\t\t\treturn;\n\t\t\tString refLimbName = relativeName.getValue();\n\t\t\tsetNewReferencedLimb(base, refLimbName);\n\t\t\trelIndex.setDisable(false);\n\n\t\t});\n\t\trelIndex.setOnAction(event -> {\n\t\t\tif (resetting)\n\t\t\t\treturn;\n\t\t\ttry {\n\t\t\t\tgetGroup().setupReferencedLimb(dh, robotToFiducialTransform, relativeName.getValue(),\n\t\t\t\t\t\trelIndex.getValue());\n\t\t\t\te.setDisable(false);\n\t\t\t} catch (java.lang.NullPointerException e) {\n\t\t\t}\n\t\t\thome();\n\t\t});\n\n\t\te = new TransformWidget(\"Parallel Tip Offset\", robotToFiducialTransform, new IOnTransformChange() {\n\n\t\t\t@Override\n\t\t\tpublic void onTransformFinished(TransformNR newTrans) {\n\t\t\t\tif (resetting)\n\t\t\t\t\treturn;\n\t\t\t\thome();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onTransformChaging(TransformNR newTrans) {\n\t\t\t\tif (resetting)\n\t\t\t\t\treturn;\n\t\t\t\trobotToFiducialTransform = newTrans;\n\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Tip offset for \" + dh.getScriptingName() + \" \" + newTrans);\n\t\t\t\tgetGroup().setTipOffset(dh, newTrans);\n\t\t\t\tdh.refreshPose();\n\t\t\t\thome();\n\t\t\t}\n\t\t});\n\t\tBowlerStudio.runLater(() -> getChildren().add(boxTop));\n\t\tBowlerStudio.runLater(() -> boxTop.getChildren().add(useParallel));\n\t\tBowlerStudio.runLater(() -> boxTop.getChildren().add(box));\n\t\tBowlerStudio.runLater(() -> box.getChildren().add(row(\"Parallel Group Name\", groupName)));\n\t\tBowlerStudio.runLater(() -> box.getChildren().add(useRelative));\n\t\tBowlerStudio.runLater(() -> box.getChildren().add(relativeToControls));\n\n\t\tBowlerStudio.runLater(() -> relativeToControls.getChildren().add(row(\"Limb Relative\", relativeName)));\n\t\tBowlerStudio.runLater(() -> relativeToControls.getChildren().add(row(\"Limb Relative index\", relIndex)));\n\t\tBowlerStudio.runLater(() -> relativeToControls.getChildren().add(e));\n\t}\n\n\tprivate void home() {\n\t\ttry {\n\t\t\tgetGroup().setDesiredTaskSpaceTransform(getGroup().getCurrentPoseTarget(), 0);\n\t\t} catch (Exception e) {\n\t\t}\n\t}\n\n\tprivate void setupAddReferenceSection() {\n\t\tbase.getParallelGroup(groupName.getText()).setupReferencedLimbStartup(dh, null, \"\", 0);\n\t\tBowlerStudio.runLater(() -> relativeName.getItems().clear());\n\t\tfor (DHParameterKinematics l : base.getAllDHChains()) {\n\t\t\tif (!l.getScriptingName().contentEquals(dh.getScriptingName())) {\n\t\t\t\tBowlerStudio.runLater(() -> relativeName.getItems().add(l.getScriptingName()));\n\t\t\t}\n\t\t}\n\n\t\trelIndex.setDisable(true);\n\t\te.setDisable(true);\n\t}\n\n\tpublic void configure(MobileBase b, DHParameterKinematics dh, CreatureLab creatureLab) {\n\t\tresetting = true;\n\t\tthis.base = b;\n\t\tthis.dh = dh;\n\t\tthis.creatureLab = creatureLab;\n\t\tcom.neuronrobotics.sdk.common.Log.error(\"Configuring arm \" + dh.getScriptingName());\n\t\trobotToFiducialTransform = new TransformNR();\n\t\tBowlerStudio.runLater(() -> groupName.setText(\"\"));\n\t\tBowlerStudio.runLater(() -> relativeName.getItems().clear());\n\t\tBowlerStudio.runLater(() -> relIndex.getItems().clear());\n\n\t\tBowlerStudio.runLater(() -> relIndex.setDisable(true));\n\t\tBowlerStudio.runLater(() -> e.setDisable(true));\n\n\t\tif (getGroup() == null) {\n\t\t\tuseParallel.setSelected(false);\n\t\t\tbox.setDisable(true);\n\t\t} else {\n\t\t\tuseParallel.setSelected(true);\n\t\t\tbox.setDisable(false);\n\t\t\tBowlerStudio.runLater(() -> groupName.setText(getGroup().getNameOfParallelGroup()));\n\t\t\tfor (DHParameterKinematics l : base.getAllDHChains()) {\n\t\t\t\tif (!l.getScriptingName().contentEquals(dh.getScriptingName())) {\n\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Adding Option \" + l.getScriptingName());\n\t\t\t\t\tBowlerStudio.runLater(() -> relativeName.getItems().add(l.getScriptingName()));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (getGroup().getTipOffset(dh) != null) {\n\t\t\t\tBowlerStudio.runLater(() -> useRelative.setSelected(true));\n\t\t\t\tBowlerStudio.runLater(() -> relativeToControls.setDisable(false));\n\t\t\t\tBowlerStudio.runLater(() -> relIndex.setDisable(false));\n\t\t\t\tBowlerStudio.runLater(() -> e.setDisable(false));\n\t\t\t\trobotToFiducialTransform = getGroup().getTipOffset(dh);\n\t\t\t\tBowlerStudio.runLater(() -> e.updatePose(robotToFiducialTransform));\n\t\t\t\tString refLimbName = getGroup().getTipOffsetRelativeName(dh);\n\t\t\t\tsetNewReferencedLimb(base, refLimbName);\n\t\t\t\tBowlerStudio.runLater(() -> relativeName.setValue(refLimbName));\n\t\t\t\tBowlerStudio.runLater(() -> relIndex.setValue(getGroup().getTipOffsetRelativeIndex(dh)));\n\t\t\t} else {\n\t\t\t\tBowlerStudio.runLater(() -> useRelative.setSelected(false));\n\t\t\t\tBowlerStudio.runLater(() -> relativeToControls.setDisable(true));\n\t\t\t}\n\t\t}\n\t\te.updatePose(robotToFiducialTransform);\n\t\tBowlerStudio.runLater(() -> resetting = false);\n\t\thome();\n\t}\n\n\tprivate void setNewReferencedLimb(MobileBase base, String refLimbName) {\n\n\t\tDHParameterKinematics referencedLimb = null;\n\t\tfor (DHParameterKinematics lm : base.getAllDHChains()) {\n\t\t\tif (lm.getScriptingName().toLowerCase().contentEquals(refLimbName.toLowerCase())) {\n\t\t\t\t// FOund the referenced limb\n\t\t\t\treferencedLimb = lm;\n\t\t\t}\n\t\t}\n\t\tBowlerStudio.runLater(() -> relIndex.getItems().clear());\n\t\tDHParameterKinematics rl = referencedLimb;\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tfor (int i = 0; i < rl.getNumberOfLinks(); i++) {\n\t\t\t\trelIndex.getItems().add(i);\n\t\t\t}\n\t\t});\n\t}\n\n\tpublic ParallelGroup getGroup() {\n\t\treturn base.getParallelGroup(dh);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/creature/PhysicsWidget.java",
    "content": "package com.neuronrobotics.bowlerstudio.creature;\n\nimport com.neuronrobotics.bowlerstudio.BowlerStudio;\nimport com.neuronrobotics.bowlerstudio.BowlerStudioController;\nimport com.neuronrobotics.bowlerstudio.CreatureLab3dController;\nimport com.neuronrobotics.bowlerstudio.assets.AssetFactory;\nimport com.neuronrobotics.bowlerstudio.assets.ConfigurationDatabase;\nimport com.neuronrobotics.bowlerstudio.physics.MuJoCoPhysicsManager;\nimport com.neuronrobotics.bowlerstudio.scripting.ScriptingEngine;\nimport com.neuronrobotics.bowlerstudio.vitamins.Vitamins;\nimport com.neuronrobotics.sdk.addons.kinematics.MobileBase;\nimport com.neuronrobotics.sdk.addons.kinematics.imu.IMUUpdate;\nimport com.neuronrobotics.sdk.addons.kinematics.imu.IMUUpdateListener;\nimport com.neuronrobotics.sdk.addons.kinematics.math.RotationNR;\nimport com.neuronrobotics.sdk.addons.kinematics.math.TransformNR;\nimport com.neuronrobotics.sdk.common.BowlerAbstractDevice;\nimport com.neuronrobotics.sdk.common.IDeviceConnectionEventListener;\nimport com.neuronrobotics.sdk.util.ThreadUtil;\n\nimport eu.mihosoft.vrl.v3d.CSG;\nimport eu.mihosoft.vrl.v3d.parametrics.CSGDatabase;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.ComboBox;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.TextField;\nimport javafx.scene.layout.GridPane;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Map;\nimport java.util.Set;\n\nimport javax.xml.bind.JAXBException;\n\nimport org.mujoco.xml.attributetypes.IntegratorType;\n\n@SuppressWarnings(\"restriction\")\npublic class PhysicsWidget extends GridPane implements IMUUpdateListener {\n\tButton runstop = new Button(\"Run\", AssetFactory.loadIcon(\"Run.png\"));\n\tButton pauseresume = new Button(\"Pause\", AssetFactory.loadIcon(\"Pause.png\"));\n\tButton step = new Button(\"Step\", AssetFactory.loadIcon(\"Step.png\"));\n\tTextField msLoopTime = new TextField(\"1\");\n\tint msLoopTimeInt = 0;\n\tprivate boolean run = false;\n\tprivate boolean takestep = false;\n\tprivate boolean pause = false;\n\tThread physicsThread = null;\n\tprivate Set<CSG> oldParts = null;\n\tprivate MobileBase base;\n\tprivate MuJoCoPhysicsManager mujoco;\n\tprivate ComboBox<String> filesStatic;\n\tprivate ComboBox<String> filesMoving;\n\tprivate TextField gitStatic;\n\tprivate TextField gitMoving;\n\tprivate ArrayList<CSG> staticObjects = null;\n\n\tprivate ArrayList<CSG> movingObjects = null;\n\n\t@SuppressWarnings(\"restriction\")\n\tpublic PhysicsWidget(MobileBase base) {\n\n\t\tthis.base = base;\n\t\tbase.addConnectionEventListener(new IDeviceConnectionEventListener() {\n\n\t\t\t@Override\n\t\t\tpublic void onDisconnect(BowlerAbstractDevice arg0) {\n\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\tstopPhysics();\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onConnect(BowlerAbstractDevice arg0) {\n\t\t\t}\n\t\t});\n\t\tGridPane controls = new GridPane();\n\t\tBowlerStudio.setToRunButton(runstop);\n\t\tcontrols.add(runstop, 0, 0);\n\t\tcontrols.add(pauseresume, 1, 0);\n\t\tcontrols.add(step, 2, 0);\n\t\tcontrols.add(new Label(\"MS loop\"), 3, 0);\n\t\tcontrols.add(msLoopTime, 4, 0);\n\t\tadd(controls, 0, 0);\n\t\tadd(new Label(\"Static Objects:\"), 0, 1);\n\t\tgitStatic = new TextField();\n\t\tfilesStatic = new ComboBox<>();\n\t\tadd(gitStatic, 0, 2);\n\t\tadd(filesStatic, 1, 2);\n\n\t\tadd(new Label(\"Moving Objects:\"), 0, 4);\n\t\tgitMoving = new TextField();\n\t\tfilesMoving = new ComboBox<>();\n\t\tadd(gitMoving, 0, 5);\n\t\tadd(filesMoving, 1, 5);\n\n\t\tfilesMoving.setDisable(true);\n\t\tfilesStatic.setDisable(true);\n\t\tfilesMoving.setOnAction(event -> {\n\t\t\tupdateObjects();\n\t\t});\n\t\tfilesStatic.setOnAction(event -> {\n\t\t\tupdateObjects();\n\t\t});\n\t\tString string = ConfigurationDatabase\n\t\t\t\t.getObject(\"PhysicsWidget\", \"gitMoving\", \"https://github.com/madhephaestus/VexHighStakes2024.git\")\n\t\t\t\t.toString();\n\t\tgitMoving.setText(string);\n\t\tString string2 = ConfigurationDatabase\n\t\t\t\t.getObject(\"PhysicsWidget\", \"gitStatic\", \"https://github.com/madhephaestus/VexHighStakes2024.git\")\n\t\t\t\t.toString();\n\t\tgitStatic.setText(string2);\n\t\tvalidateInput();\n\t\tgitMoving.textProperty().addListener((observable, oldValue, newValue) -> {\n\t\t\tvalidateInput();\n\t\t});\n\t\tgitStatic.textProperty().addListener((observable, oldValue, newValue) -> {\n\t\t\tvalidateInput();\n\t\t});\n\n\t\tpauseresume.setDisable(true);\n\t\tstep.setDisable(true);\n\t\tpauseresume.setOnAction(event -> {\n\t\t\tif (!isPause()) {\n\t\t\t\tpauseresume.setGraphic(AssetFactory.loadIcon(\"Resume.png\"));\n\t\t\t\tpauseresume.setText(\"Resume\");\n\t\t\t\tstep.setDisable(false);\n\t\t\t} else {\n\t\t\t\tpauseresume.setGraphic(AssetFactory.loadIcon(\"Pause.png\"));\n\t\t\t\tpauseresume.setText(\"Pause\");\n\t\t\t\tstep.setDisable(true);\n\t\t\t}\n\t\t\tsetPause(!isPause());\n\t\t});\n\t\tstep.setOnAction(event -> {\n\t\t\tsetTakestep(true);\n\t\t});\n\t\trunstop.setOnAction(event -> {\n\t\t\tif (isRun()) {\n\t\t\t\tstopPhysics();\n\t\t\t\t// new Thread(){\n\t\t\t\t// public void run(){\n\t\t\t\t// ThreadUtil.wait(50);\n\t\t\t\t// System.gc();// clean up any objects created by the physics engine\n\t\t\t\t// }\n\t\t\t\t// }.start();\n\t\t\t} else {\n\t\t\t\t// System.gc();// clean up any objects created by the physics engine\n\t\t\t\t// runstop.setGraphic(AssetFactory.loadIcon(\"Stop.png\"));\n\t\t\t\t// runstop.setText(\"Stop\");\n\t\t\t\tBowlerStudio.setToStopButton(runstop);\n\t\t\t\tmsLoopTime.setDisable(true);\n\t\t\t\tpauseresume.setDisable(false);\n\t\t\t\tbase.getImu().addvirtualListeners(this);\n\t\t\t\tnew Thread() {\n\n\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\twhile (MobileBaseCadManager.get(CSGDatabase.getInstance(), base).getProcesIndictor()\n\t\t\t\t\t\t\t\t.get() < 1) {\n\t\t\t\t\t\t\tThreadUtil.wait(100);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbase.DriveArc(new TransformNR(.01, 0, 0, new RotationNR()), 0);\n\t\t\t\t\t\tArrayList<MobileBase> bases = new ArrayList<>();\n\t\t\t\t\t\tbases.add(base);\n\t\t\t\t\t\tFile cache = new File(ScriptingEngine.getWorkspace().getAbsolutePath() + \"/physics-\"\n\t\t\t\t\t\t\t\t+ base.getScriptingName());\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tmujoco = new MuJoCoPhysicsManager(CSGDatabase.getInstance(), base.getScriptingName(), bases,\n\t\t\t\t\t\t\t\t\tmovingObjects, staticObjects, cache);\n\n\t\t\t\t\t\t} catch (IOException | JAXBException e) {\n\t\t\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\t\t\te.printStackTrace();\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// MobileBasePhysicsManager m =new MobileBasePhysicsManager(base, baseCad,\n\t\t\t\t\t\t// simplecad);\n\t\t\t\t\t\t// BowlerStudio3dEngine threeD =\n\t\t\t\t\t\t// BowlerStudioController.getBowlerStudio().getJfx3dmanager();\n\t\t\t\t\t\toldParts = CreatureLab3dController.getEngine().getCsgMap().keySet();\n\t\t\t\t\t\tdouble loopTiming = (int) Double.parseDouble(msLoopTime.getText());\n\t\t\t\t\t\tmujoco.setTimestep(loopTiming / 1000.0);\n\t\t\t\t\t\tmujoco.setIntegratorType(IntegratorType.IMPLICIT);\n\t\t\t\t\t\tmujoco.setCondim(4);\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tmujoco.generateNewModel(CSGDatabase.getInstance());\n\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\t\t\te.printStackTrace(System.out);\n\t\t\t\t\t\t\tclose();\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t} // generate model before start counting time\n\t\t\t\t\t\tphysicsThread = new Thread() {\n\t\t\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\t\t\tblockingUpdateObjects();\n\t\t\t\t\t\t\t\tBowlerStudioController.clearCSG();\n\t\t\t\t\t\t\t\tBowlerStudioController.clearUserNodes();\n\t\t\t\t\t\t\t\tBowlerStudioController.addObject(mujoco.getAllCSG(), null);\n\t\t\t\t\t\t\t\tConfigurationDatabase.save();\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tlong timeSinceLastPrint = 0;\n\t\t\t\t\t\t\t\t\twhile (isRun()) {\n\t\t\t\t\t\t\t\t\t\twhile (isPause() && isTakestep() == false) {\n\t\t\t\t\t\t\t\t\t\t\tThreadUtil.wait(0, 100);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tsetTakestep(false);\n\t\t\t\t\t\t\t\t\t\tlong now;\n\t\t\t\t\t\t\t\t\t\tif ((now = mujoco.stepAndWait()) > (3 * mujoco.getTimestepMilliSeconds())) {\n\t\t\t\t\t\t\t\t\t\t\tif (System.currentTimeMillis() - timeSinceLastPrint > 500) {\n\t\t\t\t\t\t\t\t\t\t\t\ttimeSinceLastPrint = System.currentTimeMillis();\n\t\t\t\t\t\t\t\t\t\t\t\tcom.neuronrobotics.sdk.common.Log\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t.error(\"MuJoCo Real time broken, expected \"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t+ mujoco.getTimestepMilliSeconds() + \" took: \" + now);\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\t\t\te.printStackTrace();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tclose();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t};\n\t\t\t\t\t\tphysicsThread.start();\n\t\t\t\t\t}\n\n\t\t\t\t\tprivate void close() {\n\t\t\t\t\t\tif (mujoco != null)\n\t\t\t\t\t\t\tmujoco.close();\n\t\t\t\t\t\tmujoco = null;\n\t\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\t\tstopPhysics();\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}.start();\n\n\t\t\t}\n\t\t\tsetRun(!isRun());\n\t\t});\n\n\t}\n\n\tprivate void validateInput() {\n\t\tnew Thread(() -> {\n\t\t\tvalidateInput(gitMoving, filesMoving, \"gitMoving\", \"movingObjects\");\n\t\t\tvalidateInput(gitStatic, filesStatic, \"gitStatic\", \"staticObjects\");\n\t\t}).start();\n\t}\n\n\tprivate void validateInput(TextField text, ComboBox<String> box, String key, String key2) {\n\t\tBowlerStudio.runLater(() -> box.getItems().clear());\n\t\tBowlerStudio.runLater(() -> box.setDisable(true));\n\t\tString text2 = text.getText();\n\t\tConfigurationDatabase.setObject(\"PhysicsWidget\", key, text2);\n\t\tif (!Vitamins.isGitURL(text2)) {\n\t\t\treturn;\n\t\t}\n\t\tBowlerStudio.runLater(() -> box.setDisable(false));\n\n\t\ttry {\n\t\t\tArrayList<String> files = ScriptingEngine.filesInGit(text2);\n\n\t\t\tfor (String name : files) {\n\t\t\t\tBowlerStudio.runLater(() -> box.getItems().add(name));\n\t\t\t}\n\t\t\tString file = ConfigurationDatabase.getObject(\"PhysicsWidget\", key2, \"\").toString();\n\t\t\tif (file.length() > 0) {\n\t\t\t\tboolean hasFile = false;\n\t\t\t\tfor (String s : files)\n\t\t\t\t\tif (s.contentEquals(file))\n\t\t\t\t\t\thasFile = true;\n\t\t\t\tif (!hasFile)\n\t\t\t\t\treturn;\n\t\t\t\tThread.sleep(16 * files.size());\n\t\t\t\tBowlerStudio.runLater(() -> box.getSelectionModel().select(file));\n\t\t\t\tupdateObjects();\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tBowlerStudio.runLater(() -> box.setDisable(true));\n\t\t\te.printStackTrace();\n\t\t}\n\n\t}\n\n\tprivate void updateObjects() {\n\t\tnew Thread(() -> {\n\t\t\tblockingUpdateObjects();\n\n\t\t}).start();\n\n\t}\n\n\tprivate void blockingUpdateObjects() {\n\t\tif (movingObjects != null) {\n\t\t\tfor (CSG c : movingObjects) {\n\t\t\t\tBowlerStudioController.removeObject(c);\n\t\t\t}\n\t\t}\n\t\tif (staticObjects != null) {\n\t\t\tfor (CSG c : staticObjects) {\n\t\t\t\tBowlerStudioController.removeObject(c);\n\t\t\t}\n\t\t}\n\t\ttry {\n\t\t\tmovingObjects = null;\n\t\t\tString selectedItem = filesMoving.getSelectionModel().getSelectedItem();\n\t\t\tmovingObjects = loadObjects(gitMoving.getText(), selectedItem);\n\t\t\tConfigurationDatabase.setObject(\"PhysicsWidget\", \"movingObjects\", selectedItem);\n\n\t\t} catch (Exception e) {\n\t\t\t// Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t}\n\t\ttry {\n\t\t\tstaticObjects = null;\n\t\t\tString selectedItem = filesStatic.getSelectionModel().getSelectedItem();\n\t\t\tstaticObjects = loadObjects(gitStatic.getText(), selectedItem);\n\t\t\tConfigurationDatabase.setObject(\"PhysicsWidget\", \"staticObjects\", selectedItem);\n\n\t\t} catch (Exception e) {\n\t\t\t// Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t}\n\t\tif (movingObjects != null) {\n\t\t\tfor (CSG c : movingObjects) {\n\t\t\t\tBowlerStudioController.addObject(c, null);\n\t\t\t}\n\t\t}\n\t\tif (staticObjects != null) {\n\t\t\tfor (CSG c : staticObjects) {\n\t\t\t\tBowlerStudioController.addObject(c, null);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate ArrayList<CSG> loadObjects(String url, String selectedItem) throws Exception {\n\t\tArrayList<CSG> ret = new ArrayList<CSG>();\n\t\tif (url == null || selectedItem == null)\n\t\t\treturn ret;\n\t\tScriptingEngine.cloneRepo(url, null);\n\t\tScriptingEngine.pull(url);\n\t\tObject o = ScriptingEngine.gitScriptRun(CSGDatabase.getInstance(), url, selectedItem);\n\t\tload(o, ret);\n\t\treturn ret;\n\t}\n\n\tprivate void load(Object o, ArrayList<CSG> storage) {\n\t\tif (CSG.class.isInstance(o))\n\t\t\tstorage.add((CSG) o);\n\t\tif (ArrayList.class.isInstance(o)) {\n\t\t\tArrayList l = (ArrayList) o;\n\t\t\tfor (int i = 0; i < l.size(); i++) {\n\t\t\t\tload(l.get(i), storage);\n\t\t\t}\n\t\t}\n\t\tif (Map.class.isInstance(o)) {\n\t\t\tMap l = (Map) o;\n\t\t\tfor (Object x : l.keySet()) {\n\t\t\t\tload(l.get(x), storage);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void stopPhysics() {\n\t\tBowlerStudio.setToRunButton(runstop);\n\t\tif (physicsThread != null)\n\t\t\tphysicsThread.interrupt();\n\n\t\tmsLoopTime.setDisable(false);\n\t\tpauseresume.setDisable(true);\n\t\tif (oldParts != null) {\n\t\t\tArrayList<CSG> oldp = new ArrayList<>();\n\t\t\tfor (CSG c : oldParts) {\n\t\t\t\toldp.add(c);\n\t\t\t}\n\t\t\tBowlerStudioController.setCsg(oldp);\n\t\t\toldParts = null;\n\t\t}\n\t\tbase.getImu().removevirtualListeners(this);\n\t}\n\n\tpublic boolean isTakestep() {\n\t\treturn takestep;\n\t}\n\n\tpublic void setTakestep(boolean takestep) {\n\t\tthis.takestep = takestep;\n\t}\n\n\tpublic boolean isPause() {\n\t\treturn pause;\n\t}\n\n\tpublic void setPause(boolean pause) {\n\t\tthis.pause = pause;\n\t}\n\n\tpublic boolean isRun() {\n\t\treturn run;\n\t}\n\n\tpublic void setRun(boolean run) {\n\t\tthis.run = run;\n\t}\n\n\t@Override\n\tpublic void onIMUUpdate(IMUUpdate arg0) {\n\t\t// com.neuronrobotics.sdk.common.Log.error(\"X = \"+arg0.getxAcceleration()+\n\t\t// \" Y = \"+arg0.getyAcceleration()+\n\t\t// \" Z = \"+arg0.getzAcceleration()+\n\t\t// \" rX = \"+arg0.getRotxAcceleration()+\n\t\t// \" rY = \"+arg0.getRotyAcceleration()+\n\t\t// \" rZ = \"+arg0.getRotzAcceleration()\n\t\t//\n\t\t// );\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/creature/TransformWidget.java",
    "content": "package com.neuronrobotics.bowlerstudio.creature;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.ArrayList;\n\nimport org.eclipse.jgit.api.errors.GitAPIException;\nimport org.eclipse.jgit.api.errors.InvalidRemoteException;\nimport org.eclipse.jgit.api.errors.TransportException;\n\nimport com.neuronrobotics.bowlerstudio.BowlerStudio;\nimport com.neuronrobotics.bowlerstudio.assets.AssetFactory;\nimport com.neuronrobotics.bowlerstudio.assets.ConfigurationDatabase;\nimport com.neuronrobotics.bowlerstudio.assets.FontSizeManager;\nimport com.neuronrobotics.bowlerstudio.scripting.ScriptingEngine;\nimport com.neuronrobotics.sdk.addons.kinematics.math.RotationNR;\nimport com.neuronrobotics.sdk.addons.kinematics.math.TransformNR;\n\nimport eu.mihosoft.vrl.v3d.JavaFXInitializer;\nimport javafx.event.ActionEvent;\nimport javafx.event.EventHandler;\nimport javafx.scene.layout.ColumnConstraints;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.TextField;\nimport javafx.scene.control.Tooltip;\nimport javafx.scene.image.ImageView;\nimport javafx.stage.Stage;\nimport javafx.scene.control.Label;\n\npublic class TransformWidget extends GridPane\n\t\timplements\n\t\t\tIOnEngineeringUnitsChange,\n\t\t\tEventHandler<ActionEvent>,\n\t\t\tIAmControlled {\n\n\tpublic IOnTransformChange onChange;\n\t// EngineeringUnitsSliderWidget rw;\n\tpublic EngineeringUnitsSliderWidget tilt;\n\tpublic EngineeringUnitsSliderWidget elevation;\n\tpublic EngineeringUnitsSliderWidget azimuth;\n\tpublic EngineeringUnitsSliderWidget tx;\n\tpublic EngineeringUnitsSliderWidget ty;\n\tpublic EngineeringUnitsSliderWidget tz;\n\t// public TextField tx;\n\t// public TextField ty;\n\t// public TextField tz;\n\tpublic TransformNR initialState;\n\tpublic RotationNR storeRotation;\n\tpublic double linearIncrement = 1;\n\tpublic double rotationIncrement = 45;\n\tpublic Button game = new Button(\"Jog With Game Controller\", AssetFactory.loadIcon(\"Add-Game-Controller.png\"));\n\tpublic Thread scriptRunner = null;\n\tprivate String title;\n\tprivate TransformWidget self;\n\tprivate Label mode = new Label(\"\");\n\tprivate TextField lin;\n\tprivate TextField rot;\n\n\tpublic TransformWidget(String title, TransformNR is, IOnTransformChange onChange) {\n\t\tTransformWidget c = this;\n\t\tc.title = title;\n\t\tself = c;\n\t\tinitialState = is.copy();\n\t\tc.onChange = (onChange);\n\t\t// tx = new TextField(CreatureLab.getFormatted(initialState.getX()));\n\t\t// ty = new TextField(CreatureLab.getFormatted(initialState.getY()));\n\t\t// tz = new TextField(CreatureLab.getFormatted(initialState.getZ()));\n\t\t// tx.setOnAction(this);\n\t\t// ty.setOnAction(this);\n\t\t// tz.setOnAction(this);\n\t\tdouble scale = (double) (FontSizeManager.getDefaultSize()) / 12.0;\n\n\t\tdouble width = 200 * scale;\n\t\ttx = new EngineeringUnitsSliderWidget(c, initialState.getX(), width, \"mm\");\n\t\tty = new EngineeringUnitsSliderWidget(c, initialState.getY(), width, \"mm\");\n\t\ttz = new EngineeringUnitsSliderWidget(c, initialState.getZ(), width, \"mm\");\n\n\t\tstoreRotation = initialState.getRotation();\n\t\tdouble t = 0;\n\t\ttry {\n\t\t\tt = Math.toDegrees(storeRotation.getRotationTilt());\n\t\t} catch (Exception ex) {\n\t\t\tex.printStackTrace();\n\t\t}\n\t\tdouble e = 0;\n\t\ttry {\n\t\t\te = Math.toDegrees(storeRotation.getRotationElevationRadians());\n\t\t} catch (Exception ex) {\n\t\t\tex.printStackTrace();\n\t\t}\n\t\tdouble a = 0;\n\t\ttry {\n\t\t\ta = Math.toDegrees(storeRotation.getRotationAzimuthRadians());\n\t\t} catch (Exception ex) {\n\t\t\tex.printStackTrace();\n\t\t}\n\t\ttilt = new EngineeringUnitsSliderWidget(new IOnEngineeringUnitsChange() {\n\n\t\t\t@Override\n\t\t\tpublic void onSliderMoving(EngineeringUnitsSliderWidget source, double newAngleDegrees) {\n\t\t\t\tinitialState.setTiltDegrees(newAngleDegrees);\n\t\t\t\tonChange.onTransformChaging(getCurrent());\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onSliderDoneMoving(EngineeringUnitsSliderWidget source, double newAngleDegrees) {\n\t\t\t\tinitialState.setTiltDegrees(newAngleDegrees);\n\t\t\t\tonChange.onTransformFinished(getCurrent());\n\n\t\t\t}\n\t\t}, -179.99, 179.99, t, width, \"degrees\");\n\t\televation = new EngineeringUnitsSliderWidget(new IOnEngineeringUnitsChange() {\n\n\t\t\t@Override\n\t\t\tpublic void onSliderMoving(EngineeringUnitsSliderWidget source, double newAngleDegrees) {\n\t\t\t\tinitialState.setElevationDegrees(newAngleDegrees);\n\t\t\t\tonChange.onTransformChaging(getCurrent());\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onSliderDoneMoving(EngineeringUnitsSliderWidget source, double newAngleDegrees) {\n\t\t\t\tinitialState.setElevationDegrees(newAngleDegrees);\n\t\t\t\tonChange.onTransformFinished(getCurrent());\n\t\t\t}\n\t\t}, -89.99, 89.99, e, width, \"degrees\");\n\t\tazimuth = new EngineeringUnitsSliderWidget(new IOnEngineeringUnitsChange() {\n\n\t\t\t@Override\n\t\t\tpublic void onSliderMoving(EngineeringUnitsSliderWidget source, double newAngleDegrees) {\n\t\t\t\tinitialState.setAzimuthDegrees(newAngleDegrees);\n\t\t\t\tonChange.onTransformChaging(getCurrent());\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onSliderDoneMoving(EngineeringUnitsSliderWidget source, double newAngleDegrees) {\n\t\t\t\tinitialState.setAzimuthDegrees(newAngleDegrees);\n\t\t\t\tonChange.onTransformFinished(getCurrent());\n\t\t\t}\n\t\t}, -179.99, 179.99, a, width, \"degrees\");\n\t\ttilt.setAllowResize(false);\n\t\televation.setAllowResize(false);\n\t\tazimuth.setAllowResize(false);\n\t\tgetColumnConstraints().add(new ColumnConstraints(60 * scale)); // translate text\n\t\tgetColumnConstraints().add(new ColumnConstraints(width)); // translate values\n\t\tgetColumnConstraints().add(new ColumnConstraints(60 * scale)); // units\n\t\tgetColumnConstraints().add(new ColumnConstraints(60 * scale)); // rotate text\n\t\tsetHgap(20);// gab between elements\n\n\t\ttx.showSlider(false);\n\t\tty.showSlider(false);\n\t\ttz.showSlider(false);\n\t\tGameControlThreadManager.setCurrentController(c);\n\t\tGameControlThreadManager.reset();\n\t\tgame.setOnAction(event -> {\n\t\t\tnew Thread() {\n\t\t\t\tpublic void run() {\n\t\t\t\t\tGameControlThreadManager.setCurrentController(c);\n\t\t\t\t\tGameControlThreadManager.startStopAction();\n\t\t\t\t}\n\t\t\t}.start();\n\n\t\t});\n\t\tgame.setTooltip(new Tooltip(\n\t\t\t\t\"Connect game controllers and use them jog the item around. Use the joysticks to move. \\nPress X to Translate. \\nPress Y to rotate. \\nPress A to exit\"));\n\n\t\tadd(new Label(title), 1, 0);\n\t\tadd(mode, 1, 1);\n\t\tadd(game, 0, 0);\n\n\t\t// These all seem out of order here, but it is because the\n\t\t// screen is rotating the orenation of this interface from\n\t\t// BowlerStudio3dEngine.getOffsetforvisualization()\n\t\t// X line\n\n\t\tint startIndex = 3;\n\t\tlin = new TextField(linearIncrement + \"\");\n\t\trot = new TextField(rotationIncrement + \"\");\n\t\tsetDefaultValues();\n\t\tsetIncrements();\n\t\tlin.setOnAction(ac -> {\n\t\t\ttry {\n\t\t\t\tlinearIncrement = Double.parseDouble(lin.getText());\n\t\t\t\tsetIncrements();\n\t\t\t} catch (NumberFormatException ex) {\n\n\t\t\t}\n\t\t});\n\t\trot.setOnAction(ac -> {\n\t\t\ttry {\n\t\t\t\trotationIncrement = Double.parseDouble(rot.getText());\n\t\t\t\tsetIncrements();\n\t\t\t} catch (NumberFormatException ex) {\n\n\t\t\t}\n\t\t});\n\n\t\tadd(new Label(\"Linear \"), 0, startIndex - 1);\n\t\tadd(new Label(\"Rotation \"), 0, startIndex);\n\n\t\tadd(lin, 1, startIndex - 1);\n\t\tadd(rot, 1, startIndex);\n\t\tadd(new Label(\"(mm)\"), 2, startIndex - 1);\n\t\tadd(new Label(\"(degrees)\"), 2, startIndex);\n\n\t\tadd(new Label(\"X\"), 0, 1 + startIndex);\n\t\tadd(tx, 1, 1 + startIndex);\n\n\t\t// Y line\n\t\tadd(new Label(\"Y\"), 0, 2 + startIndex);\n\t\tadd(ty, 1, 2 + startIndex);\n\n\t\t// Z line\n\t\tadd(new Label(\"Z\"), 0, 3 + startIndex);\n\t\tadd(tz, 1, 3 + startIndex);\n\t\tadd(new Label(\"Tilt\"), 0, 4 + startIndex);\n\t\tadd(tilt, 1, 4 + startIndex);\n\n\t\tadd(new Label(\"Elevation\"), 0, 5 + startIndex);\n\t\tadd(elevation, 1, 5 + startIndex);\n\n\t\tadd(new Label(\"Azimuth\"), 0, 6 + startIndex);\n\t\tadd(azimuth, 1, 6 + startIndex);\n\t\t// game\n\n\t\tupdatePose(is);\n\t}\n\n\tprivate void setDefaultValues() {\n\t\tlinearIncrement = Double\n\t\t\t\t.parseDouble(ConfigurationDatabase.getObject(\"TransformWidget\", \"linear\", linearIncrement).toString());\n\t\trotationIncrement = Double\n\t\t\t\t.parseDouble(ConfigurationDatabase.getObject(\"TransformWidget\", \"rot\", rotationIncrement).toString());\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tlin.setText(linearIncrement + \"\");\n\t\t\trot.setText(rotationIncrement + \"\");\n\t\t});\n\t}\n\n\tpublic String toString() {\n\t\treturn title + \" \" + initialState.toSimpleString();\n\t}\n\n\tpublic void setMode(String m) {\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tmode.setText(m + \" Mode\");\n\t\t});\n\t}\n\n\tpublic void setIncrements() {\n\t\tConfigurationDatabase.setObject(\"TransformWidget\", \"linear\", linearIncrement);\n\t\tConfigurationDatabase.setObject(\"TransformWidget\", \"rot\", rotationIncrement);\n\n\t\ttx.setJogIncrement(linearIncrement);\n\t\tty.setJogIncrement(linearIncrement);\n\t\ttz.setJogIncrement(linearIncrement);\n\t\ttilt.setJogIncrement(rotationIncrement);\n\t\televation.setJogIncrement(rotationIncrement);\n\t\tazimuth.setJogIncrement(rotationIncrement);\n\t}\n\n\tpublic TransformNR getCurrent() {\n\t\tTransformNR tmp = new TransformNR(tx.getValue(), ty.getValue(), tz.getValue(), initialState.getRotation());\n\n\t\treturn tmp;\n\t}\n\n\t@Override\n\tpublic void onSliderMoving(EngineeringUnitsSliderWidget source, double newAngleDegrees) {\n\t\tgetOnChange().onTransformChaging(getCurrent());\n\t}\n\n\t@Override\n\tpublic void onSliderDoneMoving(EngineeringUnitsSliderWidget source, double newAngleDegrees) {\n\t\thandle(null);\n\t}\n\n\t@Override\n\tpublic void handle(ActionEvent event) {\n\t\tgetOnChange().onTransformChaging(getCurrent());\n\t\tgetOnChange().onTransformFinished(getCurrent());\n\t}\n\n\tpublic void updatePose(TransformNR p) {\n\t\tinitialState = p.copy();\n\n\t\ttx.setValue(initialState.getX());\n\t\tty.setValue(initialState.getY());\n\t\ttz.setValue(initialState.getZ());\n\n\t\tRotationNR rot = initialState.getRotation();\n\t\tdouble t = 0;\n\t\ttry {\n\t\t\tt = Math.toDegrees(rot.getRotationTilt());\n\t\t} catch (Exception ex) {\n\t\t\tex.printStackTrace();\n\t\t}\n\t\tdouble e = 0;\n\t\ttry {\n\t\t\te = Math.toDegrees(rot.getRotationElevationRadians());\n\t\t} catch (Exception ex) {\n\t\t\tex.printStackTrace();\n\t\t}\n\t\tdouble a = 0;\n\t\ttry {\n\t\t\ta = Math.toDegrees(rot.getRotationAzimuthRadians());\n\t\t} catch (Exception ex) {\n\t\t\tex.printStackTrace();\n\t\t}\n\t\tdouble tiltVar = t;\n\t\tdouble eVar = e;\n\t\tdouble aVar = a;\n\t\ttilt.setValue(tiltVar);\n\t\televation.setValue(eVar);\n\t\tazimuth.setValue(aVar);\n\t\t// Set the rotation after setting the UI so the read will load the rotation in\n\t\t// its pure form\n\t\tsetDefaultValues();\n\t}\n\n\tpublic static void main(String[] args) {\n\t\tJavaFXInitializer.go();\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tStage s = new Stage();\n\t\t\t// new Thread(() -> {\n\t\t\tTransformWidgetTest controller = new TransformWidgetTest();\n\n\t\t\ttry {\n\t\t\t\tcontroller.start(s);\n\t\t\t} catch (Exception e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t\t// }).start();\n\t\t});\n\t}\n\n\t/**\n\t * @return the onChange\n\t */\n\tpublic IOnTransformChange getOnChange() {\n\t\treturn onChange;\n\t}\n\n\t/**\n\t * @param onChange\n\t *            the onChange to set\n\t */\n\tpublic void setOnChange(IOnTransformChange onChange, TransformNR initial) {\n\t\tthis.onChange = onChange;\n\t\tinitialState = initial;\n\t\tupdatePose(initial);\n\t}\n\n\t@Override\n\tpublic File getScriptFile() {\n\t\t// Auto-generated method stub\n\t\ttry {\n\t\t\treturn ScriptingEngine.fileFromGit(\"https://github.com/OperationSmallKat/Katapult.git\", \"jogWidget.groovy\");\n\t\t} catch (InvalidRemoteException e) {\n\t\t\t// Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t} catch (TransportException e) {\n\t\t\t// Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t} catch (GitAPIException e) {\n\t\t\t// Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t} catch (IOException e) {\n\t\t\t// Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic ArrayList<Object> getArguments() {\n\t\tArrayList<Object> args = new ArrayList<>();\n\t\targs.add(self);\n\t\treturn args;\n\t}\n\n\t@Override\n\tpublic ImageView getRunAsset() {\n\t\treturn AssetFactory.loadIcon(\"Add-Game-Controller.png\");\n\t}\n\n\t@Override\n\tpublic Button getRunStopButton() {\n\t\treturn game;\n\t}\n\n\t@Override\n\tpublic String getButtonRunText() {\n\t\treturn \"Run Game Controller\";\n\t}\n\n\tpublic String getName() {\n\t\treturn title;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/creature/TransformWidgetTest.java",
    "content": "package com.neuronrobotics.bowlerstudio.creature;\n\nimport java.io.File;\n\nimport com.neuronrobotics.bowlerstudio.assets.AssetFactory;\nimport com.neuronrobotics.bowlerstudio.assets.FontSizeManager;\nimport com.neuronrobotics.sdk.addons.kinematics.math.TransformNR;\n\nimport javafx.application.Application;\nimport javafx.stage.Stage;\nimport javafx.scene.Scene;\n\npublic class TransformWidgetTest extends Application {\n\tprivate TransformWidget w;\n\n\tpublic TransformWidgetTest() {\n\t\tthis.w = new TransformWidget(\"Test Widget\", new TransformNR(1, 2, 3), new IOnTransformChange() {\n\n\t\t\t@Override\n\t\t\tpublic void onTransformFinished(TransformNR newTrans) {\n\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Finished \" + newTrans.toSimpleString());\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onTransformChaging(TransformNR newTrans) {\n\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Changing \" + newTrans.toSimpleString());\n\t\t\t}\n\t\t});\n\n\t}\n\n\t@Override\n\tpublic void start(Stage primaryStage) throws Exception {\n\t\tFile layoutFile = AssetFactory.loadFile(\"layout/default.css\");\n\t\tString nwfile = layoutFile.toURI().toString().replace(\"file:/\", \"file:///\");\n\t\tScene scene = new Scene(w);\n\n\t\tscene.getStylesheets().clear();\n\t\tscene.getStylesheets().add(nwfile);\n\t\tcom.neuronrobotics.sdk.common.Log.error(\"Loading CSS from \" + nwfile);\n\t\tdouble scale = (double) (FontSizeManager.getDefaultSize()) / 12.0;\n\n\t\tprimaryStage.setScene(scene);\n\t\t// primaryStage.setWidth(668*scale);\n\t\t// primaryStage.setHeight(664*scale);\n\t\tprimaryStage.setTitle(\"Test Application\");\n\t\tprimaryStage.show();\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/creature/VitaminWidgetTest.java",
    "content": "package com.neuronrobotics.bowlerstudio.creature;\n\nimport java.io.File;\n\nimport com.neuronrobotics.bowlerstudio.BowlerStudio;\nimport com.neuronrobotics.bowlerstudio.assets.AssetFactory;\nimport com.neuronrobotics.bowlerstudio.assets.FontSizeManager;\nimport com.neuronrobotics.bowlerstudio.scripting.ScriptingEngine;\nimport com.neuronrobotics.sdk.addons.kinematics.MobileBase;\nimport com.neuronrobotics.sdk.addons.kinematics.math.TransformNR;\n\nimport eu.mihosoft.vrl.v3d.JavaFXInitializer;\nimport eu.mihosoft.vrl.v3d.parametrics.CSGDatabase;\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.stage.Stage;\nimport javafx.scene.Parent;\nimport javafx.fxml.FXMLLoader;\n\npublic class VitaminWidgetTest extends Application {\n\tprivate VitatminWidget tw;\n\n\t@Override\n\tpublic void start(Stage primaryStage) throws Exception {\n\t\tFXMLLoader loader = AssetFactory.loadLayout(\"layout/AddRemoveVitamins.fxml\");\n\t\tloader.setClassLoader(VitatminWidget.class.getClassLoader());\n\t\tParent w = loader.load();\n\n\t\ttw = loader.getController();\n\n\t\tFile layoutFile = AssetFactory.loadFile(\"layout/default.css\");\n\t\tString nwfile = layoutFile.toURI().toString().replace(\"file:/\", \"file:///\");\n\t\tScene scene = new Scene(w);\n\n\t\tscene.getStylesheets().clear();\n\t\tscene.getStylesheets().add(nwfile);\n\t\tcom.neuronrobotics.sdk.common.Log.error(\"Loading CSS from \" + nwfile);\n\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\tint tmp = fontNum - 10;\n\t\t\tif (tmp < 12)\n\t\t\t\ttmp = 12;\n\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Setting font size to \" + fontNum);\n\t\t\tw.setStyle(\"-fx-font-size: \" + tmp + \"pt\");\n\t\t});\n\t\tprimaryStage.setOnCloseRequest(arg0 -> {\n\t\t\tSystem.exit(0);\n\t\t});\n\t\tprimaryStage.setScene(scene);\n\t\tprimaryStage.setWidth(600);\n\t\tprimaryStage.setHeight(777);\n\t\tprimaryStage.setTitle(\"Test Application\");\n\t\tprimaryStage.show();\n\t}\n\n\tpublic static void main(String[] args) {\n\t\tJavaFXInitializer.go();\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tStage s = new Stage();\n\t\t\t//\n\t\t\tVitaminWidgetTest controller = new VitaminWidgetTest();\n\t\t\ttry {\n\t\t\t\tcontroller.start(s);\n\t\t\t} catch (Exception e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t\tVitatminWidget tw = controller.getTw();\n\t\t\tnew Thread(() -> {\n\t\t\t\ttry {\n\t\t\t\t\tMobileBase mb = (MobileBase) ScriptingEngine.gitScriptRun(CSGDatabase.getInstance(),\n\t\t\t\t\t\t\t\"https://github.com/NeuronRobotics/NASACurisoity.git\", \"NASA_Curiosity.xml\");\n\t\t\t\t\ttw.setVitaminProvider(mb.getAllDHChains().get(0).getLinkConfiguration(0), selected -> {\n\t\t\t\t\t\treturn mb.forwardOffset(new TransformNR());\n\t\t\t\t\t});\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\n\t\t\t}).start();\n\t\t});\n\t}\n\n\t/**\n\t * @return the tw\n\t */\n\tpublic VitatminWidget getTw() {\n\t\treturn tw;\n\t}\n\n\t/**\n\t * @param tw\n\t *            the tw to set\n\t */\n\tpublic void setTw(VitatminWidget tw) {\n\t\tif (tw == null)\n\t\t\tthrow new RuntimeException();\n\t\tthis.tw = tw;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/creature/VitatminWidget.java",
    "content": "/**\n * Sample Skeleton for 'AddRemoveVitamins.fxml' Controller Class\n */\n\npackage com.neuronrobotics.bowlerstudio.creature;\n\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.ResourceBundle;\nimport java.util.stream.Collectors;\n\nimport com.neuronrobotics.bowlerstudio.BowlerStudioController;\nimport com.neuronrobotics.bowlerstudio.assets.AssetFactory;\nimport com.neuronrobotics.bowlerstudio.assets.FontSizeManager;\nimport com.neuronrobotics.bowlerstudio.scripting.ScriptingEngine;\nimport com.neuronrobotics.bowlerstudio.vitamins.Vitamins;\nimport com.neuronrobotics.sdk.addons.kinematics.IVitaminHolder;\nimport com.neuronrobotics.sdk.addons.kinematics.VitaminFrame;\nimport com.neuronrobotics.sdk.addons.kinematics.VitaminLocation;\nimport com.neuronrobotics.sdk.addons.kinematics.math.TransformNR;\n\nimport eu.mihosoft.vrl.v3d.CSG;\nimport eu.mihosoft.vrl.v3d.parametrics.CSGDatabase;\nimport javafx.event.ActionEvent;\nimport javafx.fxml.FXML;\nimport javafx.scene.control.*;\nimport javafx.scene.layout.*;\nimport javafx.scene.transform.Affine;\n\npublic class VitatminWidget implements IOnTransformChange {\n\n\tprivate String selectedType = null;\n\tprivate String sizeSelected = null;\n\tprivate TransformWidget tf;\n\n\t@FXML // ResourceBundle that was given to the FXMLLoader\n\tprivate ResourceBundle resources;\n\n\t@FXML // URL location of the FXML file that was given to the FXMLLoader\n\tprivate URL location;\n\n\t@FXML // fx:id=\"add\"\n\tprivate Button add; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"name\"\n\tprivate TextField name; // Value injected by FXMLLoader\n\t@FXML // fx:id=\"name\"\n\tprivate TextField scriptSource; // Value injected by FXMLLoader\n\t@FXML // fx:id=\"name\"\n\tprivate CheckBox isScript; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"type\"\n\tprivate ComboBox<String> type; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"size\"\n\tprivate ComboBox<String> size; // Value injected by FXMLLoader\n\t@FXML // fx:id=\"listOfItems\"\n\tprivate ListView<GridPane> listOfItems; // Value injected by FXMLLoader\n\t@FXML\n\tprivate ComboBox<VitaminFrame> frameType;\n\t@FXML // fx:id=\"transformPanel\"\n\tprivate AnchorPane transformPanel; // Value injected by FXMLLoader\n\n\tprivate IVitaminHolder holder;\n\tprivate HashMap<GridPane, VitaminLocation> locationMap = new HashMap<>();\n\tprivate VitaminLocation selectedVitamin;\n\tprivate ITransformProvider currentTipProvider;\n\tprivate Affine lastLinkAffine = null;\n\tprivate Affine manipulator = null;\n\tprivate TransformNR offset = new TransformNR();\n\n\t@FXML\n\tvoid onAdd(ActionEvent event) {\n\t\tVitaminLocation newVit = new VitaminLocation(isScript.isSelected(), name.getText(), selectedType, sizeSelected,\n\t\t\t\ttf.getCurrent());\n\t\tVitaminFrame value = frameType.getValue();\n\t\tif (value == null)\n\t\t\tvalue = VitaminFrame.DefaultFrame;\n\t\tnewVit.setFrame(value);\n\t\tholder.addVitamin(newVit);\n\t\tadd(newVit);\n\t\tvalidateInput();\n\t\tCSG newDisplay = getCSG(newVit);\n\t\tif (newDisplay != null) {\n\t\t\tBowlerStudioController.addCsg(newDisplay);\n\t\t}\n\n\t}\n\n\tprivate CSG getCSG(VitaminLocation newVit) {\n\t\tMobileBaseCadManager manager = MobileBaseCadManager.searchForCadManager(holder);\n\t\tCSG newDisplay = null;\n\t\tswitch (newVit.getFrame()) {\n\t\t\tcase DefaultFrame :\n\t\t\t\tnewDisplay = manager.getVitaminDisplay(CSGDatabase.getInstance(), newVit, manipulator,\n\t\t\t\t\t\tnew TransformNR());\n\t\t\t\tbreak;\n\t\t\tcase LinkOrigin :\n\t\t\t\tnewDisplay = manager.getVitaminDisplay(CSGDatabase.getInstance(), newVit, manipulator, offset);\n\t\t\t\tbreak;\n\t\t\tcase previousLinkTip :\n\t\t\t\tnewDisplay = manager.getVitaminDisplay(CSGDatabase.getInstance(), newVit, lastLinkAffine,\n\t\t\t\t\t\tnew TransformNR());\n\t\t\t\tbreak;\n\t\t}\n\t\treturn newDisplay;\n\t}\n\n\tprivate void add(VitaminLocation newVit) {\n\t\tGridPane box = new GridPane();\n\t\tdouble scale = (double) (FontSizeManager.getDefaultSize()) / 12.0;\n\n\t\tbox.getColumnConstraints().add(new ColumnConstraints(25 * scale)); // translate text\n\t\tbox.getColumnConstraints().add(new ColumnConstraints(170 * scale)); // translate values\n\t\tbox.getColumnConstraints().add(new ColumnConstraints(170 * scale)); // units\n\t\tbox.getColumnConstraints().add(new ColumnConstraints(170 * scale)); // rotate text\n\t\tbox.setHgap(20 * scale);// gab between elements\n\t\tbox.setVgap(10 * scale);// gab between elements\n\t\tlocationMap.put(box, newVit);\n\t\tButton remove = new Button();\n\t\tremove.setGraphic(AssetFactory.loadIcon(\"Clear-Screen.png\"));\n\t\tremove.setOnAction(action -> {\n\t\t\tlistOfItems.getSelectionModel().clearSelection();\n\t\t\tlistOfItems.getItems().remove(box);\n\t\t\ttransformPanel.setDisable(true);\n\t\t\tframeType.setDisable(true);\n\t\t\tholder.removeVitamin(newVit);\n\t\t\tvalidateInput();\n\t\t\tlocationMap.remove(box);\n\t\t\tCSG part = getCSG(newVit);\n\t\t\tif (part != null)\n\t\t\t\tBowlerStudioController.removeObject(part);\n\t\t});\n\t\tbox.add(remove, 0, 0);\n\t\tbox.add(new Label(newVit.getName()), 1, 0);\n\t\tbox.add(new Label(newVit.getType()), 2, 0);\n\t\tbox.add(new Label(newVit.getSize()), 3, 0);\n\n\t\tlistOfItems.getItems().add(box);\n\t}\n\n\tvoid validateURL() {\n\t\tsize.setDisable(true);\n\t\tString text2 = scriptSource.getText();\n\t\tif (!Vitamins.isGitURL(text2)) {\n\t\t\treturn;\n\t\t}\n\t\tsize.setDisable(false);\n\t\tsize.getItems().clear();\n\t\tsizeSelected = null;\n\t\tselectedType = text2;\n\t\ttry {\n\t\t\tArrayList<String> files = ScriptingEngine.filesInGit(selectedType);\n\t\t\tfor (String s : files) {\n\t\t\t\tsize.getItems().add(s);\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\t// Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t}\n\n\t}\n\n\tvoid validateInput() {\n\t\tadd.setDisable(true);\n\t\tString nameTmp = name.getText();\n\t\t// com.neuronrobotics.sdk.common.Log.error(\"Validating \" + nameTmp);\n\t\tif (nameTmp.length() == 0)\n\t\t\treturn;\n\t\tnameTmp = nameTmp.trim();\n\t\tif (selectedType == null)\n\t\t\treturn;\n\t\tif (sizeSelected == null)\n\t\t\treturn;\n\t\tfor (VitaminLocation l : holder.getVitamins()) {\n\t\t\tString name2 = l.getName();\n\t\t\tif (name2.contentEquals(nameTmp))\n\t\t\t\treturn;\n\t\t\tcom.neuronrobotics.sdk.common.Log.error(nameTmp + \" is not \" + name2);\n\t\t}\n\t\tadd.setDisable(false);\n\t}\n\n\t@FXML // This method is called by the FXMLLoader when initialization is complete\n\tvoid initialize() {\n\t\tassert add != null : \"fx:id=\\\"add\\\" was not injected: check your FXML file 'AddRemoveVitamins.fxml'.\";\n\t\tassert name != null : \"fx:id=\\\"name\\\" was not injected: check your FXML file 'AddRemoveVitamins.fxml'.\";\n\t\tassert type != null : \"fx:id=\\\"type\\\" was not injected: check your FXML file 'AddRemoveVitamins.fxml'.\";\n\t\tassert size != null : \"fx:id=\\\"size\\\" was not injected: check your FXML file 'AddRemoveVitamins.fxml'.\";\n\t\tpopulateType();\n\t\ttype.setOnAction(action -> {\n\t\t\tadd.setDisable(true);\n\t\t\tsize.getItems().clear();\n\t\t\tselectedType = type.getSelectionModel().getSelectedItem();\n\t\t\tsizeSelected = null;\n\t\t\tList<String> sizes = Vitamins.listVitaminSizes(selectedType).stream().sorted().collect(Collectors.toList());\n\t\t\tfor (String s : sizes) {\n\t\t\t\tsize.getItems().add(s);\n\t\t\t}\n\t\t});\n\t\tsize.setOnAction(action -> {\n\t\t\tadd.setDisable(true);\n\t\t\tsizeSelected = size.getSelectionModel().getSelectedItem();\n\t\t\tvalidateInput();\n\t\t});\n\t\tname.textProperty().addListener((observable, oldValue, newValue) -> {\n\t\t\tvalidateInput();\n\t\t});\n\t\tlistOfItems.getSelectionModel().selectedItemProperty().addListener((ob, old, fresh) -> {\n\t\t\tselectedVitamin = locationMap.get(fresh);\n\t\t\tif (selectedVitamin != null) {\n\t\t\t\tfireVitaminSelectedUpdate();\n\t\t\t}\n\t\t});\n\t\ttf = new TransformWidget(\"Vitamin Location\", new TransformNR(), this);\n\t\ttransformPanel.getChildren().add(tf);\n\t\ttransformPanel.setDisable(true);\n\t\tframeType.setDisable(true);\n\t\tframeType.setOnAction(event -> {\n\t\t\tif (frameType.getValue() != selectedVitamin.getFrame())\n\t\t\t\tselectedVitamin.setFrame(frameType.getValue());\n\t\t});\n\t\tfor (VitaminFrame vf : VitaminFrame.values()) {\n\t\t\tframeType.getItems().add(vf);\n\t\t}\n\t\tisScript.setOnAction(action -> {\n\t\t\tif (isScript.isSelected()) {\n\t\t\t\tsetScriptMode();\n\t\t\t} else {\n\t\t\t\tpopulateType();\n\t\t\t}\n\t\t});\n\t\tscriptSource.textProperty().addListener((observable, oldValue, newValue) -> {\n\t\t\tvalidateURL();\n\t\t});\n\n\t}\n\n\tprivate void setScriptMode() {\n\t\ttype.getItems().clear();\n\t\ttype.setDisable(true);\n\t\tscriptSource.setDisable(false);\n\t}\n\n\tprivate void populateType() {\n\t\tList<String> types = Vitamins.listVitaminTypes().stream().sorted().collect(Collectors.toList());\n\t\tfor (String s : types) {\n\t\t\ttype.getItems().add(s);\n\t\t}\n\t\ttype.setDisable(false);\n\t\tscriptSource.setDisable(true);\n\t\tvalidateURL();\n\t}\n\n\tpublic void fireVitaminSelectedUpdate() {\n\t\tif (selectedVitamin == null)\n\t\t\treturn;\n\t\tcom.neuronrobotics.sdk.common.Log.error(\"Selected \" + selectedVitamin.getName());\n\t\tname.setText(selectedVitamin.getName());\n\t\tisScript.setSelected(selectedVitamin.isScript());\n\t\tif (selectedVitamin.isScript()) {\n\t\t\tscriptSource.setText(selectedVitamin.getType());\n\t\t} else {\n\t\t\ttype.getSelectionModel().select(selectedVitamin.getType());\n\t\t}\n\t\tsize.getSelectionModel().select(selectedVitamin.getSize());\n\n\t\ttf.updatePose(selectedVitamin.getLocation());\n\t\ttransformPanel.setDisable(false);\n\t\tframeType.setDisable(false);\n\t\tframeType.getSelectionModel().select(selectedVitamin.getFrame());\n\t\tMobileBaseCadManager manager = MobileBaseCadManager.searchForCadManager(holder);\n\t\ttry {\n\t\t\tAffine af = manager.getVitaminAffine(selectedVitamin);\n\t\t\tTransformNR poseToMove = currentTipProvider.get(selectedVitamin).copy();\n\t\t\t// poseToMove.setRotation(new RotationNR());\n\t\t\tCSG current = manager.getVitaminDisplay(CSGDatabase.getInstance(), selectedVitamin, af, poseToMove);\n\t\t\tBowlerStudioController.highlightCsg(current);\n\t\t\tBowlerStudioController.targetAndFollow(poseToMove, af);\n\t\t} catch (Exception e) {\n\t\t\t// e.printStackTrace();\n\t\t}\n\t\tGameControlThreadManager.setCurrentController(tf);\n\n\t}\n\n\tpublic void setVitaminProvider(IVitaminHolder h, ITransformProvider currentTipProvider) {\n\t\tthis.holder = h;\n\t\tthis.currentTipProvider = currentTipProvider;\n\t\tfor (VitaminLocation l : h.getVitamins()) {\n\t\t\tadd(l);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onTransformChaging(TransformNR newTrans) {\n\t\t// Auto-generated method stub\n\t\tselectedVitamin.setLocation(newTrans);\n\t}\n\n\t@Override\n\tpublic void onTransformFinished(TransformNR newTrans) {\n\t\tselectedVitamin.setLocation(newTrans);\n\t}\n\n\tpublic Affine getLastLinkAffine() {\n\t\treturn lastLinkAffine;\n\t}\n\n\tpublic void setLastLinkAffine(Affine lastLinkAffine) {\n\t\tthis.lastLinkAffine = lastLinkAffine;\n\t}\n\n\tpublic Affine getManipulator() {\n\t\treturn manipulator;\n\t}\n\n\tpublic void setManipulator(Affine manipulator) {\n\t\tthis.manipulator = manipulator;\n\t}\n\n\tpublic TransformNR getOffset() {\n\t\treturn offset;\n\t}\n\n\tpublic void setOffset(TransformNR offset) {\n\t\tthis.offset = offset;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/scripting/AskToDeleteWidget.java",
    "content": "package com.neuronrobotics.bowlerstudio.scripting;\n\nimport java.util.concurrent.CompletableFuture;\n\nimport com.neuronrobotics.bowlerstudio.assets.FontSizeManager;\n\nimport javafx.application.Platform;\nimport javafx.scene.Node;\nimport javafx.scene.control.Alert;\nimport javafx.scene.control.ButtonType;\nimport javafx.stage.Stage;\n\npublic class AskToDeleteWidget {\n\tpublic static boolean askToDeleteFile(String name) {\n\t\tCompletableFuture<Boolean> future = new CompletableFuture<>();\n\n\t\tPlatform.runLater(() -> {\n\t\t\tAlert alert = new Alert(Alert.AlertType.CONFIRMATION);\n\t\t\talert.setTitle(\"File Exists\");\n\t\t\talert.setHeaderText(name);\n\t\t\talert.setContentText(\"Delete existing and replace?\");\n\n\t\t\tButtonType yes = new ButtonType(\"Yes\");\n\t\t\tButtonType no = new ButtonType(\"No\");\n\n\t\t\talert.getButtonTypes().setAll(yes, no);\n\t\t\tStage stage = (Stage) alert.getDialogPane().getScene().getWindow();\n\t\t\tstage.setOnCloseRequest(event -> alert.hide());\n\t\t\tNode root = alert.getDialogPane();\n\t\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\t\tint tmp = fontNum - 10;\n\t\t\t\tif (tmp < 12)\n\t\t\t\t\ttmp = 12;\n\t\t\t\troot.setStyle(\"-fx-font-size: \" + tmp + \"pt\");\n\t\t\t\talert.getDialogPane().applyCss();\n\t\t\t\talert.getDialogPane().layout();\n\t\t\t\tstage.sizeToScene();\n\t\t\t});\n\t\t\tboolean result = alert.showAndWait().map(response -> {\n\t\t\t\tif (response == yes)\n\t\t\t\t\treturn true;\n\t\t\t\tif (response == no)\n\t\t\t\t\treturn false;\n\t\t\t\treturn null;\n\t\t\t}).orElse(null);\n\n\t\t\tfuture.complete(result);\n\t\t});\n\n\t\ttry {\n\t\t\treturn future.get();\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t\treturn false;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/scripting/GithubLoginFX.java",
    "content": "package com.neuronrobotics.bowlerstudio.scripting;\n\nimport java.io.IOException;\nimport java.net.URL;\nimport java.util.ResourceBundle;\n\nimport javafx.fxml.FXML;\nimport javafx.scene.Parent;\nimport javafx.scene.Scene;\nimport javafx.scene.control.TextField;\nimport javafx.scene.control.PasswordField;\nimport javafx.stage.Stage;\n\npublic class GithubLoginFX implements javafx.fxml.Initializable {\n\n\t@FXML\n\tprivate TextField username;\n\t@FXML\n\tPasswordField password;\n\n\tprivate boolean done = false;\n\n\tprivate String[] creds = new String[]{\"\", \"\"};\n\tprivate Stage stage;\n\tprivate Parent root;\n\tprivate Scene scene;\n\n\t@Override\n\tpublic void initialize(URL location, ResourceBundle resources) {\n\n\t\treset();\n\t}\n\n\tpublic void reset() {\n\t\tdone = false;\n\t\tsetCreds(new String[]{\"\", \"\"});\n\t\tpassword.clear();\n\t\tgetUsername().clear();\n\n\t}\n\n\t@FXML\n\tpublic void anonMode() {\n\t\tsetCreds(null);\n\t\ttry {\n\t\t\t// this should make anon mode stick\n\t\t\tScriptingEngine.setupAnyonmous();\n\t\t} catch (IOException e) {\n\t\t\t// Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t}\n\t\tfinish();\n\t}\n\n\tprivate void finish() {\n\t\tstage.close();\n\t\tstage.hide();\n\t\tdone = true;\n\t}\n\n\t@FXML\n\tpublic void login() {\n\t\tgetCreds()[0] = getUsername().getText();\n\t\tgetCreds()[1] = password.getText();\n\t\tif (getCreds()[0] == null || getCreds()[1] == null) {\n\t\t\tsetCreds(null);\n\t\t} else if (getCreds()[0].equals(\"\") || getCreds()[1].equals(\"\")) {\n\t\t\tsetCreds(null);\n\t\t}\n\n\t\tfinish();\n\t}\n\n\t@FXML\n\tpublic void focusOnPw() {\n\t\tpassword.requestFocus();\n\t}\n\n\tpublic boolean isDone() {\n\t\treturn done;\n\t}\n\n\tpublic void setDone(boolean done) {\n\t\tthis.done = done;\n\t}\n\n\tpublic String[] getCreds() {\n\t\treturn creds;\n\t}\n\n\tpublic void setCreds(String[] creds) {\n\t\tthis.creds = creds;\n\t}\n\n\tpublic void setStage(Stage stage, Parent root) {\n\t\tthis.stage = stage;\n\t\tif (this.root == null) {\n\t\t\tthis.root = root;\n\t\t\tscene = new Scene(root);\n\t\t}\n\t\tstage.setScene(scene);\n\t}\n\n\tpublic TextField getUsername() {\n\t\treturn username;\n\t}\n\n\tpublic void setUsername(TextField username) {\n\t\tthis.username = username;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/scripting/IExternalEditor.java",
    "content": "package com.neuronrobotics.bowlerstudio.scripting;\n\nimport java.io.File;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.util.List;\n\nimport javafx.scene.control.Button;\nimport javafx.scene.image.Image;\n\npublic interface IExternalEditor {\n\n\tpublic abstract List<Class> getSupportedLangauge();\n\n\tdefault boolean isSupportedByExtension(File file) {\n\t\tif (getSupportedLangauge() != null)\n\t\t\tfor (Class c : getSupportedLangauge())\n\t\t\t\tif (c.isInstance(ScriptingEngine.getLangaugeByExtension(file.getAbsolutePath()))) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\treturn false;\n\t}\n\n\tpublic abstract void launch(File file, Button advanced, Runnable onExit);\n\n\tpublic abstract String nameOfEditor();\n\n\tpublic abstract URL getInstallURL() throws MalformedURLException;\n\n\tpublic abstract void onProcessExit(int ev);\n\n\tpublic abstract Image getImage();\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/scripting/IScriptEventListener.java",
    "content": "package com.neuronrobotics.bowlerstudio.scripting;\n\nimport java.io.File;\n\npublic interface IScriptEventListener {\n\n\tvoid onScriptFinished(Object result, Object previous, File source);\n\n\tvoid onScriptChanged(String previous, String current, File source);\n\n\tvoid onScriptError(Throwable except, File source);\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/scripting/ScriptingFileWidget.java",
    "content": "package com.neuronrobotics.bowlerstudio.scripting;\n\nimport javafx.scene.Node;\nimport com.neuronrobotics.bowlerstudio.BowlerStudio;\nimport com.neuronrobotics.bowlerstudio.BowlerStudioController;\nimport com.neuronrobotics.bowlerstudio.assets.AssetFactory;\nimport com.neuronrobotics.bowlerstudio.assets.FontSizeManager;\nimport com.neuronrobotics.bowlerstudio.creature.CadFileExporter;\nimport com.neuronrobotics.bowlerstudio.creature.MobleBaseMenueFactory;\nimport com.neuronrobotics.bowlerstudio.printbed.PrintBedManager;\nimport com.neuronrobotics.bowlerstudio.scripting.external.ExternalEditorController;\nimport com.neuronrobotics.bowlerstudio.util.FileChangeWatcher;\nimport com.neuronrobotics.bowlerstudio.util.IFileChangeListener;\n//import com.neuronrobotics.imageprovider.OpenCVImageProvider;\nimport com.neuronrobotics.nrconsole.util.CommitWidget;\nimport com.neuronrobotics.nrconsole.util.FileSelectionFactory;\nimport com.neuronrobotics.sdk.common.Log;\nimport com.neuronrobotics.sdk.util.ThreadUtil;\n\nimport eu.mihosoft.vrl.v3d.CSG;\nimport eu.mihosoft.vrl.v3d.parametrics.CSGDatabase;\nimport javafx.application.Platform;\nimport javafx.geometry.Insets;\nimport javafx.scene.control.Alert;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.CheckBox;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.TextField;\nimport javafx.scene.control.Tooltip;\nimport javafx.scene.layout.*;\nimport javafx.stage.FileChooser.ExtensionFilter;\nimport org.eclipse.jgit.api.errors.GitAPIException;\n\nimport javafx.stage.Stage;\n\nimport java.awt.*;\nimport java.io.*;\nimport java.nio.file.Files;\nimport java.nio.file.Paths;\nimport java.nio.file.WatchEvent;\nimport java.util.ArrayList;\nimport java.util.List;\nimport javafx.scene.control.Tooltip;\nimport javafx.application.Platform;\nimport javafx.scene.control.Alert;\nimport javafx.scene.control.ButtonType;\nimport java.util.concurrent.CompletableFuture;\n\n@SuppressWarnings(\"unused\")\npublic class ScriptingFileWidget extends BorderPane implements IFileChangeListener {\n\n\tprivate boolean running = false;\n\tprivate Thread scriptRunner = null;\n\tprivate Dimension codeDimentions = new Dimension(1168, 768);\n\t// Label fileLabel = new Label();\n\tprivate Object scriptResult;\n\tprivate String codeText = \"\";\n\n\tprivate ArrayList<IScriptEventListener> listeners = new ArrayList<>();\n\n\tprivate Button runfx = new Button(\"Run\");\n\tprivate Button arrange = new Button(\"Bed\");\n\n\tprivate Button publish = new Button(\"Save\");\n\tprivate Button convert = new Button(\"Convert...\");\n\n\tprivate CheckBox autoRun = new CheckBox();\n\n\tprivate String addr;\n\tboolean loadGist = false;\n\n\tprivate PrintBedManager manager = null;\n\tprivate ScriptingWidgetType type;\n\n\tfinal TextField gitField = new TextField();\n\tfinal TextField fileNameBox = new TextField();\n\tprivate File currentFile = null;\n\tExternalEditorController externalEditorController;\n\tprivate HBox controlPane;\n\tprivate String currentGist;\n\tprivate boolean updateneeded = false;\n\tprivate IScriptingLanguage langaugeType;\n\t// private ImageView image=new ImageView();\n\tprivate boolean isOwnedByLoggedInUser = false;\n\tprivate String remote;\n\tprivate boolean isArrange = false;\n\n\tprivate Button printbed;\n\tprivate FileChangeWatcher watch;\n\tprivate String findLocalPath;;\n\n\tpublic ScriptingFileWidget(File currentFile) throws IOException {\n\t\tload(ScriptingWidgetType.FILE, currentFile);\n\n\t}\n\n\tprivate void startStopAction() {\n\t\tBowlerStudio.runLater(() -> {\n\t\t\trunfx.setDisable(true);\n\t\t});\n\t\t// perform start stop outside the UI thread\n\t\tnew Thread(() -> {\n\t\t\tif (running)\n\t\t\t\tstop();\n\t\t\telse\n\t\t\t\tstart();\n\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\trunfx.setDisable(false);\n\t\t\t});\n\t\t}).start();\n\t}\n\n\tprivate void load(ScriptingWidgetType type, File currentFile) {\n\t\tisOwnedByLoggedInUser = ScriptingEngine.checkOwner(currentFile);\n\t\tthis.type = type;\n\t\tthis.currentFile = currentFile;\n\t\trunfx.setOnAction(e -> {\n\t\t\tisArrange = false;\n\t\t\trun();\n\t\t});\n\t\tarrange.setOnAction(e -> {\n\t\t\tisArrange = true;\n\t\t\trun();\n\t\t});\n\t\trunfx.setTooltip(new Tooltip(\"Run this code and display the result\"));\n\t\tarrange.setTooltip(new Tooltip(\"Arrange a print bed of these parts\"));\n\t\tpublish.setOnAction(e -> {\n\t\t\tsaveTheFile(currentFile);\n\n\t\t});\n\t\tpublish.setTooltip(new Tooltip(\"Save this code to Git\"));\n\n\t\tconvert.setOnAction(e -> {\n\t\t\tlaunchConvert(currentFile);\n\t\t});\n\t\tconvert.setTooltip(new Tooltip(\n\t\t\t\t\"Convert the output of this file to a different file type. For instance convert each  of the CSG's from a script into STL's or Blender Files in the working repo.\"));\n\n\t\tautoRun.setTooltip(new Tooltip(\"Check to auto-run files on file change\"));\n\n\t\t// arrange.setMinWidth(80);\n\t\t// publish.setMinWidth(80);\n\n\t\t// Set up the run controls and the code area\n\t\t// The BorderPane has the same areas laid out as the\n\t\t// BorderLayout layout manager\n\t\tsetPadding(new Insets(1, 0, 3, 10));\n\n\t\tcontrolPane = new HBox(20);\n\t\tdouble lengthScalar = fileNameBox.getFont().getSize() * 1.5;\n\t\t// fileNameBox.prefColumnCountProperty().bind(fileNameBox.textProperty().length());\n\t\t// fileListBox.prefColumnCountProperty().bind(fileListBox.textProperty().length());\n\n\t\tHBox.setHgrow(fileNameBox, Priority.ALWAYS);\n\t\tfileNameBox.setMaxWidth(Double.MAX_VALUE);\n\t\tHBox.setHgrow(gitField, Priority.ALWAYS);\n\t\tgitField.setMaxWidth(Double.MAX_VALUE);\n\n\t\t// fileNameBox.textProperty().addListener((ov, prevText, currText) -> {\n\t\t// // Do this in a BowlerStudio.runLater because of Textfield has no padding at\n\t\t// // first\n\t\t// // time and so on\n\t\t// BowlerStudio.runLater(() -> {\n\t\t// Text text = new Text(currText);\n\t\t// text.setFont(fileNameBox.getFont()); // Set the same font, so the size is the\n\t\t// same\n\t\t// double width = text.getLayoutBounds().getWidth() // This big is the Text in\n\t\t// the TextField\n\t\t// + fileNameBox.getPadding().getLeft() + fileNameBox.getPadding().getRight() //\n\t\t// Add the padding of\n\t\t// // the TextField\n\t\t// + lengthScalar; // Add some spacing\n\t\t// fileNameBox.setPrefWidth(width); // Set the width\n\t\t// fileNameBox.positionCaret(fileNameBox.getCaretPosition()); // If you remove\n\t\t// this line, it flashes a\n\t\t// // little bit\n\t\t// });\n\t\t// });\n\t\t// fileListBox.textProperty().addListener((ov, prevText, currText) -> {\n\t\t// // Do this in a BowlerStudio.runLater because of Textfield has no padding at\n\t\t// // first\n\t\t// // time and so on\n\t\t// BowlerStudio.runLater(() -> {\n\t\t// Text text = new Text(currText);\n\t\t// text.setFont(fileListBox.getFont()); // Set the same font, so the size is the\n\t\t// same\n\t\t// double width = text.getLayoutBounds().getWidth() // This big is the Text in\n\t\t// the TextField\n\t\t// + fileListBox.getPadding().getLeft() + fileListBox.getPadding().getRight() //\n\t\t// Add the padding of\n\t\t// // the TextField\n\t\t// + lengthScalar; // Add some spacing\n\t\t// fileListBox.setPrefWidth(width); // Set the width\n\t\t// fileListBox.positionCaret(fileListBox.getCaretPosition()); // If you remove\n\t\t// this line, it flashes a\n\t\t// // little bit\n\t\t// });\n\t\t// });\n\n\t\t// com.neuronrobotics.sdk.common.Log.error(\"\\n\\n\\nScriptingFileWidget loading\n\t\t// the editor loader:\\n\\n\\n\");\n\t\ttry {\n\t\t\texternalEditorController = new ExternalEditorController(currentFile, autoRun, () -> {\n\t\t\t});\n\t\t} catch (Throwable t) {\n\t\t\tt.printStackTrace();\n\t\t}\n\n\t\tButton openFile = new Button(\"Open\");\n\t\topenFile.setGraphic(AssetFactory.loadIcon(\"Folder.png\"));\n\t\topenFile.setMinWidth(85);\n\t\topenFile.setTooltip(new Tooltip(\"Click here to open the file in the OS browser\"));\n\t\topenFile.setOnAction(event -> {\n\t\t\tnew Thread(() -> {\n\t\t\t\tDesktop desktop = Desktop.getDesktop();\n\t\t\t\ttry {\n\t\t\t\t\tdesktop.open(currentFile.getParentFile());\n\t\t\t\t} catch (IOException e1) {\n\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\te1.printStackTrace();\n\t\t\t\t}\n\t\t\t}).start();\n\n\t\t});\n\t\tprintbed = new Button(\"STL\");\n\t\tprintbed.setMinWidth(80);\n\t\tprintbed.setGraphic(AssetFactory.loadIcon(\"Edit-CAD-Engine.png\"));\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tprintbed.setDisable(true);\n\t\t});\n\t\tprintbed.setOnAction(event -> {\n\t\t\tif (manager != null) {\n\t\t\t\texportAll(true);\n\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\tprintbed.setDisable(true);\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Nothing to export!\");\n\t\t\t}\n\t\t});\n\t\tfinal Tooltip tooltip = new Tooltip();\n\t\ttooltip.setText(\n\t\t\t\t\"\\nMake a print bed and export all of the parts on the screen\\n\" + \"to manufacturing. STL and SVG\\n\");\n\t\tprintbed.setTooltip(tooltip);\n\n\t\tcontrolPane.getChildren().add(runfx);\n\t\tif (isOwnedByLoggedInUser) {\n\t\t\tcontrolPane.getChildren().add(arrange);\n\t\t\tcontrolPane.getChildren().add(printbed);\n\t\t}\n\t\tif (externalEditorController != null)\n\t\t\tcontrolPane.getChildren().add(externalEditorController.getControl());\n\t\tcontrolPane.getChildren().add(autoRun);\n\t\tcontrolPane.getChildren().add(publish);\n\t\tcontrolPane.getChildren().add(openFile);\n\t\tLabel e = new Label(\"file:\");\n\t\te.setMinWidth(30);\n\t\tcontrolPane.getChildren().add(e);\n\t\tcontrolPane.getChildren().add(fileNameBox);\n\t\tfileNameBox.setMaxWidth(Double.MAX_VALUE);\n\t\tLabel e2 = new Label(\"git:\");\n\t\te2.setMinWidth(30);\n\t\tcontrolPane.getChildren().add(e2);\n\t\tcontrolPane.getChildren().add(gitField);\n\t\tgitField.setMaxWidth(Double.MAX_VALUE);\n\t\tcontrolPane.setMaxWidth(Double.MAX_VALUE);\n\t\t// convert\n\t\tcontrolPane.getChildren().add(convert);\n\n\t\t// put the flowpane in the top area of the BorderPane\n\t\tsetTop(controlPane);\n\n\t\taddIScriptEventListener(BowlerStudioController.getBowlerStudio());\n\n\t\ttry {\n\t\t\tloadCodeFromFile(currentFile);\n\t\t} catch (IOException e1) {\n\t\t\t// file has no git\n\t\t\tisOwnedByLoggedInUser = false;\n\t\t}\n\t\t// publish.setDisable(!isOwnedByLoggedInUser);\n\t\trunfx.setGraphic(AssetFactory.loadIcon(\"Run.png\"));\n\t\tif (isOwnedByLoggedInUser)\n\t\t\tpublish.setGraphic(AssetFactory.loadIcon(\"Publish.png\"));\n\t\telse\n\t\t\tpublish.setGraphic(AssetFactory.loadIcon(\"Fork.png\"));\n\t\tarrange.setGraphic(AssetFactory.loadIcon(\"Edit-CAD-Engine.png\"));\n\t\tconvert.setGraphic(AssetFactory.loadIcon(\"Forward-Button.png\"));\n\t\tarrange.setDisable(true);\n\t\tconvert.setDisable(true);\n\t\treset();\n\t}\n\n\tprivate void launchConvert(File currentFile2) {\n\t\t// Auto-generated method stub\n\t\tnew Thread(() -> {\n\t\t\tString fileType = chooseFileType();\n\t\t\tif (fileType != null) {\n\t\t\t\ttry {\n\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"User selected: \" + fileType);\n\n\t\t\t\t\tconvertResults(fileType);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\n\t\t\t}\n\t\t}).start();\n\t}\n\n\tprivate void convertResults(String fileType) throws Exception {\n\t\tObject obj = ScriptingEngine.inlineFileScriptRun(currentFile, null);\n\t\tArrayList<CSG> cache = new ArrayList<>();\n\t\taddObject(obj, cache);\n\t\tint index = 0;\n\t\tString url = getURL();\n\n\t\tboolean useSingleFileFOrImports = fileType.toLowerCase().contains(\"fcstd\")\n\t\t\t\t|| fileType.toLowerCase().contains(\"blend\");\n\t\tFile newFile = null;\n\t\tif (useSingleFileFOrImports) {\n\t\t\tString file = fileNameBox.getText() + \".\" + fileType;\n\t\t\tnewFile = ScriptingEngine.fileFromGit(url, file);\n\t\t\tScriptingEngine.ignore(url, \"/**.FCBak\");\n\t\t\tScriptingEngine.ignore(url, \"**.blend1\");\n\t\t\tif (newFile.exists()) {\n\t\t\t\tif (AskToDeleteWidget.askToDeleteFile(file)) {\n\t\t\t\t\tnewFile.delete();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfor (CSG c : cache) {\n\t\t\tString objectName = c.getName().length() > 0 ? \"_\" + c.getName() : \"\";\n\t\t\tString indexString = cache.size() > 1 ? \"_\" + index : \"\";\n\t\t\tif (useSingleFileFOrImports) {\n\t\t\t\t// In freeecad we add each item to the same model\n\t\t\t\tobjectName = \"\";\n\t\t\t\tindexString = \"\";\n\t\t\t}\n\t\t\tString file = fileNameBox.getText() + objectName + indexString + \".\" + fileType;\n\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"File Name \" + file);\n\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Placing file in \" + url);\n\t\t\ttry {\n\t\t\t\tnewFile = ScriptingEngine.fileFromGit(url, file);\n\t\t\t\tif (newFile.exists() && !useSingleFileFOrImports) {\n\t\t\t\t\tif (AskToDeleteWidget.askToDeleteFile(file)) {\n\t\t\t\t\t\tnewFile.delete();\n\t\t\t\t\t} else\n\t\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t// do the conversion now\n\t\t\t\tif (fileType.toLowerCase().contains(\"stl\")) {\n\t\t\t\t\tc.toStl(Paths.get(newFile.getAbsolutePath()));\n\t\t\t\t}\n\t\t\t\tif (fileType.toLowerCase().contains(\"blend\")) {\n\t\t\t\t\tBlenderLoader.toBlenderFile(CSGDatabase.getInstance(), c, newFile);\n\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Added mesh to \" + newFile);\n\t\t\t\t}\n\t\t\t\tif (fileType.toLowerCase().contains(\"fcstd\")) {\n\t\t\t\t\tFreecadLoader.addCSGToFreeCAD(newFile, c);\n\t\t\t\t}\n\t\t\t\tif (!useSingleFileFOrImports)\n\t\t\t\t\tBowlerStudio.createFileTab(newFile);\n\t\t\t} catch (GitAPIException | IOException e) {\n\t\t\t\t// Auto-generated catch block\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t\tindex++;\n\t\t}\n\t\tif (useSingleFileFOrImports && newFile != null)\n\t\t\tBowlerStudio.createFileTab(newFile);\n\t}\n\n\tpublic String chooseFileType() {\n\t\tCompletableFuture<String> future = new CompletableFuture<>();\n\n\t\tPlatform.runLater(() -> {\n\t\t\tAlert alert = new Alert(Alert.AlertType.CONFIRMATION);\n\t\t\talert.setTitle(\"File Type Selection\");\n\t\t\talert.setHeaderText(\"Choose file type\");\n\t\t\talert.setContentText(\"Output results to type:\");\n\n\t\t\tButtonType stlButton = new ButtonType(\"STL\");\n\t\t\tButtonType blenderButton = new ButtonType(\"Blender\");\n\t\t\tButtonType freecad = new ButtonType(\"FreeCAD\");\n\t\t\tButtonType cancelButton = new ButtonType(\"Cancel\");\n\n\t\t\talert.getButtonTypes().setAll(stlButton, blenderButton, freecad, cancelButton);\n\t\t\tStage stage = (Stage) alert.getDialogPane().getScene().getWindow();\n\t\t\tstage.setOnCloseRequest(event -> alert.hide());\n\t\t\tNode root = alert.getDialogPane();\n\t\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\t\tint tmp = fontNum - 10;\n\t\t\t\tif (tmp < 12)\n\t\t\t\t\ttmp = 12;\n\t\t\t\troot.setStyle(\"-fx-font-size: \" + tmp + \"pt\");\n\t\t\t\talert.getDialogPane().applyCss();\n\t\t\t\talert.getDialogPane().layout();\n\t\t\t\tstage.sizeToScene();\n\t\t\t});\n\t\t\tString result = alert.showAndWait().map(response -> {\n\t\t\t\tif (response == stlButton)\n\t\t\t\t\treturn \"stl\";\n\t\t\t\tif (response == blenderButton)\n\t\t\t\t\treturn \"blend\";\n\t\t\t\tif (response == freecad)\n\t\t\t\t\treturn \"FCStd\";\n\t\t\t\treturn null;\n\t\t\t}).orElse(null);\n\n\t\t\tfuture.complete(result);\n\t\t});\n\n\t\ttry {\n\t\t\treturn future.get();\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate String getURL() {\n\t\treturn gitField.getText();\n\t}\n\n\tprivate void exportAll(boolean makePrintBed) {\n\t\tnew Thread() {\n\t\t\tpublic void run() {\n\t\t\t\tsetName(\"Exporting the CAD objects\");\n\t\t\t\tArrayList<CSG> csgs = manager.makePrintBeds();\n\t\t\t\tif (makePrintBed) {\n\n\t\t\t\t}\n\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Exporting \" + csgs.size() + \" parts\");\n\t\t\t\tFile baseDirForFiles = FileSelectionFactory.GetDirectory(MobleBaseMenueFactory.getBaseDirForFiles());\n\t\t\t\ttry {\n\t\t\t\t\tArrayList<File> files = new CadFileExporter(BowlerStudioController.getMobileBaseUI())\n\t\t\t\t\t\t\t.generateManufacturingParts(csgs, baseDirForFiles);\n\t\t\t\t\tfor (File f : files) {\n\t\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Exported \" + f.getAbsolutePath());\n\n\t\t\t\t\t}\n\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Success! \" + files.size() + \" parts exported\");\n\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\tBowlerStudio.printStackTrace(e);\n\t\t\t\t}\n\n\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\tprintbed.setDisable(false);\n\t\t\t\t});\n\t\t\t}\n\t\t}.start();\n\t}\n\n\tprivate void run() {\n\t\tnew Thread() {\n\t\t\tpublic void run() {\n\n\t\t\t\tsave();\n\t\t\t\t// do not attempt to save no binary files\n\t\t\t\tstartStopAction();\n\t\t\t}\n\t\t}.start();\n\t}\n\n\tpublic void saveTheFile(File currentFile) {\n\t\tnew Thread(() -> {\n\t\t\tif (isOwnedByLoggedInUser) {\n\t\t\t\tsave();\n\t\t\t\tCommitWidget.commit(currentFile, langaugeType.getIsTextFile() ? getCode() : null);\n\t\t\t} else {\n\t\t\t\tString reponame = currentFile.getName().split(\"\\\\.\")[0] + \"_\" + PasswordManager.getLoginID();\n\t\t\t\tString content = langaugeType.getIsTextFile() ? getCode() : null;\n\t\t\t\tString newGit;\n\t\t\t\ttry {\n\t\t\t\t\tnewGit = ScriptingEngine.fork(remote, reponame, \"Making fork from git: \" + remote);\n\t\t\t\t\tScriptingEngine.pushCodeToGit(newGit, null, currentFile.getName(), content, \"Tmp save during fork\");\n\t\t\t\t\tFile file = ScriptingEngine.fileFromGit(newGit, currentFile.getName());\n\t\t\t\t\t// ScriptingEngine.deleteRepo(remote);\n\t\t\t\t\tThread.sleep(500);\n\t\t\t\t\tBowlerStudio.createFileTab(file);\n\t\t\t\t} catch (Exception e1) {\n\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\te1.printStackTrace();\n\t\t\t\t}\n\n\t\t\t}\n\t\t}).start();\n\t}\n\n\tprivate void reset() {\n\t\trunning = false;\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tBowlerStudio.setToRunButton(runfx);\n\t\t});\n\n\t}\n\n\tpublic void addIScriptEventListener(IScriptEventListener l) {\n\t\tif (!listeners.contains(l))\n\t\t\tlisteners.add(l);\n\t}\n\n\tpublic void removeIScriptEventListener(IScriptEventListener l) {\n\t\tif (listeners.contains(l))\n\t\t\tlisteners.remove(l);\n\t}\n\n\tpublic void stop() {\n\t\t// Auto-generated method stub\n\n\t\treset();\n\t\tif (scriptRunner != null)\n\t\t\twhile (scriptRunner.isAlive()) {\n\n\t\t\t\tLog.debug(\"Interrupting\");\n\t\t\t\tThreadUtil.wait(10);\n\t\t\t\ttry {\n\t\t\t\t\tscriptRunner.interrupt();\n\t\t\t\t\tscriptRunner.join();\n\t\t\t\t} catch (InterruptedException e) {\n\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\n\t\t\t}\n\n\t}\n\n\tpublic void loadCodeFromFile(File currentFile) throws IOException {\n\t\tif (!currentFile.exists()) {\n\t\t\tcurrentFile.createNewFile();\n\t\t}\n\t\tsetUpFile(currentFile);\n\t\tif (!langaugeType.getIsTextFile())\n\t\t\tsetCode(\"Binary File\");\n\t\telse\n\t\t\tsetCode(new String(Files.readAllBytes(currentFile.toPath())));\n\n\t}\n\n\tprivate void start() {\n\n\t\tBowlerStudio.clearConsole();\n\t\tBowlerStudioController.clearHighlight();\n\n\t\trunning = true;\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tBowlerStudio.setToStopButton(runfx);\n\n\t\t});\n\t\tscriptRunner = new Thread() {\n\n\t\t\tpublic void run() {\n\t\t\t\t// String name;\n\t\t\t\t// try{\n\t\t\t\t// name = currentFile.getName();\n\t\t\t\t// }catch (NullPointerException e){\n\t\t\t\t// name=\"\";\n\t\t\t\t// }\n\t\t\t\ttry {\n\t\t\t\t\tObject obj = ScriptingEngine.inlineFileScriptRun(CSGDatabase.getInstance(), currentFile, null);\n\t\t\t\t\tArrayList<CSG> cache = new ArrayList<>();\n\t\t\t\t\taddObject(obj, cache);\n\t\t\t\t\tString git;\n\t\t\t\t\tgit = ScriptingEngine.locateGitUrl(currentFile);\n\t\t\t\t\tif (cache.size() > 0) {\n\t\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\t\tconvert.setDisable(false);\n\t\t\t\t\t\t});\n\t\t\t\t\t\tboolean enableArraange = false;\n\t\t\t\t\t\tfor (CSG c : cache) {\n\t\t\t\t\t\t\tif (c.getName().length() > 0) {\n\t\t\t\t\t\t\t\tenableArraange = true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (enableArraange) {\n\t\t\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\t\t\tarrange.setDisable(false);\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tif (git != null && isArrange) {\n\t\t\t\t\t\t\t\tmanager = new PrintBedManager(git, cache);\n\t\t\t\t\t\t\t\tobj = manager.get();\n\t\t\t\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\t\t\t\tprintbed.setDisable(false);\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tfor (int i = 0; i < listeners.size(); i++) {\n\t\t\t\t\t\tIScriptEventListener l = listeners.get(i);\n\t\t\t\t\t\tl.onScriptFinished(obj, scriptResult, currentFile);\n\t\t\t\t\t}\n\n\t\t\t\t\tscriptResult = obj;\n\t\t\t\t\treset();\n\n\t\t\t\t} catch (groovy.lang.MissingPropertyException | org.python.core.PyException d) {\n\t\t\t\t\tBowlerStudioController.highlightException(currentFile, d);\n\t\t\t\t} catch (Throwable ex) {\n\t\t\t\t\tLog.error(ex);\n\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.debug(\"Script exception of type= \" + ex.getClass().getName());\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (ex.getMessage().contains(\"sleep interrupted\")) {\n\t\t\t\t\t\t\tappend(\"\\n\" + currentFile + \" Interupted\\n\");\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tBowlerStudioController.highlightException(currentFile, new Exception(ex));\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\te.printStackTrace();\n\t\t\t\t\t\tBowlerStudioController.highlightException(currentFile, new Exception(ex));\n\t\t\t\t\t}\n\n\t\t\t\t\treset();\n\n\t\t\t\t\tfor (IScriptEventListener l : listeners) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tl.onScriptError(new Exception(ex), currentFile);\n\t\t\t\t\t\t} catch (Throwable e) {\n\t\t\t\t\t\t\tLog.error(e);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t}\n\t\t};\n\n\t\ttry {\n\n\t\t\tscriptRunner.start();\n\t\t} catch (Exception e) {\n\t\t\t// Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t}\n\n\t}\n\n\tvoid addObject(Object o, ArrayList<CSG> cache) {\n\t\tif (List.class.isInstance(o)) {\n\t\t\tList<Object> c = (List<Object>) o;\n\t\t\tfor (int i = 0; i < c.size(); i++) {\n\t\t\t\t// Log.warning(\"Loading array Lists with removals \" + c.get(i));\n\t\t\t\taddObject(c.get(i), cache);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (CSG.class.isInstance(o)) {\n\t\t\tCSG csg = (CSG) o;\n\t\t\tcache.add(csg);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tprivate void append(String s) {\n\t\tcom.neuronrobotics.sdk.common.Log.error(s);\n\t}\n\n\tpublic String getGitRepo() {\n\t\treturn remote;\n\t}\n\n\tpublic String getGitFile() {\n\t\treturn findLocalPath;\n\t}\n\n\tprivate void setUpFile(File f) {\n\t\tcom.neuronrobotics.sdk.common.Log.error(\"Setup ScriptingFileWidget \" + f.getAbsolutePath());\n\t\tcurrentFile = f;\n\t\ttry {\n\t\t\twatch = FileChangeWatcher.watch(currentFile);\n\t\t} catch (IOException e2) {\n\t\t\t// Auto-generated catch block\n\t\t\te2.printStackTrace();\n\t\t}\n\t\tString langType = ScriptingEngine.getShellType(currentFile.getName());\n\t\t// try {\n\t\t// image.setImage(AssetFactory.loadAsset(\"Script-Tab-\"+ScriptingEngine.getShellType(currentFile.getName())+\".png\"));\n\t\t// } catch (Exception e2) {\n\t\t// // Auto-generated catch block\n\t\t// e2.printStackTrace();\n\t\t// }\n\t\tlangaugeType = ScriptingEngine.getLangaugesMap().get(langType);\n\t\t// ScriptingEngine.setLastFile(f);\n\n\t\ttry {\n\t\t\tScriptingEngine.locateGit(currentFile, git -> {\n\t\t\t\tremote = git.getRepository().getConfig().getString(\"remote\", \"origin\", \"url\");\n\t\t\t\tfindLocalPath = ScriptingEngine.findLocalPath(f, git);\n\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t// fileListBox.setMinWidth(remote.getBytes().length*10);\n\t\t\t\t\tgitField.setText(remote);\n\t\t\t\t\tgitField.setTooltip(new Tooltip(remote));\n\t\t\t\t\t// fileListBox.res\n\n\t\t\t\t\tfileNameBox.setText(findLocalPath);\n\t\t\t\t\tfileNameBox.setTooltip(new Tooltip(findLocalPath));\n\t\t\t\t\t// These values are display only, so if hte user tries to change them, they\n\t\t\t\t\t// reset\n\t\t\t\t\t// the use of text field for static dats is so the user cna copy the vlaues and\n\t\t\t\t\t// use them in their scritpts\n\t\t\t\t\tfileNameBox.textProperty().addListener((observable, oldValue, newValue) -> {\n\t\t\t\t\t\tfileNameBox.setText(findLocalPath);\n\t\t\t\t\t});\n\t\t\t\t\tgitField.textProperty().addListener((observable, oldValue, newValue) -> {\n\t\t\t\t\t\tgitField.setText(remote);\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t});\n\n\t\t} catch (Exception e1) {\n\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\tgitField.setText(\"none\");\n\t\t\t\tgitField.setMinWidth(40);\n\t\t\t\tfileNameBox.setText(f.getAbsolutePath());\n\t\t\t\t// These values are display only, so if hte user tries to change them, they\n\t\t\t\t// reset\n\t\t\t\t// the use of text field for static dats is so the user cna copy the vlaues and\n\t\t\t\t// use them in their scritpts\n\t\t\t\tfileNameBox.textProperty().addListener((observable, oldValue, newValue) -> {\n\t\t\t\t\tfileNameBox.setText(f.getAbsolutePath());\n\t\t\t\t});\n\t\t\t\tgitField.textProperty().addListener((observable, oldValue, newValue) -> {\n\t\t\t\t\tgitField.setText(\"none\");\n\t\t\t\t});\n\n\t\t\t});\n\t\t\te1.printStackTrace();\n\t\t}\n\t\tif (!langaugeType.getIsTextFile())\n\t\t\treturn;\n\t\ttry {\n\t\t\twatch.addIFileChangeListener(this);\n\t\t} catch (Exception e) {\n\t\t\t// Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t}\n\n\t}\n\n\tprivate void updateFile() {\n\n\t\tFile last = FileSelectionFactory\n\t\t\t\t.GetFile(\n\t\t\t\t\t\tcurrentFile == null\n\t\t\t\t\t\t\t\t? ScriptingEngine.getWorkspace()\n\t\t\t\t\t\t\t\t: new File(\n\t\t\t\t\t\t\t\t\t\tScriptingEngine.getWorkspace().getAbsolutePath() + \"/\" + currentFile.getName()),\n\t\t\t\t\t\ttrue, new ExtensionFilter(\"Save Script\", \"*\"));\n\t\tif (last != null) {\n\t\t\tsetUpFile(last);\n\t\t}\n\n\t}\n\n\tpublic void open() {\n\n\t\tupdateFile();\n\t\ttry {\n\t\t\tsetCode(new String(Files.readAllBytes(currentFile.toPath())));\n\t\t} catch (IOException e) {\n\t\t\t// Auto-generated catch block\n\t\t\t// e.printStackTrace();\n\t\t}\n\t}\n\n\tpublic void save() {\n\t\tif (!langaugeType.getIsTextFile())\n\t\t\treturn;\n\t\ttry {\n\n\t\t\tString content = new String(Files.readAllBytes(Paths.get(currentFile.getAbsolutePath())));\n\t\t\tString ineditor = getCode();\n\t\t\tif (content.contentEquals(ineditor)) {\n\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Skip Writing file contents, file is same\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Writing file contents\");\n\t\t\tBufferedWriter writer = new BufferedWriter(new FileWriter(currentFile));\n\t\t\twriter.write(ineditor);\n\t\t\twriter.close();\n\t\t} catch (Exception ex) {\n\t\t\t// ex.printStackTrace();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onFileChange(File fileThatChanged, @SuppressWarnings(\"rawtypes\") WatchEvent event) {\n\t\tif (updateneeded)\n\t\t\treturn;\n\t\tupdateneeded = true;\n\t\ttry {\n\t\t\twatch.removeIFileChangeListener(this);\n\t\t\t// BowlerStudio.runLater( new Runnable() {\n\t\t\t// @Override\n\t\t\t// public void run() {\n\t\t\t// updateneeded = false;\n\t\t\t// Auto-generated method stub\n\t\t\tString absolutePath = fileThatChanged.getAbsolutePath();\n\t\t\tString absolutePath2 = currentFile.getAbsolutePath();\n\t\t\t// com.neuronrobotics.sdk.common.Log.error(absolutePath+\" \"+absolutePath2);\n\t\t\tif (absolutePath.contains(absolutePath2)) {\n\n\t\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"Code in \" + absolutePath + \"\n\t\t\t\t// changed\");\n\t\t\t\tString content = new String(Files.readAllBytes(Paths.get(absolutePath)));\n\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\tif (content.length() > 2)// ensures tha the file contents never get wiped out on the\n\t\t\t\t\t\t\t\t\t\t\t\t// user\n\t\t\t\t\t\tsetCode(content);\n\t\t\t\t\tif (autoRun.isSelected()) {\n\t\t\t\t\t\tnew Thread(() -> {\n\t\t\t\t\t\t\tstop();\n\t\t\t\t\t\t\tstart();\n\t\t\t\t\t\t}).start();\n\n\t\t\t\t\t}\n\t\t\t\t\twatch.addIFileChangeListener(this);\n\t\t\t\t\t// watch.addIFileChangeListener(ScriptingFileWidget.this);\n\n\t\t\t\t});\n\n\t\t\t} else {\n\t\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"Othr Code in\n\t\t\t\t// \"+fileThatChanged.getAbsolutePath()+\"\n\t\t\t\t// changed\");\n\t\t\t\twatch.addIFileChangeListener(this);\n\t\t\t}\n\t\t\t// }\n\t\t\t// });\n\t\t} catch (Throwable e) {\n\t\t\t// Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t}\n\t\tupdateneeded = false;\n\n\t}\n\n\tpublic String getCode() {\n\t\treturn codeText;\n\t}\n\n\tpublic void setCode(String string) {\n\t\tString previous = codeText;\n\t\tcodeText = string;\n\t\t// com.neuronrobotics.sdk.common.Log.error(codeText);\n\t\tfor (int i = 0; i < listeners.size(); i++) {\n\t\t\tlisteners.get(i).onScriptChanged(previous, string, currentFile);\n\t\t}\n\t}\n\n\tpublic String getFileName() {\n\t\tif (currentFile != null)\n\t\t\treturn currentFile.getName();\n\t\telse\n\t\t\treturn \"Web\";\n\t}\n\n\tpublic void close() {\n\n\t\twatch.removeIFileChangeListener(this);\n\t\twatch.close();\n\t\twatch = null;\n\n\t}\n\n\t@Override\n\tpublic void onFileDelete(File fileThatIsDeleted) {\n\t\tclose();\n\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/scripting/ScriptingWebWidget.java",
    "content": "package com.neuronrobotics.bowlerstudio.scripting;\n\nimport com.neuronrobotics.bowlerstudio.BowlerStudio;\nimport com.neuronrobotics.bowlerstudio.BowlerStudioController;\nimport com.neuronrobotics.bowlerstudio.assets.AssetFactory;\nimport com.neuronrobotics.bowlerstudio.assets.FontSizeManager;\n//import com.neuronrobotics.imageprovider.OpenCVImageProvider;\nimport com.neuronrobotics.sdk.common.Log;\nimport com.neuronrobotics.sdk.util.ThreadUtil;\nimport javafx.beans.value.ChangeListener;\nimport javafx.beans.value.ObservableValue;\nimport javafx.geometry.Insets;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.ComboBox;\nimport javafx.scene.image.ImageView;\nimport javafx.scene.layout.*;\nimport javafx.scene.web.WebEngine;\n\nimport org.eclipse.jgit.api.errors.GitAPIException;\nimport org.eclipse.jgit.api.errors.InvalidRemoteException;\nimport org.eclipse.jgit.api.errors.TransportException;\n\nimport java.awt.*;\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.PrintWriter;\nimport java.io.StringWriter;\nimport java.util.ArrayList;\nimport java.util.List;\n\n@SuppressWarnings({\"unused\", \"restriction\"})\npublic class ScriptingWebWidget extends BorderPane implements ChangeListener<Object> {\n\n\tprivate boolean running = false;\n\tprivate Thread scriptRunner = null;\n\n\tprivate Dimension codeDimentions = new Dimension(1168, 768);\n\t// Label fileLabel = new Label();\n\tprivate Object scriptResult;\n\tprivate String codeText = \"\";\n\n\tprivate ArrayList<IScriptEventListener> listeners = new ArrayList<>();\n\n\tprivate Button runfx = new Button(\"Run\");;\n\tprivate Button edit = new Button(\"Edit...\");\n\tprivate WebEngine engine;\n\n\tprivate String addr;\n\tboolean loadGist = false;\n\n\tprivate ScriptingWidgetType type;\n\n\tfinal ComboBox<String> fileListBox = new ComboBox<>();\n\tprivate File currentFile = null;\n\n\tprivate HBox controlPane;\n\tprivate String currentGit;\n\t// private String currentGist;\n\tprivate boolean isOwnedByLoggedInUser;\n\tprivate ImageView image = new ImageView();\n\n\tpublic ScriptingWebWidget(File currentFile, String currentGist, WebEngine engine)\n\t\t\tthrows IOException, InterruptedException {\n\t\tthis(ScriptingWidgetType.GIST);\n\t\trunfx.setGraphic(AssetFactory.loadIcon(\"Run.png\"));\n\t\tedit.setGraphic(AssetFactory.loadIcon(\"Edit-Script.png\"));\n\t\tthis.currentFile = currentFile;\n\t\ttry {\n\t\t\tloadCodeFromGist(currentGist, engine);\n\t\t} catch (Exception e) {\n\t\t\t// Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t}\n\n\t}\n\n\tprivate void startStopAction() {\n\t\tBowlerStudio.runLater(() -> runfx.setDisable(true));\n\t\tif (running)\n\t\t\tstop();\n\t\telse\n\t\t\tstart();\n\t\tBowlerStudio.runLater(() -> runfx.setDisable(false));\n\t}\n\n\tpublic ScriptingWebWidget(ScriptingWidgetType type) {\n\t\tthis.type = type;\n\n\t\trunfx.setOnAction(e -> {\n\t\t\tnew Thread() {\n\t\t\t\tpublic void run() {\n\n\t\t\t\t\tstartStopAction();\n\t\t\t\t}\n\t\t\t}.start();\n\t\t});\n\t\tedit.setOnAction(e -> {\n\t\t\tnew Thread() {\n\t\t\t\tpublic void run() {\n\t\t\t\t\tdoFork();\n\t\t\t\t}\n\n\t\t\t}.start();\n\n\t\t});\n\n\t\tsetPadding(new Insets(1, 0, 3, 10));\n\n\t\tcontrolPane = new HBox(20);\n\n\t\tcontrolPane.getChildren().add(runfx);\n\t\tcontrolPane.getChildren().add(image);\n\t\tcontrolPane.getChildren().add(edit);\n\n\t\tcontrolPane.getChildren().add(fileListBox);\n\n\t\t// put the flowpane in the top area of the BorderPane\n\t\tsetTop(controlPane);\n\n\t\taddIScriptEventListener(BowlerStudioController.getBowlerStudio());\n\t\treset();\n\t}\n\n\tprivate void doFork() {\n\t\tif (isOwnedByLoggedInUser)\n\t\t\tBowlerStudio.createFileTab(currentFile);\n\t\telse {\n\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Making Fork...\");\n\t\t\tString reponame = currentFile.getName().split(\"\\\\.\")[0] + \"_\" + PasswordManager.getLoginID();\n\t\t\ttry {\n\t\t\t\tString newGit = ScriptingEngine.fork(currentGit, reponame, \"Making fork from web gist\");\n\t\t\t\tFile file = ScriptingEngine.fileFromGit(newGit, currentFile.getName());\n\t\t\t\tBowlerStudio.createFileTab(file);\n\t\t\t} catch (Exception e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void reset() {\n\t\trunning = false;\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tBowlerStudio.setToRunButton(runfx);\n\t\t});\n\n\t}\n\n\tpublic void addIScriptEventListener(IScriptEventListener l) {\n\t\tif (!listeners.contains(l))\n\t\t\tlisteners.add(l);\n\t}\n\n\tpublic void removeIScriptEventListener(IScriptEventListener l) {\n\t\tif (listeners.contains(l))\n\t\t\tlisteners.remove(l);\n\t}\n\n\tpublic void stop() {\n\t\t// Auto-generated method stub\n\n\t\treset();\n\t\tif (scriptRunner != null)\n\t\t\twhile (scriptRunner.isAlive()) {\n\n\t\t\t\tLog.debug(\"Interrupting\");\n\t\t\t\tThreadUtil.wait(10);\n\t\t\t\ttry {\n\t\t\t\t\tscriptRunner.interrupt();\n\t\t\t\t\tscriptRunner.join();\n\t\t\t\t} catch (InterruptedException e) {\n\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\n\t\t\t}\n\n\t}\n\n\t// public void loadCodeFromFile(File currentFile) throws IOException {\n\t// if (!currentFile.exists()) {\n\t// currentFile.createNewFile();\n\t// }\n\t// setUpFile(currentFile);\n\t// setCode(new String(Files.readAllBytes(currentFile.toPath())));\n\t// }\n\n\tprivate void loadGitLocal(String id, String file) {\n\t\t// com.neuronrobotics.sdk.common.Log.error(\"Loading \"+file+\" from \"+id);\n\t\tString[] code;\n\t\ttry {\n\t\t\tcode = ScriptingEngine.codeFromGit(id, file);\n\n\t\t\tif (code != null) {\n\t\t\t\tsetCode(code[0]);\n\t\t\t\tcurrentFile = ScriptingEngine.fileFromGit(id, file);\n\t\t\t}\n\t\t\tisOwnedByLoggedInUser = ScriptingEngine.checkOwner(currentFile);\n\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\tif (isOwnedByLoggedInUser) {\n\t\t\t\t\tedit.setText(\"Edit...\");\n\t\t\t\t\tedit.setGraphic(AssetFactory.loadIcon(\"Edit-Script.png\"));\n\t\t\t\t} else {\n\t\t\t\t\tedit.setText(\"Make Copy\");\n\t\t\t\t\tedit.setGraphic(AssetFactory.loadIcon(\"Make-Copy-Script.png\"));\n\t\t\t\t}\n\t\t\t});\n\t\t\ttry {\n\t\t\t\timage.setImage(AssetFactory\n\t\t\t\t\t\t.loadAsset(\"Script-Tab-\" + ScriptingEngine.getShellType(currentFile.getName()) + \".png\"));\n\t\t\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\t\t\timage.setScaleX(FontSizeManager.getImageScale());\n\t\t\t\t\timage.setScaleY(FontSizeManager.getImageScale());\n\t\t\t\t});\n\t\t\t} catch (Exception e2) {\n\t\t\t\t// Auto-generated catch block\n\t\t\t\te2.printStackTrace();\n\t\t\t}\n\n\t\t} catch (Exception e) {\n\t\t\tStringWriter sw = new StringWriter();\n\t\t\tPrintWriter pw = new PrintWriter(sw);\n\t\t\te.printStackTrace(pw);\n\t\t\tcom.neuronrobotics.sdk.common.Log.error(sw.toString());\n\t\t}\n\t}\n\n\tpublic void loadCodeFromGist(String a, WebEngine e) throws Exception {\n\t\t// new Thread(()->{\n\t\taddr = a;\n\t\tengine = e;\n\t\tloadGist = true;\n\t\tfileListBox.valueProperty().removeListener(this);\n\t\tBowlerStudio.runLater(() -> runfx.setDisable(true));\n\t\tBowlerStudio.runLater(() -> edit.setDisable(true));\n\t\tBowlerStudio.runLater(() -> fileListBox.getItems().clear());\n\t\tList<String> gists = ScriptingEngine.getCurrentGist(addr, engine);\n\t\tArrayList<String> fileList;\n\t\tif (!gists.isEmpty()) {\n\t\t\tString currentGist = gists.get(0);\n\t\t\tcurrentGit = \"https://gist.github.com/\" + currentGist + \".git\";\n\t\t} else if (addr.contains(\"https://github.com/\")) {\n\n\t\t\tif (a.endsWith(\"/\")) {\n\t\t\t\ta = a.substring(0, a.length() - 1);\n\t\t\t}\n\t\t\tcurrentGit = a + \".git\";\n\n\t\t} else {\n\t\t\treturn;\n\t\t}\n\t\tArrayList<String> tmp = ScriptingEngine.filesInGit(currentGit);\n\t\tfileList = new ArrayList<>();\n\t\tfor (String s : tmp) {\n\t\t\tif (!s.contains(\"csgDatabase.json\"))// filter out configuration files from the list\n\t\t\t\tfileList.add(s);\n\t\t}\n\t\t// for(String s:fileList){\n\t\t// com.neuronrobotics.sdk.common.Log.error(\"GITS: \"+s);\n\t\t// }\n\t\tif (!fileList.isEmpty())\n\t\t\tloadGitLocal(currentGit, fileList.get(0));\n\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tArrayList<String> fileListToDisplay = new ArrayList<>();\n\t\t\tfor (String s : fileList) {\n\t\t\t\tif (!s.startsWith(\".\")) {\n\t\t\t\t\tfileListBox.getItems().add(s);\n\t\t\t\t\tfileListToDisplay.add(s);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!fileListToDisplay.isEmpty()) {\n\t\t\t\tfileListBox.setValue(fileListToDisplay.get(0));\n\t\t\t\ttry {\n\t\t\t\t\tcurrentFile = ScriptingEngine.fileFromGit(currentGit, fileListToDisplay.get(0));\n\t\t\t\t} catch (InvalidRemoteException e1) {\n\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\te1.printStackTrace();\n\t\t\t\t} catch (TransportException e1) {\n\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\te1.printStackTrace();\n\t\t\t\t} catch (GitAPIException e1) {\n\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\te1.printStackTrace();\n\t\t\t\t} catch (IOException e1) {\n\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\te1.printStackTrace();\n\t\t\t\t}\n\t\t\t\tfileListBox.valueProperty().addListener(this);\n\t\t\t\tBowlerStudio.runLater(() -> runfx.setDisable(false));\n\t\t\t\tBowlerStudio.runLater(() -> edit.setDisable(false));\n\t\t\t}\n\t\t});\n\t\t// }).start();\n\n\t}\n\n\tprivate void start() {\n\t\tBowlerStudio.clearConsole();\n\n\t\trunning = true;\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tBowlerStudio.setToStopButton(runfx);\n\t\t});\n\t\tscriptRunner = new Thread() {\n\n\t\t\tpublic void run() {\n\t\t\t\tString name;\n\t\t\t\ttry {\n\t\t\t\t\tname = currentFile.getName();\n\t\t\t\t} catch (NullPointerException e) {\n\t\t\t\t\tname = \"\";\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tObject obj = ScriptingEngine.inlineScriptRun(currentFile, null, ScriptingEngine.getShellType(name));\n\t\t\t\t\tfor (IScriptEventListener l : listeners) {\n\t\t\t\t\t\tl.onScriptFinished(obj, scriptResult, currentFile);\n\t\t\t\t\t}\n\n\t\t\t\t\tscriptResult = obj;\n\t\t\t\t\treset();\n\n\t\t\t\t} catch (Throwable ex) {\n\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Script exception of type= \" + ex.getClass().getName());\n\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tif (ex.getMessage().contains(\"sleep interrupted\")) {\n\t\t\t\t\t\t\t\tappend(\"\\n\" + currentFile + \" Interupted\\n\");\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tBowlerStudio.printStackTrace(ex, currentFile);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch (Throwable e) {\n\t\t\t\t\t\t\tStringWriter sw = new StringWriter();\n\t\t\t\t\t\t\tPrintWriter pw = new PrintWriter(sw);\n\t\t\t\t\t\t\tex.printStackTrace(pw);\n\t\t\t\t\t\t\tappend(\"\\n\" + currentFile + \" \\n\" + sw + \"\\n\");\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treset();\n\t\t\t\t\t});\n\t\t\t\t\tfor (IScriptEventListener l : listeners) {\n\t\t\t\t\t\tl.onScriptError(ex, currentFile);\n\t\t\t\t\t}\n\t\t\t\t\tBowlerStudio.printStackTrace(ex, currentFile);\n\t\t\t\t}\n\n\t\t\t}\n\t\t};\n\n\t\ttry {\n\t\t\t// if (loadGist)\n\t\t\t// loadCodeFromGist(addr, engine);\n\n\t\t\tscriptRunner.start();\n\t\t} catch (Exception e) {\n\t\t\t// Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t}\n\n\t}\n\n\tprivate void append(String s) {\n\t\tcom.neuronrobotics.sdk.common.Log.error(s);\n\t}\n\n\tpublic String getCode() {\n\t\treturn codeText;\n\t}\n\n\tpublic void setCode(String string) {\n\t\tString previous = codeText;\n\t\tcodeText = string;\n\t\t// com.neuronrobotics.sdk.common.Log.error(codeText);\n\t\tfor (IScriptEventListener l : listeners) {\n\t\t\tl.onScriptChanged(previous, string, currentFile);\n\t\t}\n\t}\n\n\tpublic String getFileName() {\n\t\tif (currentFile != null)\n\t\t\treturn currentFile.getName();\n\t\telse\n\t\t\treturn \"Web\";\n\t}\n\n\t@Override\n\tpublic void changed(@SuppressWarnings(\"rawtypes\") ObservableValue observable, Object oldValue, Object newValue) {\n\t\tloadGitLocal(currentGit, (String) newValue);\n\t}\n\n\tpublic static void main(String[] args) {\n\t\tnew ScriptingWebWidget(ScriptingWidgetType.WEB).doFork();\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/scripting/ScriptingWidgetType.java",
    "content": "package com.neuronrobotics.bowlerstudio.scripting;\n\npublic enum ScriptingWidgetType {\n\tFILE, WEB, GIST, CREATURE\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/scripting/external/ArduinoExternalEditor.java",
    "content": "package com.neuronrobotics.bowlerstudio.scripting.external;\n\nimport static com.neuronrobotics.bowlerstudio.scripting.DownloadManager.*;\n\nimport java.io.File;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport com.neuronrobotics.bowlerstudio.assets.AssetFactory;\nimport com.neuronrobotics.bowlerstudio.scripting.ArduinoLoader;\nimport com.neuronrobotics.bowlerstudio.scripting.DownloadManager;\nimport com.neuronrobotics.bowlerstudio.scripting.IExternalEditor;\nimport javafx.scene.control.Button;\nimport javafx.scene.image.Image;\n\npublic class ArduinoExternalEditor implements IExternalEditor {\n\n\tprivate Button advanced;\n\n\t@Override\n\tpublic void launch(File file, Button advanced, Runnable OnComplete) {\n\t\tthis.advanced = advanced;\n\t\tnew Thread(() -> {\n\t\t\ttry {\n\t\t\t\tFile exe = DownloadManager.getRunExecutable(\"arduino2\", null);\n\t\t\t\tList<String> asList = Arrays.asList(exe.getAbsolutePath(), file.getAbsolutePath());\n\t\t\t\tif (isMac()) {\n\t\t\t\t\tasList = Arrays.asList(\"open\", \"-a\", exe.getAbsolutePath(), file.getAbsolutePath());\n\n\t\t\t\t}\n\t\t\t\tThread rthread = run(this, file.getParentFile(), System.err, asList);\n\t\t\t\ttry {\n\t\t\t\t\trthread.join();\n\t\t\t\t} catch (InterruptedException e) {\n\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\n\t\t\t} catch (Throwable t) {\n\t\t\t\tt.printStackTrace();\n\t\t\t}\n\t\t\tif (advanced != null)\n\t\t\t\tonProcessExit(0);\n\t\t}).start();\n\t}\n\n\tpublic Image getImage() {\n\t\ttry {\n\t\t\treturn AssetFactory.loadAsset(\"Script-Tab-Arduino.png\");\n\t\t} catch (Exception e) {\n\t\t\t// Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String nameOfEditor() {\n\t\t// Auto-generated method stub\n\t\treturn \"Arduino\";\n\t}\n\n\t@Override\n\tpublic URL getInstallURL() throws MalformedURLException {\n\t\ttry {\n\t\t\treturn new URI(\n\t\t\t\t\t\"https://github.com/WPIRoboticsEngineering/RobotInterfaceBoard/blob/master/InstallEclipse.md\")\n\t\t\t\t\t.toURL();\n\t\t} catch (URISyntaxException e) {\n\t\t\tthrow new MalformedURLException(e.getMessage());\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onProcessExit(int ev) {\n\t\tadvanced.setDisable(false);\n\t}\n\n\t@Override\n\tpublic List<Class> getSupportedLangauge() {\n\t\treturn Arrays.asList(ArduinoLoader.class);\n\t}\n\n\tpublic static void main(String[] args) throws Exception {\n\t\t/*\n\t\t * JavaFXInitializer.go(); ScriptingEngine.pull(\n\t\t * \"https://github.com/OperationSmallKat/LunaMotherboardFirmware.git\"); File f =\n\t\t * ScriptingEngine.fileFromGit(\n\t\t * \"https://github.com/OperationSmallKat/LunaMotherboardFirmware.git\",\n\t\t * \"LunaMotherboardFirmware.ino\");\n\t\t *\n\t\t * new ArduinoExternalEditor().launch(f, new javafx.scene.control.Button());\n\t\t */\n\t\t// File exe = DownloadManager.getRunExecutable(\"arduino2\", null);\n\t\t// File file = new File();\n\t\tString absolutePath = \"C:\\\\Users\\\\Kevin Bad Name\\\\bin\\\\\" + DownloadManager.getSTUDIO_INSTALL()\n\t\t\t\t+ \"\\\\arduino2\\\\Arduino IDE.exe\";// exe.getAbsolutePath();\n\t\trun(null, new File(\"C:\\\\Users\\\\Kevin Bad Name\\\\bin\\\\\" + DownloadManager.getSTUDIO_INSTALL() + \"\\\\arduino2\"),\n\t\t\t\tSystem.err, Arrays.asList(absolutePath,\n\t\t\t\t\t\t\"C:\\\\Users\\\\Kevin Bad Name\\\\Documents\\\\bowler-workspace\\\\gitcache\\\\github.com\\\\OperationSmallKat\\\\LunaMotherboardFirmware\\\\LunaMotherboardFirmware.ino\"));\n\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/scripting/external/BlenderExternalEditor.java",
    "content": "package com.neuronrobotics.bowlerstudio.scripting.external;\n\nimport static com.neuronrobotics.bowlerstudio.scripting.DownloadManager.*;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.eclipse.jgit.api.errors.GitAPIException;\nimport org.eclipse.jgit.api.errors.InvalidRemoteException;\nimport org.eclipse.jgit.api.errors.TransportException;\nimport org.eclipse.jgit.errors.NoWorkTreeException;\n\nimport com.neuronrobotics.bowlerstudio.BowlerStudio;\nimport com.neuronrobotics.bowlerstudio.assets.AssetFactory;\nimport com.neuronrobotics.bowlerstudio.scripting.AskToDeleteWidget;\nimport com.neuronrobotics.bowlerstudio.scripting.BlenderLoader;\nimport com.neuronrobotics.bowlerstudio.scripting.DownloadManager;\nimport com.neuronrobotics.bowlerstudio.scripting.IExternalEditor;\nimport com.neuronrobotics.bowlerstudio.scripting.ScriptingEngine;\nimport com.neuronrobotics.bowlerstudio.scripting.StlLoader;\nimport com.neuronrobotics.sdk.common.Log;\n\nimport eu.mihosoft.vrl.v3d.JavaFXInitializer;\nimport eu.mihosoft.vrl.v3d.parametrics.CSGDatabase;\nimport javafx.scene.control.Button;\nimport javafx.scene.image.Image;\n\npublic class BlenderExternalEditor implements IExternalEditor {\n\n\tprivate Button advanced;\n\n\t@Override\n\tpublic void launch(File file, Button advanced, Runnable OnComplete) {\n\t\tnew Thread(() -> {\n\t\t\tthis.advanced = advanced;\n\t\t\tString filename = file.getAbsolutePath();\n\t\t\tFile dir = new File(filename).getParentFile();\n\t\t\tFile exe = DownloadManager.getRunExecutable(\"blender\", null);\n\n\t\t\tif (filename.toLowerCase().endsWith(\".stl\")) {\n\n\t\t\t\tFile blenderfile = new File(dir.getAbsolutePath() + delim() + file.getName() + \".blend\");\n\t\t\t\tif (blenderfile.exists())\n\t\t\t\t\tif (AskToDeleteWidget.askToDeleteFile(blenderfile.getName())) {\n\t\t\t\t\t\tblenderfile.delete();\n\t\t\t\t\t}\n\t\t\t\tBlenderLoader.toBlenderFile(CSGDatabase.getInstance(), file, blenderfile);\n\t\t\t\tfilename = blenderfile.getAbsolutePath();\n\t\t\t\ttry {\n\t\t\t\t\tBowlerStudio.createFileTab(blenderfile);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (filename.toLowerCase().endsWith(\".stl\") || !new File(filename).exists()) {\n\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"ERROR blender conversion failed!\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tList<String> asList = Arrays.asList(exe.getAbsolutePath(), filename);\n\t\t\t\tif (isMac()) {\n\t\t\t\t\tasList = Arrays.asList(\"open\", \"-a\", exe.getAbsolutePath(), filename);\n\n\t\t\t\t}\n\t\t\t\tThread t = run(this, dir, System.out, asList);\n\t\t\t\tt.join();\n\t\t\t} catch (NoWorkTreeException e) {\n\t\t\t\t// Auto-generated catch block\n\t\t\t\te.printStackTrace();\n\t\t\t} catch (Exception e) {\n\t\t\t\t// Auto-generated catch block\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t\tonProcessExit(0);\n\t\t\ttry {\n\t\t\t\tOnComplete.run();\n\t\t\t} catch (Throwable t) {\n\t\t\t\tLog.error(t);\n\t\t\t}\n\n\t\t}).start();\n\t}\n\n\tpublic void onProcessExit(int ev) {\n\t\tadvanced.setDisable(false);\n\t}\n\n\t@Override\n\tpublic URL getInstallURL() throws MalformedURLException {\n\t\ttry {\n\t\t\treturn new URI(\"https://www.blender.org/download/release/Blender4.1/blender-4.1.1-linux-x64.tar.xz/\")\n\t\t\t\t\t.toURL();\n\t\t} catch (URISyntaxException e) {\n\t\t\tthrow new MalformedURLException(e.getMessage());\n\t\t}\n\t}\n\n\t@Override\n\tpublic String nameOfEditor() {\n\t\treturn \"Blender\";\n\t}\n\n\tpublic Image getImage() {\n\t\ttry {\n\t\t\treturn AssetFactory.loadAsset(\"Blender.png\");\n\t\t} catch (Exception e) {\n\t\t\t// Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic static void main(String[] args)\n\t\t\tthrows InvalidRemoteException, TransportException, GitAPIException, IOException {\n\t\tJavaFXInitializer.go();\n\t\tFile f = ScriptingEngine.fileFromGit(\"https://github.com/NeuronRobotics/NASACurisoity.git\",\n\t\t\t\t\"STL/upper-arm.STL\");\n\n\t\tnew BlenderExternalEditor().launch(f, new Button(), () -> {\n\t\t});\n\t}\n\n\t@Override\n\tpublic List<Class> getSupportedLangauge() {\n\t\treturn Arrays.asList(StlLoader.class, BlenderLoader.class);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/scripting/external/CaDoodleExternalEditor.java",
    "content": "package com.neuronrobotics.bowlerstudio.scripting.external;\n\nimport static com.neuronrobotics.bowlerstudio.scripting.DownloadManager.*;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\n\nimport org.eclipse.jgit.api.errors.GitAPIException;\nimport org.eclipse.jgit.api.errors.InvalidRemoteException;\nimport org.eclipse.jgit.api.errors.TransportException;\nimport org.eclipse.jgit.errors.NoWorkTreeException;\n\nimport com.neuronrobotics.bowlerstudio.assets.AssetFactory;\nimport com.neuronrobotics.bowlerstudio.scripting.CaDoodleLoader;\nimport com.neuronrobotics.bowlerstudio.scripting.DownloadManager;\nimport com.neuronrobotics.bowlerstudio.scripting.IExternalEditor;\nimport com.neuronrobotics.bowlerstudio.scripting.ScriptingEngine;\nimport com.neuronrobotics.video.OSUtil;\n\nimport eu.mihosoft.vrl.v3d.JavaFXInitializer;\nimport javafx.scene.control.Button;\nimport javafx.scene.image.Image;\n\npublic class CaDoodleExternalEditor implements IExternalEditor {\n\n\tprivate Button advanced;\n\n\tpublic void launch(File file, Button advanced, Runnable onExit) {\n\t\tnew Thread(() -> {\n\t\t\tthis.advanced = advanced;\n\t\t\tString filename = \"\\\"\" + file.getAbsolutePath() + \"\\\"\";\n\n\t\t\ttry {\n\t\t\t\tFile dir = file.getAbsoluteFile().getParentFile();\n\t\t\t\tFile exe;\n\t\t\t\tif (OSUtil.isOSX()) {\n\t\t\t\t\texe = DownloadManager.getConfigExecutable(\"cadoodle\", null);\n\t\t\t\t} else {\n\t\t\t\t\texe = DownloadManager.getRunExecutable(\"cadoodle\", null);\n\t\t\t\t}\n\n\t\t\t\tList<String> asList = Arrays.asList(exe.getAbsolutePath(), filename);\n\n\t\t\t\tDownloadManager.legacySystemRun(new HashMap<String, String>(), dir, System.err, asList);\n\n\t\t\t} catch (NoWorkTreeException e) {\n\t\t\t\t// Auto-generated catch block\n\t\t\t\te.printStackTrace();\n\t\t\t} catch (Exception e) {\n\t\t\t\t// Auto-generated catch block\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t\tonProcessExit(0);\n\t\t\tonExit.run();\n\n\t\t}).start();\n\t}\n\n\tpublic void onProcessExit(int ev) {\n\t\tadvanced.setDisable(false);\n\n\t}\n\n\t@Override\n\tpublic URL getInstallURL() throws MalformedURLException {\n\t\ttry {\n\t\t\treturn new URI(\"https://github.com/CommonWealthRobotics/CaDoodle/blob/main/README.md\").toURL();\n\t\t} catch (URISyntaxException e) {\n\t\t\tthrow new MalformedURLException(e.getMessage());\n\t\t}\n\t}\n\n\tpublic String nameOfEditor() {\n\t\treturn \"CaDoodle\";\n\t}\n\n\tpublic Image getImage() {\n\t\ttry {\n\t\t\tImage asset = AssetFactory.loadAsset(\"Script-Tab-CaDoodle.png\");\n\t\t\treturn asset;\n\t\t} catch (Exception e) {\n\t\t\t// Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic static void main(String[] args)\n\t\t\tthrows InvalidRemoteException, TransportException, GitAPIException, IOException {\n\t\tJavaFXInitializer.go();\n\t\tString url = \"https://github.com/madhephaestus/TestRepo.git\";\n\t\tScriptingEngine.pull(\"https://github.com/CommonWealthRobotics/ExternalEditorsBowlerStudio.git\");\n\t\tScriptingEngine.pull(url);\n\t\tFile f = ScriptingEngine.fileFromGit(url, \"Doodle1/TestRepo.doodle\");\n\n\t\tnew CaDoodleExternalEditor().launch(f, new Button(), () -> {\n\t\t});\n\t}\n\n\tpublic List<Class> getSupportedLangauge() {\n\t\treturn Arrays.asList(CaDoodleLoader.class);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/scripting/external/EclipseExternalEditor.java",
    "content": "package com.neuronrobotics.bowlerstudio.scripting.external;\n\nimport static com.neuronrobotics.bowlerstudio.scripting.DownloadManager.*;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.io.RandomAccessFile;\nimport java.io.Reader;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.nio.channels.FileLock;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.Paths;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.apache.commons.io.FileUtils;\nimport org.apache.commons.io.IOUtils;\nimport org.eclipse.jgit.lib.Repository;\n\nimport com.neuronrobotics.bowlerstudio.assets.AssetFactory;\nimport com.neuronrobotics.bowlerstudio.scripting.DownloadManager;\nimport com.neuronrobotics.bowlerstudio.scripting.IExternalEditor;\nimport com.neuronrobotics.bowlerstudio.scripting.ScriptingEngine;\nimport com.neuronrobotics.video.OSUtil;\n\nimport javafx.scene.control.Button;\nimport javafx.scene.image.Image;\n\npublic abstract class EclipseExternalEditor implements IExternalEditor {\n\n\tprotected Button advanced;\n\tprivate File dir;\n\tprivate String name;\n\n\tprotected abstract void setUpEclipseProjectFiles(File dir, File project, String name)\n\t\t\tthrows IOException, MalformedURLException;\n\n\tprotected abstract boolean checkForExistingProjectFiles(File dir);\n\n\tprotected static String readAll(Reader rd) throws IOException {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tint cp;\n\t\twhile ((cp = rd.read()) != -1) {\n\t\t\tsb.append((char) cp);\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\tpublic Image getImage() {\n\t\ttry {\n\t\t\tImage loadAsset = AssetFactory.loadAsset(\"eclipse.png\");\n\t\t\treturn loadAsset;\n\t\t} catch (Exception e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t}\n\n\tprotected boolean OSSupportsEclipse() {\n\t\treturn OSUtil.isLinux() || OSUtil.isWindows() || OSUtil.isOSX();\n\t}\n\n\tprivate String sanitize(String s) {\n\t\tif (OSUtil.isWindows()) {\n\t\t\treturn \"\\\"\" + s + \"\\\"\";\n\t\t}\n\t\treturn s;\n\t}\n\n\t@Override\n\tpublic void launch(File file, Button advanced, Runnable OnComplete) {\n\t\tthis.advanced = advanced;\n\t\tEclipseExternalEditor ee = this;\n\t\tnew Thread(() -> {\n\t\t\t// File exeFile = getExecutable(\"eclipse\",null);\n\t\t\t// String eclipseEXE = exeFile.getAbsolutePath();\n\n\t\t\ttry {\n\t\t\t\tdir = file.getParentFile();\n\t\t\t\tname = dir.getName();\n\t\t\t\tFile project = new File(dir.getAbsolutePath() + delim() + \".project\");\n\t\t\t\ttry {\n\t\t\t\t\tScriptingEngine.locateGit(file, git -> {\n\t\t\t\t\t\tRepository repository = git.getRepository();\n\t\t\t\t\t\tdir = repository.getWorkTree();\n\t\t\t\t\t\tname = dir.getName();\n\t\t\t\t\t});\n\t\t\t\t\tString remoteURL = ScriptingEngine.locateGitUrlString(file);\n\t\t\t\t\tString branch = ScriptingEngine.getBranch(remoteURL);\n\n\t\t\t\t\tFile ignore = new File(dir.getAbsolutePath() + delim() + \".gitignore\");\n\t\t\t\t\tproject = new File(dir.getAbsolutePath() + delim() + \".project\");\n\n\t\t\t\t\tif (dir.getAbsolutePath().contains(\"gist.github.com\")) {\n\t\t\t\t\t\tString name2 = file.getName();\n\t\t\t\t\t\tString[] split = name2.split(\"\\\\.\");\n\t\t\t\t\t\tname = split[0];\n\t\t\t\t\t}\n\t\t\t\t\tif (!ignore.exists() || !project.exists() || !checkForExistingProjectFiles(dir)) {\n\t\t\t\t\t\tString content = \"\";\n\t\t\t\t\t\tString toIgnore = \"\\n/.settings\\n\" + \"/.project\\n\" + \"/.classpath\\n\" + \"/.cproject\\n\"\n\t\t\t\t\t\t\t\t+ \"/cache/\\n\" + \"/*.class\";\n\n\t\t\t\t\t\tif (ignore.exists())\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tcontent = new String(Files.readAllBytes(Paths.get(ignore.getAbsolutePath())));\n\t\t\t\t\t\t\t} catch (IOException e) {\n\t\t\t\t\t\t\t\te.printStackTrace();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!content.contains(toIgnore)) {\n\t\t\t\t\t\t\tcontent += toIgnore;\n\t\t\t\t\t\t\tif (ScriptingEngine.checkOwner(remoteURL)) {\n\t\t\t\t\t\t\t\tScriptingEngine.pushCodeToGit(remoteURL, branch, \".gitignore\", content,\n\t\t\t\t\t\t\t\t\t\t\"Ignore the project files\");\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tcontent += \"\\n.gitignore\\n\";\n\t\t\t\t\t\t\t\tFile gistDir = ScriptingEngine.cloneRepo(remoteURL, branch);\n\t\t\t\t\t\t\t\tFile desired = new File(gistDir.getAbsoluteFile() + \"/\" + \".gitignore\");\n\t\t\t\t\t\t\t\tOutputStream out = null;\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tout = FileUtils.openOutputStream(desired, false);\n\t\t\t\t\t\t\t\t\tIOUtils.write(content, out, \"UTF-8\");\n\t\t\t\t\t\t\t\t\tout.close(); // don't swallow close Exception if copy completes\n\t\t\t\t\t\t\t\t\t// normally\n\t\t\t\t\t\t\t\t} finally {\n\t\t\t\t\t\t\t\t\tIOUtils.closeQuietly(out);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsetUpEclipseProjectFiles(dir, project, name);\n\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\tex.printStackTrace();\n\t\t\t\t\t// if(!project.exists() )\n\t\t\t\t\tsetUpEclipseProjectFiles(dir, project, name);\n\t\t\t\t}\n\n\t\t\t\tString ws = getEclipseWorkspace();\n\n\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Opening workspace \" + ws);\n\t\t\t\tFile wsDir = new File(ws);\n\t\t\t\tMap<String, String> env = getEnvironment(\"eclipse\");\n\t\t\t\tHashMap<String, String> environment = new HashMap<>();;\n\t\t\t\tenvironment.putAll(env);\n\t\t\t\tFile settings = new File(ScriptingEngine.getWorkspace().getAbsolutePath() + delim() + \"appdata\"\n\t\t\t\t\t\t+ delim() + \"bowler-settings.epf\");\n\t\t\t\tFile java = DownloadManager.getConfigExecutable(\"java25\", null);\n\n\t\t\t\tif (!wsDir.exists()) {\n\t\t\t\t\tFile prefssource = ScriptingEngine.fileFromGit(\n\t\t\t\t\t\t\t\"https://github.com/CommonWealthRobotics/ExternalEditorsBowlerStudio.git\",\n\t\t\t\t\t\t\t\"settingsTEMPLATE.epf\");\n\t\t\t\t\ttry {\n\t\t\t\t\t\tString template = FileUtils.readFileToString(prefssource, StandardCharsets.UTF_8);\n\t\t\t\t\t\ttemplate = template.replace(\"MYWORKSPACES\", eclipseSanatize(getEclipseWorkspace()));\n\t\t\t\t\t\ttemplate = template.replace(\"MYJAVAHOME\", eclipseSanatize(java.getAbsolutePath()));\n\t\t\t\t\t\tFileUtils.write(settings, template, StandardCharsets.UTF_8);\n\t\t\t\t\t} catch (IOException e) {\n\t\t\t\t\t\te.printStackTrace();\n\t\t\t\t\t}\n\t\t\t\t\tenvironment.put(\"ECLIPSE_PREFERENCE_FILE\", settings.getAbsolutePath());\n\t\t\t\t}\n\t\t\t\tenvironment.put(\"JAVA_HOME\", java.getAbsolutePath());\n\t\t\t\tif (!isEclipseOpen(ws)) {\n\t\t\t\t\tFile exeFile = getConfigExecutable(\"eclipse\", null);\n\t\t\t\t\tString eclipseEXE = exeFile.getAbsolutePath();\n\t\t\t\t\trun(environment, this, ScriptingEngine.getWorkspace(), System.out,\n\t\t\t\t\t\t\tArrays.asList(eclipseEXE, \"-data\", ws));\n\t\t\t\t\twhile (!isEclipseOpen(ws)) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tThread.sleep(5000);\n\t\t\t\t\t\t} catch (InterruptedException e) {\n\t\t\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\t\t\te.printStackTrace();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcom.neuronrobotics.sdk.common.Log\n\t\t\t\t\t\t\t\t.error(\"Waiting for workspace, please wait until it opens \" + ws);\n\t\t\t\t\t\t// return;\n\t\t\t\t\t}\n\t\t\t\t\tthis.onProcessExit(0);\n\t\t\t\t} else {\n\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Eclipse is already open at \" + ws);\n\t\t\t\t}\n\n\t\t\t\tFile projects = new File(ws + delim() + \".metadata\" + delim() + \".plugins\" + delim()\n\t\t\t\t\t\t+ \"org.eclipse.core.resources\" + delim() + \".projects\" + delim());\n\t\t\t\t// For each pathname in the pathnames array\n\t\t\t\tif (projects.exists()) {\n\t\t\t\t\tfor (String pathname : projects.list()) {\n\t\t\t\t\t\tif (pathname.endsWith(name) || pathname.endsWith(dir.getName())) {\n\t\t\t\t\t\t\tcom.neuronrobotics.sdk.common.Log\n\t\t\t\t\t\t\t\t\t.error(\"Project \" + name + \" is already in the workspace!\");\n\t\t\t\t\t\t\tadvanced.setDisable(false);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tFile exeFile = getRunExecutable(\"eclipse\", null);\n\t\t\t\tString eclipseEXE = exeFile.getAbsolutePath();\n\t\t\t\tif (OSUtil.isOSX()) {\n\t\t\t\t\tFile app = exeFile;\n\t\t\t\t\twhile (!app.getName().toLowerCase().endsWith(\".app\")) {\n\t\t\t\t\t\tapp = app.getParentFile();\n\t\t\t\t\t}\n\n\t\t\t\t\trun(environment, this, dir, System.err,\n\t\t\t\t\t\t\tArrays.asList(\"open\", \"-a\", app.getAbsolutePath(), dir.getAbsolutePath()));\n\t\t\t\t} else\n\t\t\t\t\trun(environment, this, dir, System.err, Arrays.asList(eclipseEXE, dir.getAbsolutePath()));\n\t\t\t} catch (Exception e) {\n\t\t\t\t// Auto-generated catch block\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t}).start();\n\t}\n\n\tprivate CharSequence eclipseSanatize(String absolutePath) {\n\t\tif (isWin()) {\n\t\t\t// this replaces one slash with 2 slashes, just trust me\n\t\t\tabsolutePath = absolutePath.replaceAll(\"\\\\\\\\\", \"\\\\\\\\\\\\\\\\\");\n\t\t\tabsolutePath = absolutePath.replaceAll(\":\", \"\\\\\\\\:\");\n\t\t}\n\t\treturn absolutePath;\n\t}\n\n\tpublic static String getEclipseWorkspace() {\n\t\treturn ScriptingEngine.getWorkspace().getAbsolutePath() + delim() + \"eclipse-bowler-workspace\";\n\t}\n\n\tprivate boolean isEclipseOpen(String ws) {\n\t\tif (!new File(ws).exists())\n\t\t\treturn false;\n\t\tString lockFile = ws + delim() + \".metadata\" + delim() + \".lock\";\n\t\tcom.neuronrobotics.sdk.common.Log.error(\"Checking WS \" + lockFile);\n\t\tFile lock = new File(lockFile);\n\t\tif (!lock.exists())\n\t\t\treturn false;\n\n\t\ttry {\n\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Attempting to test workspace lockfile...\");\n\t\t\tRandomAccessFile raFile = new RandomAccessFile(lock.getAbsoluteFile(), \"rw\");\n\t\t\tFileLock fileLock = raFile.getChannel().tryLock(0, 1, false);\n\t\t\tif (fileLock == null) {\n\t\t\t\traFile.close();\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tfileLock.release();\n\t\t\traFile.close();\n\n\t\t} catch (Exception ex) {\n\t\t\t// lock failed eclipse is open already\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic String nameOfEditor() {\n\n\t\treturn \"Eclipse\";\n\t}\n\n\t@Override\n\tpublic URL getInstallURL() throws MalformedURLException {\n\t\ttry {\n\t\t\treturn new URI(\"https://github.com/CommonWealthRobotics/ESP32ArduinoEclipseInstaller/blob/master/README.md\")\n\t\t\t\t\t.toURL();\n\t\t} catch (URISyntaxException e) {\n\t\t\tthrow new MalformedURLException(e.getMessage());\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/scripting/external/ExternalEditorController.java",
    "content": "package com.neuronrobotics.bowlerstudio.scripting.external;\n\nimport java.io.File;\nimport java.util.ArrayList;\n\nimport com.neuronrobotics.bowlerstudio.assets.AssetFactory;\nimport com.neuronrobotics.bowlerstudio.assets.FontSizeManager;\nimport com.neuronrobotics.bowlerstudio.scripting.IExternalEditor;\nimport com.neuronrobotics.bowlerstudio.scripting.ScriptingEngine;\n\nimport javafx.scene.Node;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.CheckBox;\nimport javafx.scene.control.Tooltip;\nimport javafx.scene.image.Image;\nimport javafx.scene.image.ImageView;\n\npublic class ExternalEditorController {\n\tprivate File currentFile;\n\tboolean hasEditor = false;\n\tprivate Button advanced = new Button();\n\tprivate ImageView image = new ImageView();\n\tprivate ArrayList<IExternalEditor> editors = new ArrayList<IExternalEditor>();\n\n\tprivate void loadEditors() {\n\t\teditors.add(new SVGExternalEditor());\n\t\teditors.add(new GroovyEclipseExternalEditor());\n\t\teditors.add(new ArduinoExternalEditor());\n\t\teditors.add(new BlenderExternalEditor());\n\t\teditors.add(new FreeCADExternalEditor());\n\t\teditors.add(new SceneBuilderExternalEditor());\n\t\teditors.add(new OpenSCADExternalEditor());\n\t\teditors.add(new CaDoodleExternalEditor());\n\t}\n\tprivate IExternalEditor myEditor = null;\n\tpublic ExternalEditorController(File f, CheckBox autoRun, Runnable OnComplete) {\n\t\tloadEditors();\n\t\tthis.currentFile = f;\n\t\tfor (IExternalEditor e : editors) {\n\t\t\tif (e.isSupportedByExtension(f)) {\n\t\t\t\thasEditor = true;\n\t\t\t\tmyEditor = e;\n\t\t\t\timage.setImage(e.getImage());\n\t\t\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\t\t\timage.setScaleX(FontSizeManager.getImageScale());\n\t\t\t\t\timage.setScaleY(FontSizeManager.getImageScale());\n\t\t\t\t});\n\t\t\t\tcom.neuronrobotics.sdk.common.Log\n\t\t\t\t\t\t.debug(\"ExternalEditorController: FOUND \" + f.getName() + \" is supported by \" + e.getClass());\n\t\t\t\tbreak;\n\t\t\t} else {\n\t\t\t\t// com.neuronrobotics.sdk.common.Log.debug(\":ExternalEditorController:\n\t\t\t\t// \"+f.getName()+\" is not supported by \"+e.getClass());\n\t\t\t}\n\n\t\t}\n\t\tif (hasEditor) {\n\n\t\t\tadvanced.setGraphic(image);\n\t\t\tadvanced.setTooltip(new Tooltip(\n\t\t\t\t\t\"Click here to launch \" + myEditor.nameOfEditor() + \" the advanced editor for this file\"));\n\t\t\tadvanced.setText(myEditor.nameOfEditor());\n\t\t\tadvanced.setMinWidth(100);\n\t\t\tadvanced.setTextOverrun(javafx.scene.control.OverrunStyle.CLIP);\n\t\t\tadvanced.setOnAction(event -> {\n\t\t\t\tadvanced.setDisable(true);\n\t\t\t\tmyEditor.launch(currentFile, advanced, OnComplete);\n\t\t\t\t// autoRun.setSelected(true);\n\t\t\t});\n\t\t\t// FontSizeManager.addListener(fontNum->{\n\t\t\t// advanced.setScaleX(FontSizeManager.getImageScale());\n\t\t\t// advanced.setScaleY(FontSizeManager.getImageScale());\n\t\t\t// });\n\t\t} else {\n\t\t\ttry {\n\t\t\t\tImage loadAsset = AssetFactory\n\t\t\t\t\t\t.loadAsset(\"Script-Tab-\" + ScriptingEngine.getShellType(currentFile.getName()) + \".png\");\n\t\t\t\timage.setImage(loadAsset);\n\t\t\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\t\t\timage.setScaleX(FontSizeManager.getImageScale());\n\t\t\t\t\timage.setScaleY(FontSizeManager.getImageScale());\n\t\t\t\t});\n\t\t\t} catch (Exception e) {\n\t\t\t\t// Auto-generated catch block\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t}\n\t\timage.setFitHeight(30);\n\t\timage.setFitWidth(30);\n\n\t}\n\n\tpublic Node getControl() {\n\t\tif (hasEditor)\n\t\t\treturn advanced;\n\t\telse\n\t\t\treturn image;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/scripting/external/FreeCADExternalEditor.java",
    "content": "package com.neuronrobotics.bowlerstudio.scripting.external;\n\nimport java.io.File;\nimport java.net.MalformedURLException;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.net.URL;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport com.neuronrobotics.bowlerstudio.assets.AssetFactory;\nimport com.neuronrobotics.bowlerstudio.scripting.FreecadLoader;\nimport com.neuronrobotics.bowlerstudio.scripting.IExternalEditor;\nimport com.neuronrobotics.sdk.common.Log;\n\nimport javafx.application.Platform;\nimport javafx.scene.control.Button;\nimport javafx.scene.image.Image;\n\npublic class FreeCADExternalEditor implements IExternalEditor {\n\tprivate Button advanced;\n\n\t@Override\n\tpublic List<Class> getSupportedLangauge() {\n\t\treturn Arrays.asList(FreecadLoader.class);\n\t}\n\n\t@Override\n\tpublic void launch(File file, Button button, Runnable OnComplete) {\n\t\tadvanced = button;\n\t\tnew Thread(() -> {\n\t\t\ttry {\n\t\t\t\tFreecadLoader.open(file);\n\t\t\t} catch (Throwable t) {\n\t\t\t\tLog.error(t);\n\t\t\t\tonProcessExit(-1);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tonProcessExit(0);\n\t\t}).start();\n\t}\n\n\t@Override\n\tpublic String nameOfEditor() {\n\t\treturn \"FreeCAD\";\n\t}\n\n\t@Override\n\tpublic URL getInstallURL() throws MalformedURLException {\n\t\ttry {\n\t\t\treturn new URI(\"https://github.com/FreeCAD/FreeCAD-Bundle/releases\").toURL();\n\t\t} catch (URISyntaxException e) {\n\t\t\tthrow new MalformedURLException(e.getMessage());\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onProcessExit(int ev) {\n\t\tif (advanced != null)\n\t\t\tPlatform.runLater(() -> advanced.setDisable(false));\n\t}\n\n\t@Override\n\tpublic Image getImage() {\n\t\ttry {\n\t\t\treturn AssetFactory.loadAsset(\"Script-Tab-FreeCAD.png\");\n\t\t} catch (Exception e) {\n\t\t\t// Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/scripting/external/GroovyEclipseExternalEditor.java",
    "content": "package com.neuronrobotics.bowlerstudio.scripting.external;\n\nimport java.io.File;\nimport java.io.FileNotFoundException;\n\nimport com.neuronrobotics.bowlerstudio.BowlerStudio;\nimport com.neuronrobotics.bowlerstudio.scripting.BashLoader;\nimport com.neuronrobotics.bowlerstudio.scripting.DownloadManager;\nimport com.neuronrobotics.bowlerstudio.scripting.GroovyHelper;\nimport com.neuronrobotics.bowlerstudio.scripting.JsonRunner;\nimport com.neuronrobotics.bowlerstudio.scripting.RobotHelper;\nimport com.neuronrobotics.bowlerstudio.scripting.ScriptingEngine;\n\nimport eu.mihosoft.vrl.v3d.JavaFXInitializer;\n\nimport java.io.IOException;\nimport java.net.MalformedURLException;\nimport java.nio.file.Files;\nimport java.nio.file.Paths;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport static com.neuronrobotics.bowlerstudio.scripting.DownloadManager.*;\n\npublic class GroovyEclipseExternalEditor extends EclipseExternalEditor {\n\n\tpublic void onProcessExit(int ev) {\n\t\tadvanced.setDisable(false);\n\t}\n\n\tprotected void setUpEclipseProjectFiles(File dir, File project, String name)\n\t\t\tthrows IOException, MalformedURLException {\n\n\t\tFile classpath = new File(dir.getAbsolutePath() + delim() + \".classpath\");\n\t\tString ProjectContent = \"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?>\\n\" + \"<projectDescription>\\n\" + \"\t<name>\"\n\t\t\t\t+ name + \"</name>\\n\" + \"\t<comment></comment>\\n\" + \"\t<projects>\\n\" + \"\t</projects>\\n\"\n\t\t\t\t+ \"\t<buildSpec>\\n\" + \"\t\t<buildCommand>\\n\"\n\t\t\t\t+ \"\t\t\t<name>org.eclipse.jdt.core.javabuilder</name>\\n\" + \"\t\t\t<arguments>\\n\"\n\t\t\t\t+ \"\t\t\t</arguments>\\n\" + \"\t\t</buildCommand>\\n\" + \"\t</buildSpec>\\n\" + \"\t<natures>\\n\"\n\t\t\t\t+ \"\t\t<nature>org.eclipse.jdt.groovy.core.groovyNature</nature>\\n\"\n\t\t\t\t+ \"\t\t<nature>org.eclipse.jdt.core.javanature</nature>\\n\" + \"\t</natures>\\n\" + \"</projectDescription>\";\n\t\tString java8Prefs = \"eclipse.preferences.version=1\\n\" + \"org.eclipse.jdt.core.builder.cleanOutputFolder=clean\\n\"\n\t\t\t\t+ \"org.eclipse.jdt.core.builder.duplicateResourceTask=warning\\n\"\n\t\t\t\t+ \"org.eclipse.jdt.core.builder.invalidClasspath=abort\\n\"\n\t\t\t\t+ \"org.eclipse.jdt.core.builder.recreateModifiedClassFileInOutputFolder=ignore\\n\"\n\t\t\t\t+ \"org.eclipse.jdt.core.builder.resourceCopyExclusionFilter=*.launch\\n\"\n\t\t\t\t+ \"org.eclipse.jdt.core.circularClasspath=error\\n\"\n\t\t\t\t+ \"org.eclipse.jdt.core.classpath.exclusionPatterns=enabled\\n\"\n\t\t\t\t+ \"org.eclipse.jdt.core.classpath.mainOnlyProjectHasTestOnlyDependency=error\\n\"\n\t\t\t\t+ \"org.eclipse.jdt.core.classpath.multipleOutputLocations=enabled\\n\"\n\t\t\t\t+ \"org.eclipse.jdt.core.classpath.outputOverlappingAnotherSource=error\\n\"\n\t\t\t\t+ \"org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled\\n\"\n\t\t\t\t+ \"org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate\\n\"\n\t\t\t\t+ \"org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8\\n\"\n\t\t\t\t+ \"org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve\\n\"\n\t\t\t\t+ \"org.eclipse.jdt.core.compiler.compliance=1.8\\n\"\n\t\t\t\t+ \"org.eclipse.jdt.core.compiler.debug.lineNumber=generate\\n\"\n\t\t\t\t+ \"org.eclipse.jdt.core.compiler.debug.localVariable=generate\\n\"\n\t\t\t\t+ \"org.eclipse.jdt.core.compiler.debug.sourceFile=generate\\n\"\n\t\t\t\t+ \"org.eclipse.jdt.core.compiler.maxProblemPerUnit=100\\n\"\n\t\t\t\t+ \"org.eclipse.jdt.core.compiler.problem.assertIdentifier=error\\n\"\n\t\t\t\t+ \"org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled\\n\"\n\t\t\t\t+ \"org.eclipse.jdt.core.compiler.problem.enumIdentifier=error\\n\"\n\t\t\t\t+ \"org.eclipse.jdt.core.compiler.problem.incompatibleJDKLevel=ignore\\n\"\n\t\t\t\t+ \"org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning\\n\"\n\t\t\t\t+ \"org.eclipse.jdt.core.compiler.release=disabled\\n\" + \"org.eclipse.jdt.core.compiler.source=1.8\\n\"\n\t\t\t\t+ \"org.eclipse.jdt.core.incompatibleJDKLevel=ignore\\n\"\n\t\t\t\t+ \"org.eclipse.jdt.core.incompleteClasspath=error\" + \"\\n\";\n\t\tString launchPrefs = \"eclipse.preferences.version=1\\n\"\n\t\t\t\t+ \"org.eclipse.jdt.launching.PREF_COMPILER_COMPLIANCE_DOES_NOT_MATCH_JRE=warning\\n\"\n\t\t\t\t+ \"org.eclipse.jdt.launching.PREF_STRICTLY_COMPATIBLE_JRE_NOT_AVAILABLE=warning\\n\";\n\t\tFile file = new File(dir.getAbsolutePath() + delim() + \".settings\");\n\t\tif (!file.exists())\n\t\t\tfile.mkdirs();\n\t\tFiles.write(Paths.get(file.getAbsolutePath() + delim() + \"org.eclipse.jdt.core.prefs\"), java8Prefs.getBytes());\n\t\tFiles.write(Paths.get(file.getAbsolutePath() + delim() + \"org.eclipse.jdt.launching.prefs\"),\n\t\t\t\tlaunchPrefs.getBytes());\n\n\t\tFiles.write(Paths.get(project.getAbsolutePath()), ProjectContent.getBytes());\n\t\t// String latestVersionString = \"1.12.0\";\n\t\t// InputStream is = new URL(\n\t\t// \"https://api.github.com/repos/CommonWealthRobotics/BowlerStudio/releases/latest\")\n\t\t// .openStream();\n\t\t// try {\n\t\t// BufferedReader rd = new BufferedReader(new InputStreamReader(is,\n\t\t// Charset.forName(\"UTF-8\")));\n\t\t// String jsonText = readAll(rd);\n\t\t// // Create the type, this tells GSON what datatypes to instantiate when\n\t\t// parsing\n\t\t// // and saving the json\n\t\t// Type TT_mapStringString = new TypeToken<HashMap<String, Object>>() {\n\t\t// }.getType();\n\t\t// // chreat the gson object, this is the parsing factory\n\t\t// Gson gson = new\n\t\t// GsonBuilder().disableHtmlEscaping().setPrettyPrinting().create();\n\t\t// HashMap<String, Object> database = gson.fromJson(jsonText,\n\t\t// TT_mapStringString);\n\t\t// latestVersionString = (String) database.get(\"tag_name\");\n\t\t// } finally {\n\t\t// is.close();\n\t\t// }\n\t\t// latestVersionString = BowlerStudio.getBowlerStudioBinaryVersion();\n\n\t\tString jar = getApplicationJarPath();\n\n\t\tString classpathContent = \"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?>\\n\" + \"<classpath>\\n\"\n\t\t\t\t+ \"\t<classpathentry kind=\\\"src\\\" path=\\\"\\\"/>\\n\"\n\t\t\t\t+ \"\t<classpathentry kind=\\\"con\\\" path=\\\"org.eclipse.jdt.launching.JRE_CONTAINER\\\">\\n\"\n\t\t\t\t+ \"\t\t<attributes>\\n\" + \"\t\t\t<attribute name=\\\"module\\\" value=\\\"true\\\"/>\\n\"\n\t\t\t\t+ \"\t\t</attributes>\\n\" + \"\t</classpathentry>\\n\"\n\t\t\t\t+ \"\t<classpathentry kind=\\\"con\\\" path=\\\"GROOVY_DSL_SUPPORT\\\"/>\\n\"\n\t\t\t\t+ \"\t<classpathentry kind=\\\"lib\\\" path=\\\"\" + jar + \"\\\"/>\\n\"\n\t\t\t\t+ \"\t<classpathentry kind=\\\"output\\\" path=\\\"\\\"/>\\n\" + \"</classpath>\";\n\t\tFiles.write(Paths.get(classpath.getAbsolutePath()), classpathContent.getBytes());\n\n\t}\n\n\tpublic static String getApplicationJarPath() throws FileNotFoundException {\n\t\treturn getApplicationBinaryDirectory() + getSTUDIO_JAR();\n\t}\n\n\tpublic static void main(String[] args) throws Exception {\n\t\tJavaFXInitializer.go();\n\t\tFile f = ScriptingEngine.fileFromGit(\"https://gist.github.com/e4b0d8e95d6b3dc83c334a9950753a53.git\",\n\t\t\t\t\"jabber.groovy\");\n\n\t\tnew GroovyEclipseExternalEditor().launch(f, new javafx.scene.control.Button(), () -> {\n\t\t});\n\t}\n\n\t@Override\n\tprotected boolean checkForExistingProjectFiles(File dir) {\n\t\tFile classpath = new File(dir.getAbsolutePath() + delim() + \".classpath\");\n\t\treturn classpath.exists();\n\t}\n\n\t@Override\n\tpublic List<Class> getSupportedLangauge() {\n\t\treturn Arrays.asList(GroovyHelper.class, BashLoader.class, JsonRunner.class, RobotHelper.class);\n\t}\n\n\tpublic static String getSTUDIO_JAR() throws FileNotFoundException {\n\t\tFile dir = new File(getApplicationBinaryDirectory());\n\t\tfor (String s : dir.list()) {\n\t\t\tif (s.toLowerCase().endsWith(\".jar\")) {\n\t\t\t\treturn s;\n\t\t\t}\n\t\t}\n\t\tthrow new RuntimeException(\"Studio Jar can not be found in \" + dir.getAbsolutePath());\n\t}\n\n\tpublic static String getApplicationBinaryDirectory() throws FileNotFoundException {\n\t\treturn System.getProperty(\"user.home\") + delim() + \"bin\" + delim() + DownloadManager.getSTUDIO_INSTALL()\n\t\t\t\t+ delim() + BowlerStudio.getBowlerStudioBinaryVersion() + delim();\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/scripting/external/OpenSCADExternalEditor.java",
    "content": "package com.neuronrobotics.bowlerstudio.scripting.external;\n\nimport static com.neuronrobotics.bowlerstudio.scripting.DownloadManager.*;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.eclipse.jgit.api.errors.GitAPIException;\nimport org.eclipse.jgit.api.errors.InvalidRemoteException;\nimport org.eclipse.jgit.api.errors.TransportException;\nimport org.eclipse.jgit.errors.NoWorkTreeException;\n\nimport com.neuronrobotics.bowlerstudio.assets.AssetFactory;\nimport com.neuronrobotics.bowlerstudio.scripting.DownloadManager;\nimport com.neuronrobotics.bowlerstudio.scripting.IExternalEditor;\nimport com.neuronrobotics.bowlerstudio.scripting.OpenSCADLoader;\nimport com.neuronrobotics.bowlerstudio.scripting.ScriptingEngine;\n\nimport eu.mihosoft.vrl.v3d.JavaFXInitializer;\nimport javafx.scene.control.Button;\nimport javafx.scene.image.Image;\n\npublic class OpenSCADExternalEditor implements IExternalEditor {\n\n\tprivate Button advanced;\n\n\t@Override\n\tpublic void launch(File file, Button advanced, Runnable OnComplete) {\n\t\tnew Thread(() -> {\n\t\t\tthis.advanced = advanced;\n\t\t\tString filename = file.getAbsolutePath();\n\n\t\t\ttry {\n\t\t\t\tFile dir = file.getAbsoluteFile().getParentFile();\n\t\t\t\tFile openscad = DownloadManager.getRunExecutable(\"openscad\", null);\n\n\t\t\t\tList<String> asList = Arrays.asList(openscad.getAbsolutePath(), filename);\n\n\t\t\t\tThread t = run(this, dir, System.err, asList);\n\t\t\t\tt.join();\n\t\t\t} catch (NoWorkTreeException e) {\n\t\t\t\t// Auto-generated catch block\n\t\t\t\te.printStackTrace();\n\t\t\t} catch (Exception e) {\n\t\t\t\t// Auto-generated catch block\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t\tonProcessExit(0);\n\n\t\t}).start();\n\t}\n\n\tpublic void onProcessExit(int ev) {\n\t\tadvanced.setDisable(false);\n\t}\n\n\t@Override\n\tpublic URL getInstallURL() throws MalformedURLException {\n\t\ttry {\n\t\t\treturn new URI(\"https://openscad.org/downloads.html\").toURL();\n\t\t} catch (URISyntaxException e) {\n\t\t\tthrow new MalformedURLException(e.getMessage());\n\t\t}\n\t}\n\n\t@Override\n\tpublic String nameOfEditor() {\n\t\treturn \"OpenSCAD\";\n\t}\n\n\tpublic Image getImage() {\n\t\ttry {\n\t\t\treturn AssetFactory.loadAsset(\"Script-Tab-OpenSCAD.png\");\n\t\t} catch (Exception e) {\n\t\t\t// Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic static void main(String[] args)\n\t\t\tthrows InvalidRemoteException, TransportException, GitAPIException, IOException {\n\t\tJavaFXInitializer.go();\n\t\tString url = \"https://github.com/madhephaestus/TestRepo.git\";\n\t\tScriptingEngine.pull(url);\n\t\tFile f = ScriptingEngine.fileFromGit(url, \"test.scad\");\n\n\t\tnew OpenSCADExternalEditor().launch(f, new Button(), () -> {\n\t\t});\n\t}\n\n\t@Override\n\tpublic List<Class> getSupportedLangauge() {\n\t\treturn Arrays.asList(OpenSCADLoader.class);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/scripting/external/SVGExternalEditor.java",
    "content": "package com.neuronrobotics.bowlerstudio.scripting.external;\n\nimport static com.neuronrobotics.bowlerstudio.scripting.DownloadManager.*;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.eclipse.jgit.api.errors.GitAPIException;\nimport org.eclipse.jgit.api.errors.InvalidRemoteException;\nimport org.eclipse.jgit.api.errors.TransportException;\nimport org.eclipse.jgit.errors.NoWorkTreeException;\n\nimport com.neuronrobotics.bowlerstudio.assets.AssetFactory;\nimport com.neuronrobotics.bowlerstudio.scripting.DownloadManager;\nimport com.neuronrobotics.bowlerstudio.scripting.IExternalEditor;\nimport com.neuronrobotics.bowlerstudio.scripting.ScriptingEngine;\nimport com.neuronrobotics.bowlerstudio.scripting.SvgLoader;\n\nimport javafx.scene.control.Button;\nimport javafx.scene.image.Image;\n\npublic class SVGExternalEditor implements IExternalEditor {\n\n\tprivate Button advanced;\n\n\t@Override\n\tpublic void launch(File file, Button advanced, Runnable OnComplete) {\n\t\tnew Thread(() -> {\n\t\t\tthis.advanced = advanced;\n\t\t\tString filename = file.getAbsolutePath();\n\n\t\t\ttry {\n\t\t\t\t// Git locateGit = ScriptingEngine.locateGit(file);\n\t\t\t\t// File dir = locateGit.getRepository().getWorkTree();\n\t\t\t\t// ScriptingEngine.closeGit(locateGit);\n\n\t\t\t\tFile exe = DownloadManager.getRunExecutable(\"inkscape\", null);\n\n\t\t\t\tList<String> asList = Arrays.asList(exe.getAbsolutePath(), filename);\n\t\t\t\tif (isMac()) {\n\t\t\t\t\tasList = Arrays.asList(\"open\", \"-a\", exe.getAbsolutePath(), filename);\n\n\t\t\t\t}\n\t\t\t\tThread t = run(this, file.getParentFile(), System.err, asList);\n\t\t\t\tt.join();\n\t\t\t} catch (NoWorkTreeException e) {\n\t\t\t\t// Auto-generated catch block\n\t\t\t\te.printStackTrace();\n\t\t\t} catch (Exception e) {\n\t\t\t\t// Auto-generated catch block\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t\tonProcessExit(0);\n\n\t\t}).start();\n\t}\n\n\tpublic void onProcessExit(int ev) {\n\t\tadvanced.setDisable(false);\n\t}\n\n\t@Override\n\tpublic URL getInstallURL() throws MalformedURLException {\n\t\ttry {\n\t\t\treturn new URI(\"https://inkscape.org/release/\").toURL();\n\t\t} catch (URISyntaxException e) {\n\t\t\tthrow new MalformedURLException(e.getMessage());\n\t\t}\n\t}\n\n\t@Override\n\tpublic String nameOfEditor() {\n\t\treturn \"Inkscape\";\n\t}\n\n\tpublic Image getImage() {\n\t\ttry {\n\t\t\treturn AssetFactory.loadAsset(\"Script-Tab-SVG.png\");\n\t\t} catch (Exception e) {\n\t\t\t// Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic static void main(String[] args)\n\t\t\tthrows InvalidRemoteException, TransportException, GitAPIException, IOException {\n\t\tFile f = ScriptingEngine.fileFromGit(\"https://github.com/Technocopia/Graphics.git\",\n\t\t\t\t\"Graphics/SimplifiedLogo/simplified logo.svg\");\n\n\t\tnew SVGExternalEditor().launch(f, new Button(), () -> {\n\t\t});\n\t}\n\n\t@Override\n\tpublic List<Class> getSupportedLangauge() {\n\t\treturn Arrays.asList(SvgLoader.class);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/scripting/external/SceneBuilderExternalEditor.java",
    "content": "package com.neuronrobotics.bowlerstudio.scripting.external;\n\nimport static com.neuronrobotics.bowlerstudio.scripting.DownloadManager.*;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.eclipse.jgit.api.errors.GitAPIException;\nimport org.eclipse.jgit.api.errors.InvalidRemoteException;\nimport org.eclipse.jgit.api.errors.TransportException;\nimport org.eclipse.jgit.errors.NoWorkTreeException;\n\nimport com.neuronrobotics.bowlerstudio.assets.AssetFactory;\nimport com.neuronrobotics.bowlerstudio.scripting.DownloadManager;\nimport com.neuronrobotics.bowlerstudio.scripting.FXMLBowlerLoader;\nimport com.neuronrobotics.bowlerstudio.scripting.IExternalEditor;\nimport com.neuronrobotics.bowlerstudio.scripting.ScriptingEngine;\n\nimport javafx.scene.control.Button;\nimport javafx.scene.image.Image;\n\npublic class SceneBuilderExternalEditor implements IExternalEditor {\n\n\tprivate Button advanced;\n\n\t@Override\n\tpublic void launch(File file, Button advanced, Runnable OnComplete) {\n\t\tnew Thread(() -> {\n\t\t\tthis.advanced = advanced;\n\t\t\tString filename = file.getAbsolutePath();\n\n\t\t\ttry {\n\t\t\t\tFile dir = file.getAbsoluteFile().getParentFile();\n\t\t\t\tFile scenebuilder = DownloadManager.getRunExecutable(\"scenebuilder\", null);\n\t\t\t\tFile java = DownloadManager.getRunExecutable(\"java17\", null);\n\n\t\t\t\tList<String> asList = Arrays.asList(java.getAbsolutePath(), \"-jar\", scenebuilder.getAbsolutePath(),\n\t\t\t\t\t\tfilename);\n\n\t\t\t\tThread t = run(this, dir, System.err, asList);\n\t\t\t\tt.join();\n\t\t\t} catch (NoWorkTreeException e) {\n\t\t\t\t// Auto-generated catch block\n\t\t\t\te.printStackTrace();\n\t\t\t} catch (Exception e) {\n\t\t\t\t// Auto-generated catch block\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t\tonProcessExit(0);\n\n\t\t}).start();\n\t}\n\n\tpublic void onProcessExit(int ev) {\n\t\tadvanced.setDisable(false);\n\t}\n\n\t@Override\n\tpublic URL getInstallURL() throws MalformedURLException {\n\t\ttry {\n\t\t\treturn new URI(\"https://gluonhq.com/products/scene-builder/\").toURL();\n\t\t} catch (URISyntaxException e) {\n\t\t\tthrow new MalformedURLException(e.getMessage());\n\t\t}\n\t}\n\n\t@Override\n\tpublic String nameOfEditor() {\n\t\treturn \"SceneBuilder\";\n\t}\n\n\tpublic Image getImage() {\n\t\ttry {\n\t\t\treturn AssetFactory.loadAsset(\"Script-Tab-fxml.png\");\n\t\t} catch (Exception e) {\n\t\t\t// Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic static void main(String[] args)\n\t\t\tthrows InvalidRemoteException, TransportException, GitAPIException, IOException {\n\t\tFile f = ScriptingEngine.fileFromGit(\"https://github.com/madhephaestus/HortonsLinkages.git\", \"main.fxml\");\n\n\t\tnew SceneBuilderExternalEditor().launch(f, new Button(), () -> {\n\t\t});\n\t}\n\n\t@Override\n\tpublic List<Class> getSupportedLangauge() {\n\t\treturn Arrays.asList(FXMLBowlerLoader.class);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/tabs/AbstractBowlerStudioTab.java",
    "content": "package com.neuronrobotics.bowlerstudio.tabs;\n\nimport java.util.ArrayList;\n\nimport com.neuronrobotics.bowlerstudio.BowlerStudioController;\nimport com.neuronrobotics.bowlerstudio.BowlerStudioModularFrame;\nimport com.neuronrobotics.bowlerstudio.creature.MobileBaseCadManager;\nimport com.neuronrobotics.sdk.addons.kinematics.DHParameterKinematics;\nimport com.neuronrobotics.sdk.addons.kinematics.MobileBase;\nimport com.neuronrobotics.sdk.common.BowlerAbstractDevice;\nimport com.neuronrobotics.sdk.common.IDeviceConnectionEventListener;\n//import com.sun.javafx.scene.control.behavior.TabPaneBehavior;\n//import com.sun.javafx.scene.control.skin.TabPaneSkin;\n\nimport eu.mihosoft.vrl.v3d.CSG;\nimport javafx.event.Event;\nimport javafx.event.EventHandler;\nimport javafx.scene.control.Tab;\n\npublic abstract class AbstractBowlerStudioTab extends Tab implements EventHandler<Event> {\n\n\tprivate boolean active = false;\n\tArrayList<String> myNames = null;\n\tprivate EventHandler<Event> localCopyOfEventHandler;\n\n\tpublic abstract void onTabClosing();\n\n\tpublic abstract String[] getMyNameSpaces();\n\n\tpublic abstract void initializeUI(BowlerAbstractDevice pm);\n\n\tpublic abstract void onTabReOpening();\n\n\tpublic void setDevice(BowlerAbstractDevice pm) {\n\t\tmyNames = new ArrayList<>();\n\t\tif (getMyNameSpaces().length > 0) {\n\t\t\tfor (int i = 0; i < getMyNameSpaces().length; i++) {\n\t\t\t\tmyNames.add(getMyNameSpaces()[i]);\n\t\t\t}\n\t\t\tif (!isMyNamespace(pm.getNamespaces())) {\n\t\t\t\tthrow new RuntimeException(\"Device and namespaces are incompatible \");\n\t\t\t}\n\t\t}\n\t\tsetOnCloseRequest(this);\n\t\tinitializeUI(pm);\n\t\tpm.addConnectionEventListener(new IDeviceConnectionEventListener() {\n\n\t\t\t@Override\n\t\t\tpublic void onDisconnect(BowlerAbstractDevice source) {\n\t\t\t\t// if the device disconnects, close the tab\n\t\t\t\tif (source == pm && source != null) {\n\t\t\t\t\trequestClose();\n\t\t\t\t\tif (MobileBase.class.isInstance(pm)) {\n\t\t\t\t\t\tMobileBase dev = (MobileBase) pm;\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tfor (CSG p : MobileBaseCadManager.searchForCadManager(dev).getBasetoCadMap().get(dev))\n\t\t\t\t\t\t\t\tBowlerStudioController.removeObject(p);\n\t\t\t\t\t\t\tfor (DHParameterKinematics leg : dev.getAllDHChains())\n\t\t\t\t\t\t\t\tfor (CSG p : MobileBaseCadManager.searchForCadManager(dev).getDHtoCadMap().get(leg))\n\t\t\t\t\t\t\t\t\tBowlerStudioController.removeObject(p);\n\t\t\t\t\t\t} catch (Throwable e) {\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// Not a bug, expected to ensure one device disconnects the rest of the\n\t\t\t\t\t// dependent devices\n\t\t\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"Device type was\n\t\t\t\t\t// \"+source.getClass()+\" named \"+source.getScriptingName()+\" expected\n\t\t\t\t\t// \"+pm.getClass()+\" named \"+pm.getScriptingName());\n\t\t\t\t\t// new Exception().printStackTrace();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onConnect(BowlerAbstractDevice source) {\n\t\t\t\t// Auto-generated method stub\n\n\t\t\t}\n\t\t});\n\t}\n\n\tpublic boolean isMyNamespace(ArrayList<String> names) {\n\t\tif (names == null)\n\t\t\treturn false;\n\t\tfor (String s : names) {\n\t\t\tfor (String m : myNames) {\n\t\t\t\tif (s.contains(m)) {\n\t\t\t\t\tsetActive(true);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn isAcvive();\n\t}\n\n\t@Override\n\tpublic void setOnCloseRequest(EventHandler<Event> value) {\n\t\tthis.localCopyOfEventHandler = value;\n\t\tsuper.setOnCloseRequest(value);\n\t\tcom.neuronrobotics.sdk.common.Log.error(\" A close requested for \" + getText());\n\t}\n\n\tpublic void requestClose() {\n\t\tBowlerStudioModularFrame.getBowlerStudioModularFrame().closeTab(this);\n\t}\n\t//\n\t// private TabPaneBehavior getBehavior() {\n\t// return ((TabPaneSkin) getTabPane().getSkin()).getBehavior();\n\t// }\n\n\tpublic void setActive(boolean a) {\n\t\tactive = a;\n\t}\n\n\tpublic boolean isAcvive() {\n\t\treturn active;\n\t}\n\n\t@Override\n\tpublic void handle(Event event) {\n\t\tcom.neuronrobotics.sdk.common.Log.error(\"Closing \" + getText());\n\t\tonTabClosing();\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/tabs/CalibrateGameControl.java",
    "content": "package com.neuronrobotics.bowlerstudio.tabs;\n\nimport java.io.File;\nimport java.net.URL;\n\nimport com.neuronrobotics.bowlerstudio.assets.AssetFactory;\nimport com.neuronrobotics.bowlerstudio.assets.FontSizeManager;\nimport com.neuronrobotics.sdk.addons.gamepad.BowlerJInputDevice;\nimport com.neuronrobotics.sdk.addons.gamepad.JogTrainerWidget;\nimport com.neuronrobotics.sdk.common.BowlerAbstractDevice;\n\nimport javafx.fxml.FXMLLoader;\nimport javafx.scene.Parent;\n\npublic class CalibrateGameControl extends AbstractBowlerStudioTab {\n\n\tprivate JogTrainerWidget w;\n\n\t@Override\n\tpublic void onTabClosing() {\n\t\t// Auto-generated method stub\n\n\t}\n\n\t@Override\n\tpublic String[] getMyNameSpaces() {\n\t\t// Auto-generated method stub\n\t\treturn new String[0];\n\t}\n\n\t@Override\n\tpublic void initializeUI(BowlerAbstractDevice pm) {\n\t\tBowlerJInputDevice dev = (BowlerJInputDevice) pm;\n\n\t\tFile fxmlFIle;\n\t\ttry {\n\t\t\tfxmlFIle = AssetFactory.loadFile(\"layout/jogTrainerWidget.fxml\");\n\t\t\tURL fileURL = fxmlFIle.toURI().toURL();\n\t\t\tFXMLLoader loader = new FXMLLoader(fileURL);\n\t\t\tloader.setLocation(fileURL);\n\t\t\tParent root;\n\t\t\tw = new JogTrainerWidget(dev);\n\t\t\tloader.setController(w);\n\t\t\t// This is needed when loading on MAC\n\t\t\tloader.setClassLoader(JogTrainerWidget.class.getClassLoader());\n\t\t\troot = loader.load();\n\t\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\t\tint tmp = fontNum - 10;\n\t\t\t\tif (tmp < 12)\n\t\t\t\t\ttmp = 12;\n\t\t\t\troot.setStyle(\"-fx-font-size: \" + tmp + \"pt\");\n\t\t\t});\n\t\t\tsetContent(root);\n\t\t} catch (Exception e) {\n\t\t\t// Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t}\n\n\t\tsetText(\"Calibrate Game Control\");\n\t\tonTabReOpening();\n\t}\n\n\t@Override\n\tpublic void onTabReOpening() {\n\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/tabs/FirmataTab.java",
    "content": "package com.neuronrobotics.bowlerstudio.tabs;\n\nimport javax.swing.JFrame;\n\nimport org.firmata4j.firmata.FirmataDevice;\nimport org.firmata4j.ui.JPinboard;\n\nimport com.neuronrobotics.sdk.addons.kinematics.FirmataBowler;\nimport com.neuronrobotics.sdk.common.BowlerAbstractDevice;\n\nimport javafx.embed.swing.SwingNode;\nimport javafx.scene.control.ScrollPane;\n\npublic class FirmataTab extends AbstractBowlerStudioTab {\n\n\t@Override\n\tpublic void onTabClosing() {\n\t\t// Auto-generated method stub\n\n\t}\n\n\t@Override\n\tpublic String[] getMyNameSpaces() {\n\t\t// Auto-generated method stub\n\t\treturn new String[0];\n\t}\n\n\t@Override\n\tpublic void initializeUI(BowlerAbstractDevice pm) {\n\t\tFirmataDevice device = ((FirmataBowler) pm).getFirmataDevice();\n\t\t// Auto-generated method stub\n\t\tJFrame frame = new JFrame(\"Pinboard Example\");\n\t\tframe.add(new JPinboard(device));\n\t\tframe.pack();\n\t\tframe.setVisible(true);\n\n\t\tJPinboard pinboard = new JPinboard(device);\n\t\tpinboard.setVisible(true);\n\t\tSwingNode sn = new SwingNode();\n\t\tsn.setContent(pinboard);\n\t\tScrollPane s1 = new ScrollPane();\n\n\t\ts1.setContent(sn);\n\t\tsetContent(s1);\n\t\tsetText(\"Firmata Pinpoard\");\n\t\tonTabReOpening();\n\t}\n\n\t@Override\n\tpublic void onTabReOpening() {\n\t\t// Auto-generated method stub\n\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/tabs/LocalFileScriptTab.java",
    "content": "package com.neuronrobotics.bowlerstudio.tabs;\n\nimport java.awt.Color;\nimport java.awt.Font;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.KeyEvent;\nimport java.awt.event.MouseAdapter;\nimport java.awt.event.MouseEvent;\nimport java.io.File;\nimport java.io.IOException;\nimport java.lang.Thread.UncaughtExceptionHandler;\nimport java.text.SimpleDateFormat;\nimport java.time.Duration;\nimport java.util.Date;\nimport java.util.HashMap;\n\nimport org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;\nimport org.fife.ui.rsyntaxtextarea.SyntaxConstants;\nimport org.fife.ui.rsyntaxtextarea.Theme;\nimport org.fife.ui.rtextarea.RTextScrollPane;\n\nimport javax.swing.AbstractAction;\nimport javax.swing.JButton;\nimport javax.swing.JPanel;\nimport javax.swing.JScrollBar;\nimport javax.swing.JScrollPane;\nimport javax.swing.KeyStroke;\nimport javax.swing.event.CaretEvent;\nimport javax.swing.event.CaretListener;\nimport javax.swing.event.DocumentEvent;\nimport javax.swing.event.DocumentListener;\nimport javax.swing.plaf.basic.BasicScrollBarUI;\nimport javax.swing.text.BadLocationException;\nimport javax.swing.text.DefaultHighlighter;\nimport javax.swing.text.Highlighter;\nimport javax.swing.text.Highlighter.HighlightPainter;\n\n//import javafx.embed.swing.MySwingNode;\nimport javafx.embed.swing.SwingNode;\nimport javafx.event.EventHandler;\nimport javafx.scene.layout.VBox;\n\nimport com.neuronrobotics.bowlerstudio.BowlerStudio;\nimport com.neuronrobotics.bowlerstudio.IssueReportingExceptionHandler;\nimport com.neuronrobotics.bowlerstudio.assets.ConfigurationDatabase;\nimport com.neuronrobotics.bowlerstudio.scripting.IScriptEventListener;\nimport com.neuronrobotics.bowlerstudio.scripting.ScriptingEngine;\nimport com.neuronrobotics.bowlerstudio.scripting.ScriptingFileWidget;\nimport com.neuronrobotics.bowlerstudio.utils.FindTextWidget;\nimport com.neuronrobotics.sdk.common.Log;\n\nimport javafx.stage.Stage;\nimport javafx.stage.WindowEvent;\n\npublic class LocalFileScriptTab extends VBox implements IScriptEventListener, EventHandler<WindowEvent> {\n\tprivate static final int MaxTextSize = 75000;\n\tprivate static final UncaughtExceptionHandler ISSUE_REPORTING_EXCEPTION_HANDLER = new UncaughtExceptionHandler() {\n\t\tIssueReportingExceptionHandler reporter = new IssueReportingExceptionHandler();\n\n\t\t@Override\n\t\tpublic void uncaughtException(Thread t, Throwable e) {\n\t\t\tif (reporter.getTitle(e).contains(\"java.awt.datatransfer.DataFlavor at line 503\")) {\n\t\t\t\tcom.neuronrobotics.sdk.common.Log\n\t\t\t\t\t\t.error(\"Known bug in the Swing system, nothing we can do but ignore it\");\n\t\t\t\te.printStackTrace();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\treporter.uncaughtException(t, e);\n\t\t}\n\t};// new IssueReportingExceptionHandler();\n\tprivate long lastRefresh = 0;\n\tprivate ScriptingFileWidget scripting;\n\n\tIScriptEventListener l = null;\n\n\tprivate SwingNode swingNode;\n\tprivate RTextScrollPane spscrollPane;\n\n\tprivate Highlighter highlighter;\n\n\tprivate HighlightPainter painter;\n\tprivate int lineSelected = 0;\n\n\tprivate RSyntaxTextArea textArea = new RSyntaxTextArea(200, 300);\n\n\tprivate final File file;\n\n\tprivate Font myFont;\n\tprivate String content = \"\";\n\n\tprivate static HashMap<String, String> langaugeMapping = new HashMap<>();\n\n\tprivate static LocalFileScriptTab selectedTab = null;\n\tprivate SimpleDateFormat dateFormat = new SimpleDateFormat(\"yyyy.MM.dd.HH.mm.ss\");\n\tprivate long timeSinceLastUpdate = 0;\n\tprivate boolean refreshArmed = false;\n\tstatic {\n\t\tBowlerStudio.invokeLater(() -> Thread.setDefaultUncaughtExceptionHandler(new IssueReportingExceptionHandler()));\n\n\t}\n\n\t// public class MyRSyntaxTextArea extends RSyntaxTextArea implements\n\t// ComponentListener {\n\t//\n\t// /**\n\t// *\n\t// */\n\t// private static final long serialVersionUID = 1L;\n\t//\n\t// public MyRSyntaxTextArea() {\n\t// this.addComponentListener(this);\n\t// }\n\t//\n\t// public MyRSyntaxTextArea(int i, int j) {\n\t// super(i, j);\n\t// }\n\t//\n\t// public void componentResized(ComponentEvent e) {\n\t// com.neuronrobotics.sdk.common.Log.error(\"componentResized\");\n\t//\n\t// }\n\t//\n\t// public void componentHidden(ComponentEvent e) {\n\t// com.neuronrobotics.sdk.common.Log.error(\"componentHidden\");\n\t// }\n\t//\n\t// public void componentMoved(ComponentEvent e) {\n\t// com.neuronrobotics.sdk.common.Log.error(\"componentMoved\");\n\t// }\n\t//\n\t// public void componentShown(ComponentEvent e) {\n\t// com.neuronrobotics.sdk.common.Log.error(\"componentShown\");\n\t// }\n\t//\n\t// }\n\tpublic void requestTextAreaFocus() {\n\t\trefreshArmed = false;\n\t\t// Log.debug(\"Focus requested\");\n\t\tBowlerStudio.runLater(Duration.ofMillis(200), (Runnable) () -> {\n\t\t\tBowlerStudio.invokeLater(() -> {\n\t\t\t\tresizeEvent();\n\t\t\t});\n\t\t});\n\t}\n\n\tpublic static void setExtensionSyntaxType(String shellType, String syntax) {\n\t\tlangaugeMapping.put(shellType, syntax);\n\t}\n\n\tpublic LocalFileScriptTab(File file) throws IOException {\n\t\tThread.setDefaultUncaughtExceptionHandler(new IssueReportingExceptionHandler());\n\t\tthis.file = file;\n\t\tsetScripting(new ScriptingFileWidget(file));\n\t\tsetSpacing(5);\n\t\tl = this;\n\n\t\tgetScripting().addIScriptEventListener(l);\n\t\tString type;\n\n\t\tString shellType = ScriptingEngine.getShellType(file.getName());\n\t\tswitch (shellType) {\n\t\t\tcase \"Clojure\" :\n\t\t\t\ttype = SyntaxConstants.SYNTAX_STYLE_CLOJURE;\n\t\t\t\tbreak;\n\t\t\tdefault :\n\t\t\t\ttype = langaugeMapping.get(shellType);\n\t\t\t\tif (type == null) {\n\t\t\t\t\ttype = SyntaxConstants.SYNTAX_STYLE_NONE;\n\t\t\t\t\tif (shellType.toLowerCase().contains(\"arduino\")) {\n\t\t\t\t\t\ttype = SyntaxConstants.SYNTAX_STYLE_CPLUSPLUS;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\tcase \"JSON\" :\n\t\t\t\ttype = SyntaxConstants.SYNTAX_STYLE_JSON;\n\t\t\t\tbreak;\n\t\t\tcase \"ArduingScriptingLangauge\" :\n\t\t\t\ttype = SyntaxConstants.SYNTAX_STYLE_LISP;\n\t\t\t\tbreak;\n\t\t\tcase \"Arduino\" :\n\t\t\t\ttype = SyntaxConstants.SYNTAX_STYLE_CPLUSPLUS;\n\t\t\t\tbreak;\n\t\t\tcase \"Groovy\" :\n\t\t\t\ttype = SyntaxConstants.SYNTAX_STYLE_GROOVY;\n\t\t\t\tbreak;\n\t\t\tcase \"Jython\" :\n\t\t\t\ttype = SyntaxConstants.SYNTAX_STYLE_PYTHON;\n\t\t\t\tbreak;\n\t\t\tcase \"MobilBaseXML\" :\n\t\t\t\ttype = SyntaxConstants.SYNTAX_STYLE_XML;\n\t\t\t\tbreak;\n\t\t\tcase \"Kotlin\" :\n\t\t\t\ttype = SyntaxConstants.SYNTAX_STYLE_JAVA;\n\t\t\t\tbreak;\n\t\t\tcase \"SVG\" :\n\t\t\t\ttype = SyntaxConstants.SYNTAX_STYLE_XML;\n\t\t\t\tbreak;\n\t\t\tcase \"Bash\" :\n\t\t\t\ttype = SyntaxConstants.SYNTAX_STYLE_UNIX_SHELL;\n\t\t\t\tbreak;\n\t\t\tcase \"fxml\" :\n\t\t\t\ttype = SyntaxConstants.SYNTAX_STYLE_XML;\n\t\t\t\tbreak;\n\t\t\tcase \"CaDoodle\" :\n\t\t\t\ttype = SyntaxConstants.SYNTAX_STYLE_JSON;\n\t\t\t\tbreak;\n\t\t}\n\t\ttextArea.setSyntaxEditingStyle(type);\n\t\ttextArea.setCodeFoldingEnabled(true);\n\n\t\ttextArea.getDocument().addDocumentListener(new DocumentListener() {\n\n\t\t\t@Override\n\t\t\tpublic void removeUpdate(DocumentEvent e) {\n\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void insertUpdate(DocumentEvent e) {\n\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void changedUpdate(DocumentEvent arg0) {\n\t\t\t\t// new Thread() {\n\t\t\t\t// public void run() {\n\t\t\t\ttry {\n\t\t\t\t\ttimeSinceLastUpdate = System.currentTimeMillis();\n\t\t\t\t\tif (textArea.isEnabled())\n\t\t\t\t\t\tsetContent(textArea.getText());\n\t\t\t\t\tgetScripting().removeIScriptEventListener(l);\n\t\t\t\t\tgetScripting().setCode(content);\n\t\t\t\t\tgetScripting().addIScriptEventListener(l);\n\t\t\t\t} catch (Throwable t) {\n\t\t\t\t\tt.printStackTrace();\n\t\t\t\t}\n\t\t\t\t// }\n\t\t\t\t// }.start();\n\t\t\t}\n\t\t});//\n\n\t\ttextArea.addCaretListener(new CaretListener() {\n\n\t\t\t@Override\n\t\t\tpublic void caretUpdate(CaretEvent e) {\n\n\t\t\t\t// Lets start with some default values for the line and column.\n\t\t\t\tint linenum = 1;\n\t\t\t\tint columnnum = 1;\n\n\t\t\t\t// We create a try catch to catch any exceptions. We will simply\n\t\t\t\t// ignore such an error for our demonstration.\n\t\t\t\ttry {\n\t\t\t\t\t// First we find the position of the caret. This is the\n\t\t\t\t\t// number of where the caret is in relation to the start of\n\t\t\t\t\t// the JTextArea\n\t\t\t\t\t// in the upper left corner. We use this position to find\n\t\t\t\t\t// offset values (eg what line we are on for the given\n\t\t\t\t\t// position as well as\n\t\t\t\t\t// what position that line starts on.\n\t\t\t\t\tint caretpos = textArea.getCaretPosition();\n\t\t\t\t\tlinenum = textArea.getLineOfOffset(caretpos);\n\n\t\t\t\t\t// We subtract the offset of where our line starts from the\n\t\t\t\t\t// overall caret position.\n\t\t\t\t\t// So lets say that we are on line 5 and that line starts at\n\t\t\t\t\t// caret position 100, if our caret position is currently\n\t\t\t\t\t// 106\n\t\t\t\t\t// we know that we must be on column 6 of line 5.\n\t\t\t\t\tcolumnnum = caretpos - textArea.getLineStartOffset(linenum);\n\n\t\t\t\t\t// We have to add one here because line numbers start at 0\n\t\t\t\t\t// for getLineOfOffset and we want it to start at 1 for\n\t\t\t\t\t// display.\n\t\t\t\t\tlinenum += 1;\n\t\t\t\t\tif (lineSelected != linenum) {\n\t\t\t\t\t\tlineSelected = linenum;\n\t\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.debug(\"Select \" + lineSelected);\n\t\t\t\t\t\tnew Thread(() -> {\n\t\t\t\t\t\t\tBowlerStudio.select(file, lineSelected);\n\t\t\t\t\t\t}).start();\n\t\t\t\t\t}\n\t\t\t\t} catch (Throwable ex) {\n\t\t\t\t\tex.printStackTrace();\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t});\n\n\t\ttextArea.addMouseListener(new MouseAdapter() {\n\t\t\tpublic void mouseClicked(MouseEvent e) {\n\t\t\t\ttry {\n\t\t\t\t\tif (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() > 2) {\n\t\t\t\t\t\thighlighter.removeAllHighlights();\n\t\t\t\t\t}\n\t\t\t\t\tif (e.getClickCount() == 1 && refreshArmed) {\n\t\t\t\t\t\trequestTextAreaFocus();\n\t\t\t\t\t}\n\t\t\t\t\t// else {\n\t\t\t\t\t// swingNode.setOnMouseClicked(event -> {\n\t\t\t\t\t// SwingUtilities.invokeLater(() -> {\n\t\t\t\t\t// textArea.requestFocusInWindow();\n\t\t\t\t\t// });\n\t\t\t\t\t// });\n\t\t\t\t\t// }\n\t\t\t\t} catch (Throwable t) {\n\t\t\t\t\tt.printStackTrace();\n\t\t\t\t}\n\t\t\t\tcom.neuronrobotics.sdk.common.Log.debug(\"Number of click: \" + e.getClickCount());\n\t\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"Click position (X, Y): \" + e.getX()\n\t\t\t\t// + \",\n\t\t\t\t// \" + e.getY());\n\t\t\t}\n\t\t});\n\n\t\tspscrollPane = new RTextScrollPane(textArea);\n\t\tBoolean dark = (Boolean) ConfigurationDatabase.get(\"BowlerStudioUI\", \"DarkMode\", true);\n\t\tif (dark) {\n\t\t\t// Apply a dark theme\n\t\t\ttry {\n\t\t\t\tTheme theme = Theme\n\t\t\t\t\t\t.load(getClass().getResourceAsStream(\"/org/fife/ui/rsyntaxtextarea/themes/dark.xml\"));\n\t\t\t\ttheme.apply(textArea);\n\t\t\t} catch (IOException ioe) {\n\t\t\t\tioe.printStackTrace();\n\t\t\t}\n\n\t\t\t// Set the viewport background (content area)\n\t\t\tspscrollPane.getViewport().setBackground(new Color(0x5a6ec4)); // even lighter blue\n\n\t\t\t// Set viewport background\n\t\t\tspscrollPane.getViewport().setBackground(new Color(0x5a6ec4)); // even lighter blue\n\n\t\t\t// Style vertical scrollbar\n\t\t\tJScrollBar vertical = spscrollPane.getVerticalScrollBar();\n\t\t\tvertical.setUI(new BasicScrollBarUI() {\n\t\t\t\t@Override\n\t\t\t\tprotected void configureScrollBarColors() {\n\t\t\t\t\tthis.trackColor = new Color(0x5a6ec4); // even lighter blue\n\t\t\t\t\tthis.thumbColor = new Color(0x263d8c); // logo blue\n\t\t\t\t\tthis.thumbDarkShadowColor = new Color(0x263d8c);\n\t\t\t\t\tthis.thumbHighlightColor = new Color(0x263d8c);\n\t\t\t\t\tthis.thumbLightShadowColor = new Color(0x263d8c);\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tprotected JButton createDecreaseButton(int orientation) {\n\t\t\t\t\tJButton button = super.createDecreaseButton(orientation);\n\t\t\t\t\tbutton.setBackground(new Color(0xf2c83d)); // yellow\n\t\t\t\t\treturn button;\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tprotected JButton createIncreaseButton(int orientation) {\n\t\t\t\t\tJButton button = super.createIncreaseButton(orientation);\n\t\t\t\t\tbutton.setBackground(new Color(0xf2c83d)); // yellow\n\t\t\t\t\treturn button;\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// Style horizontal scrollbar similarly\n\t\t\tJScrollBar horizontal = spscrollPane.getHorizontalScrollBar();\n\t\t\thorizontal.setUI(new BasicScrollBarUI() {\n\t\t\t\t@Override\n\t\t\t\tprotected void configureScrollBarColors() {\n\t\t\t\t\tthis.trackColor = new Color(0x5a6ec4);\n\t\t\t\t\tthis.thumbColor = new Color(0x263d8c);\n\t\t\t\t\tthis.thumbDarkShadowColor = new Color(0x263d8c);\n\t\t\t\t\tthis.thumbHighlightColor = new Color(0x263d8c);\n\t\t\t\t\tthis.thumbLightShadowColor = new Color(0x263d8c);\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tprotected JButton createDecreaseButton(int orientation) {\n\t\t\t\t\tJButton button = super.createDecreaseButton(orientation);\n\t\t\t\t\tbutton.setBackground(new Color(0xf2c83d));\n\t\t\t\t\treturn button;\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tprotected JButton createIncreaseButton(int orientation) {\n\t\t\t\t\tJButton button = super.createIncreaseButton(orientation);\n\t\t\t\t\tbutton.setBackground(new Color(0xf2c83d));\n\t\t\t\t\treturn button;\n\t\t\t\t}\n\t\t\t});\n\t\t\t// Set the corners where scrollbars meet\n\t\t\tJPanel lowerRight = new JPanel();\n\t\t\tlowerRight.setBackground(new Color(0x5a6ec4)); // even lighter blue\n\t\t\tspscrollPane.setCorner(JScrollPane.LOWER_RIGHT_CORNER, lowerRight);\n\n\t\t\tJPanel lowerLeft = new JPanel();\n\t\t\tlowerLeft.setBackground(new Color(0x5a6ec4));\n\t\t\tspscrollPane.setCorner(JScrollPane.LOWER_LEFT_CORNER, lowerLeft);\n\n\t\t\tJPanel upperRight = new JPanel();\n\t\t\tupperRight.setBackground(new Color(0x5a6ec4));\n\t\t\tspscrollPane.setCorner(JScrollPane.UPPER_RIGHT_CORNER, upperRight);\n\n\t\t\tJPanel upperLeft = new JPanel();\n\t\t\tupperLeft.setBackground(new Color(0x5a6ec4));\n\t\t\tspscrollPane.setCorner(JScrollPane.UPPER_LEFT_CORNER, upperLeft);\n\t\t}\n\t\tswingNode = new javafx.embed.swing.SwingNode();\n\n\t\tKeyStroke keystroke_s = KeyStroke.getKeyStroke(KeyEvent.VK_S, KeyEvent.CTRL_DOWN_MASK);\n\t\ttextArea.getInputMap().put(keystroke_s, \"s\");\n\t\ttextArea.getActionMap().put(\"s\", new AbstractAction() {\n\t\t\t/**\n\t\t\t *\n\t\t\t */\n\t\t\tprivate static final long serialVersionUID = -3361326129563407389L;\n\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\ttry {\n\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.debug(\"Save \" + file + \" now.\");\n\t\t\t\t\tgetScripting().saveTheFile(file);\n\t\t\t\t} catch (Throwable t) {\n\t\t\t\t\tt.printStackTrace();\n\t\t\t\t}\n\n\t\t\t}\n\t\t});\n\t\t// Set event listener to listen for CTRL+F and find text\n\t\tKeyStroke keystroke = KeyStroke.getKeyStroke(KeyEvent.VK_F, KeyEvent.CTRL_DOWN_MASK);\n\t\ttextArea.getInputMap().put(keystroke, \"f\");\n\t\ttextArea.getActionMap().put(\"f\", new AbstractAction() {\n\t\t\t/**\n\t\t\t *\n\t\t\t */\n\t\t\tprivate static final long serialVersionUID = -4698223073831405851L;\n\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\ttry {\n\t\t\t\t\tfindTextWidget();\n\t\t\t\t} catch (Throwable t) {\n\t\t\t\t\tt.printStackTrace();\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tmyFont = textArea.getFont();\n\t\thighlighter = textArea.getHighlighter();\n\t\tpainter = new DefaultHighlighter.DefaultHighlightPainter(Color.pink);\n\n\t\thighlighter.removeAllHighlights();\n\t\tswingNode.setOnMouseExited(mouseEvent -> {\n\t\t\trefreshArmed = true;\n\t\t});\n\n\t\t// widthProperty().addListener((w, o, n) -> {\n\t\t// resizeEvent();\n\t\t//\n\t\t// });\n\t\t// heightProperty().addListener((w, o, n) -> {\n\t\t// resizeEvent();\n\t\t// });\n\t\tBowlerStudio.invokeLater(() -> {\n\t\t\ttry {\n\t\t\t\tif (getScripting() != null && getScripting().getCode() != null) {\n\t\t\t\t\tonScriptChanged(null, getScripting().getCode(), file);\n\t\t\t\t}\n\t\t\t} catch (Throwable t) {\n\t\t\t\tt.printStackTrace();\n\t\t\t}\n\t\t});\n\t\tBowlerStudio.invokeLater(() -> swingNode.setContent(spscrollPane));\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tgetChildren().setAll(swingNode, getScripting());\n\t\t});\n\n\t\t// swingNode.setOnMouseClicked(event -> {\n\t\t// SwingUtilities.invokeLater(() -> {\n\t\t// textArea.requestFocusInWindow();\n\t\t// });\n\t\t// });\n\t\t//\n\t\t// BowlerStudio.invokeLater(() -> {\n\t\t// textArea.setFocusable(true);\n\t\t// textArea.setRequestFocusEnabled(true);\n\t\t// swingNode.setContent(spscrollPane);\n\t\t// textArea.requestFocusInWindow();\n\t\t// });\n\t\t// textArea.setEditable(true);\n\t\t// textArea.setEnabled(true);\n\t}\n\n\tprivate void resizeEvent() {\n\t\tif (!((lastRefresh + 60) < System.currentTimeMillis())\n\t\t\t\t|| spscrollPane.getVerticalScrollBar().getValueIsAdjusting()\n\t\t\t\t|| spscrollPane.getHorizontalScrollBar().getValueIsAdjusting()) {\n\t\t\treturn;\n\t\t}\n\t\tlastRefresh = System.currentTimeMillis();\n\t\tBowlerStudio.invokeLater(() -> {\n\t\t\tspscrollPane.setSize((int) spscrollPane.getWidth(), (int) spscrollPane.getHeight());\n\t\t\tspscrollPane.invalidate();\n\t\t\tspscrollPane.repaint();\n\t\t\ttextArea.invalidate();\n\t\t\ttextArea.repaint();\n\n\t\t\ttextArea.requestFocusInWindow();\n\t\t\tBowlerStudio.runLater(Duration.ofMillis((int) 16), () -> {\n\t\t\t\tswingNode.setContent(spscrollPane);\n\t\t\t\tswingNode.requestFocus();\n\t\t\t});\n\t\t});\n\n\t}\n\n\t@Override\n\tpublic void onScriptFinished(Object result, Object previous, File source) {\n\t\t// BowlerStudio.invokeLater(new Runnable() {\n\t\t// @Override\n\t\t// public void run() {\n\t\t// textArea.requestFocusInWindow();\n\t\t// }\n\t\t// });\n\n\t}\n\n\t@Override\n\tpublic void onScriptChanged(String previous, String current, File source) {\n\t\t// int place = textArea.getCaretPosition();\n\t\t// com.neuronrobotics.sdk.common.Log.error(\"Carrot position is= \"+place);\n\t\t// codeArea.replaceText(current);\n\t\t// codeArea.setCursor(place);\n\t\t// com.neuronrobotics.sdk.common.Log.error(file.getAbsolutePath()+\" changed \");\n\t\t// empty\n\t\tBowlerStudio.invokeLater(() -> {\n\t\t\tsetContent(current);\n\t\t\tif (previous == null)\n\t\t\t\tBowlerStudio.invokeLater(() -> {\n\t\t\t\t\ttry {\n\t\t\t\t\t\ttextArea.setCaretPosition(0);\n\t\t\t\t\t} catch (Throwable t) {\n\t\t\t\t\t\tt.printStackTrace();\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t});\n\n\t}\n\n\tprivate void setContent(String current) {\n\t\tif (current.length() > 3 && !content.contentEquals(current)) {\n\t\t\tcontent = current; // writes\n\t\t\tlong now = System.currentTimeMillis();\n\t\t\tif (now < (timeSinceLastUpdate + 100)) {\n\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Ovewrite Protect!\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\ttimeSinceLastUpdate = now;\n\n\t\t\tcom.neuronrobotics.sdk.common.Log\n\t\t\t\t\t.error(\"External change of \" + file.getName() + \" on \" + dateFormat.format(new Date()));\n\t\t\t// if (current.length() > MaxTextSize) {\n\t\t\t// textArea.setText(\n\t\t\t// \"File too big for this text editor: \" + current.length() + \" larger than \" +\n\t\t\t// MaxTextSize);\n\t\t\t// textArea.setEnabled(false);\n\t\t\t// } else {\n\t\t\tif (!textArea.getText().contentEquals(content))\n\t\t\t\ttextArea.setText(current);\n\t\t\t// }\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onScriptError(Throwable except, File source) {\n\t\tBowlerStudio.invokeLater(new Runnable() {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"script error\");\n\t\t\t\ttextArea.requestFocusInWindow();\n\t\t\t}\n\t\t});\n\n\t}\n\n\tpublic void findTextWidget() {\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tStage s = new Stage();\n\t\t\tnew Thread() {\n\t\t\t\tpublic void run() {\n\t\t\t\t\tThread.setDefaultUncaughtExceptionHandler(ISSUE_REPORTING_EXCEPTION_HANDLER);\n\n\t\t\t\t\tFindTextWidget controller = new FindTextWidget();\n\t\t\t\t\tcontroller.setTextArea(textArea);\n\t\t\t\t\ttry {\n\t\t\t\t\t\tcontroller.start(s);\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t// ISSUE_REPORTING_EXCEPTION_HANDLER.uncaughtException(Thread.currentThread(),\n\t\t\t\t\t\t// e);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}.start();\n\t\t});\n\t}\n\n\t@Override\n\tpublic void handle(WindowEvent event) {\n\t\tgetScripting().stop();\n\t}\n\n\tpublic ScriptingFileWidget getScripting() {\n\t\treturn scripting;\n\t}\n\n\tpublic void setScripting(ScriptingFileWidget scripting) {\n\t\tthis.scripting = scripting;\n\t}\n\n\tpublic void setHighlight(int lineNumber, Color color) throws BadLocationException {\n\t\ttry {\n\t\t\tif (textArea == null) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tpainter = new DefaultHighlighter.DefaultHighlightPainter(color);\n\t\t\tint startIndex = textArea.getLineStartOffset(lineNumber - 1);\n\t\t\tint endIndex = textArea.getLineEndOffset(lineNumber - 1);\n\n\t\t\tBowlerStudio.invokeLater(() -> {\n\t\t\t\ttextArea.moveCaretPosition(startIndex);\n\t\t\t});\n\n\t\t\tBowlerStudio.invokeLater(() -> {\n\t\t\t\ttry {\n\t\t\t\t\ttextArea.getHighlighter().addHighlight(startIndex, endIndex, painter);\n\t\t\t\t} catch (BadLocationException e) {\n\t\t\t\t\t// ISSUE_REPORTING_EXCEPTION_HANDLER.uncaughtException(Thread.currentThread(),\n\t\t\t\t\t// e);\n\n\t\t\t\t}\n\t\t\t});\n\t\t} catch (Throwable t) {\n\t\t\tt.printStackTrace();\n\t\t}\n\n\t}\n\n\tpublic void clearHighlits() {\n\t\tBowlerStudio.invokeLater(new Runnable() {\n\t\t\tpublic void run() {\n\t\t\t\thighlighter.removeAllHighlights();\n\t\t\t}\n\t\t});\n\t}\n\n\tpublic int getFontSize() {\n\t\treturn myFont.getSize();\n\t}\n\n\tpublic void setFontSize(int size) {\n\t\tmyFont = new Font(myFont.getName(), myFont.getStyle(), size);\n\t\tsetFontLoop();\n\t}\n\n\tprivate void setFontLoop() {\n\t\tBowlerStudio.runLater(Duration.ofMillis(200), (Runnable) () -> {\n\t\t\tThread.setDefaultUncaughtExceptionHandler(ISSUE_REPORTING_EXCEPTION_HANDLER);\n\t\t\tBowlerStudio.invokeLater(() -> {\n\t\t\t\ttry {\n\t\t\t\t\ttextArea.setFont(myFont);\n\t\t\t\t} catch (Throwable ex) {\n\t\t\t\t\tLog.error(ex);\n\t\t\t\t\tsetFontLoop();\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\t}\n\n\tpublic static LocalFileScriptTab getSelectedTab() {\n\t\treturn selectedTab;\n\t}\n\n\tpublic static void setSelectedTab(LocalFileScriptTab selectedTab) {\n\t\t// com.neuronrobotics.sdk.common.Log.error(\"Currently selected\n\t\t// \"+selectedTab.file.getAbsolutePath());\n\t\tLocalFileScriptTab.selectedTab = selectedTab;\n\t}\n\n\tpublic void insertString(String string) {\n\t\tint caretpos = textArea.getCaretPosition();\n\t\tString text = content;\n\t\tString substring = text.substring(0, caretpos);\n\t\tString substring2;\n\t\ttry {\n\t\t\tsubstring2 = text.substring(caretpos, text.length());\n\t\t} catch (java.lang.StringIndexOutOfBoundsException ex) {\n\t\t\tsubstring2 = \"\";\n\t\t}\n\t\tString combined = substring + string + substring2;\n\t\tonScriptChanged(text, combined, file);\n\t\tBowlerStudio.invokeLater(() -> {\n\t\t\ttry {\n\t\t\t\ttextArea.setCaretPosition(caretpos + string.length());\n\t\t\t} catch (Throwable ex) {\n\t\t\t\tex.printStackTrace();\n\t\t\t}\n\t\t});\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/tabs/WebTab.java",
    "content": "package com.neuronrobotics.bowlerstudio.tabs;\n\nimport com.neuronrobotics.bowlerstudio.BowlerStudio;\nimport com.neuronrobotics.bowlerstudio.BowlerStudioController;\nimport com.neuronrobotics.bowlerstudio.Tutorial;\nimport com.neuronrobotics.bowlerstudio.assets.AssetFactory;\nimport com.neuronrobotics.bowlerstudio.assets.FontSizeManager;\nimport com.neuronrobotics.bowlerstudio.scripting.PasswordManager;\nimport com.neuronrobotics.bowlerstudio.scripting.ScriptingWebWidget;\nimport com.neuronrobotics.sdk.common.Log;\nimport javafx.beans.value.ChangeListener;\nimport javafx.beans.value.ObservableValue;\nimport javafx.collections.ObservableList;\nimport javafx.concurrent.Worker.State;\nimport javafx.event.ActionEvent;\nimport javafx.event.Event;\nimport javafx.event.EventHandler;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Tab;\nimport javafx.scene.control.TextField;\nimport javafx.scene.layout.HBox;\nimport javafx.scene.layout.Priority;\nimport javafx.scene.layout.VBox;\nimport javafx.scene.web.WebEngine;\nimport javafx.scene.web.WebHistory;\nimport javafx.scene.web.WebView;\n\nimport java.awt.*;\nimport java.io.IOException;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.time.Duration;\n\npublic class WebTab extends Tab implements EventHandler<Event> {\n\n\tprivate String Current_URL = \"http://gist.github.com/\";\n\n\tprivate WebTab myTab;\n\tboolean loaded = false;\n\tboolean initialized = false;\n\tprivate WebView webView;\n\tprivate WebEngine webEngine;\n\tprivate VBox vBox;\n\tprivate Button goButton = new Button(\"\");\n\tprivate Button homeButton = new Button(\"\");\n\tprivate Button backButton = new Button(\"\");\n\tprivate Button forwardButton = new Button(\"\");\n\n\tprivate TextField urlField;\n\t// private String currentAddress;\n\tprivate ScriptingWebWidget scripting;\n\tprivate Graphics2D splashGraphics;\n\tprivate static boolean firstBoot = true;\n\n\tprivate boolean isTutorialTab = false;\n\n\tprivate boolean finishedLoadingScriptingWidget;\n\n\tprivate static BowlerStudioController controller;\n\n\tpublic WebTab(String title, String Url) throws IOException, InterruptedException {\n\t\tthis(title, Url, false);\n\t}\n\n\t@SuppressWarnings(\"restriction\")\n\tpublic WebTab(String title, String Url, boolean isTutorialTab) throws IOException, InterruptedException {\n\n\t\tif (isTutorialTab) {\n\t\t\tsetGraphic(AssetFactory.loadIcon(\"Tutorial-Tab.png\"));\n\t\t} else\n\t\t\tsetGraphic(AssetFactory.loadIcon(\"Web-Tab.png\"));\n\t\tgoButton.setGraphic(AssetFactory.loadIcon(\"Go-Refresh.png\"));\n\t\thomeButton.setGraphic(AssetFactory.loadIcon(\"Home.png\"));\n\t\tbackButton.setGraphic(AssetFactory.loadIcon(\"Back-Button.png\"));\n\t\tforwardButton.setGraphic(AssetFactory.loadIcon(\"Forward-Button.png\"));\n\n\t\tthis.isTutorialTab = isTutorialTab;\n\t\tmyTab = this;\n\n\t\tif (title == null)\n\t\t\tmyTab.setText(\"               \");\n\t\telse\n\t\t\tmyTab.setText(title);\n\t\tLog.debug(\"Loading Gist Tab: \" + Url);\n\t\twebView = new WebView();\n\t\twebEngine = webView.getEngine();\n\t\t// webEngine.setUserAgent(\"bowlerstudio\");\n\t\tif (Url != null)\n\t\t\tCurrent_URL = Url;\n\n\t\tloaded = false;\n\t\tsetOnCloseRequest(this);\n\t\twebEngine.getLoadWorker().workDoneProperty().addListener(\n\t\t\t\t(ChangeListener<Number>) (observableValue, oldValue, newValue) -> BowlerStudio.runLater(() -> {\n\t\t\t\t\tif (!(newValue.intValue() < 100)) {\n\t\t\t\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"Just finished!\n\t\t\t\t\t\t// \"+webEngine.getLocation());\n\n\t\t\t\t\t\tnew Thread() {\n\t\t\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\t\t\tif (!initialized) {\n\t\t\t\t\t\t\t\t\tinitialized = true;\n\t\t\t\t\t\t\t\t\tloaded = true;\n\t\t\t\t\t\t\t\t\tsetName(\"Start finalizing components\");\n\t\t\t\t\t\t\t\t\tfinishLoadingComponents();\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\telse {\n\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\tgetScripting().loadCodeFromGist(Current_URL, webEngine);\n\t\t\t\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\t\t\t\t\t\t// e.printStackTrace();\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}.start();\n\n\t\t\t\t\t} else {\n\t\t\t\t\t\tloaded = false;\n\t\t\t\t\t\t// if(splashGraphics!=null && splash.isVisible()){\n\t\t\t\t\t\t// //BowlerStudio.renderSplashFrame(splashGraphics, newValue.intValue());\n\t\t\t\t\t\t// //splash.update();\n\t\t\t\t\t\t// }\n\t\t\t\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"Not Done Loading to:\n\t\t\t\t\t\t// \"+webEngine.getLocation());\n\t\t\t\t\t}\n\t\t\t\t}));\n\t\turlField = new TextField(Current_URL);\n\t\twebEngine.locationProperty().addListener(new ChangeListener<String>() {\n\t\t\t@Override\n\t\t\tpublic void changed(ObservableValue<? extends String> observable1, String oldValue, String newValue) {\n\n\t\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"Location Changed: \"+newValue);\n\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\turlField.setText(newValue);\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\n\t\t// goButton.setDefaultButton(true);\n\n\t\twebEngine.getLoadWorker().stateProperty().addListener(new ChangeListener<Object>() {\n\t\t\tpublic void changed(ObservableValue<?> observable, Object oldValue, Object newValue) {\n\t\t\t\tif (State.SUCCEEDED == newValue) {\n\t\t\t\t\tCurrent_URL = urlField.getText().startsWith(\"http://\") || urlField.getText().startsWith(\"https://\")\n\t\t\t\t\t\t\t|| urlField.getText().startsWith(\"file:\")\n\t\t\t\t\t\t\t\t\t? urlField.getText()\n\t\t\t\t\t\t\t\t\t: \"http://\" + urlField.getText();\n\n\t\t\t\t\tLog.debug(\"Load Worker State Changed \" + Current_URL);\n\t\t\t\t\tif (!processNewTab(urlField.getText())) {\n\t\t\t\t\t\tgoBack();\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// Log.error(\"State load fault: \"+newValue+\" object:\" +observable);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tbackButton.setOnAction(arg0 -> {\n\t\t\tgoBack();\n\t\t});\n\t\tforwardButton.setOnAction(arg0 -> {\n\t\t\t// Auto-generated method stub\n\t\t\tgoForward();\n\t\t});\n\t\thomeButton.setOnAction(arg0 -> {\n\t\t\t// Auto-generated method stub\n\t\t\ttry {\n\t\t\t\tloadUrl(Tutorial.getHomeUrl());\n\t\t\t} catch (Exception e) {\n\t\t\t\t// Auto-generated catch block\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t});\n\n\t\t// Layout logic\n\t\tHBox hBox = new HBox(5);\n\t\thBox.getChildren().setAll(backButton, forwardButton, homeButton, goButton, urlField);\n\t\tHBox.setHgrow(urlField, Priority.ALWAYS);\n\n\t\tvBox = new VBox(5);\n\t\tvBox.getChildren().setAll(hBox, webView);\n\t\tVBox.setVgrow(webView, Priority.ALWAYS);\n\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\tdouble scale = ((double) fontNum - 10) / 12.0;\n\t\t\tif (scale < 1)\n\t\t\t\tscale = 1;\n\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Web scale \" + scale);\n\t\t\tdouble s = scale;\n\t\t\tBowlerStudio.runLater(() -> webView.setZoom(s));\n\t\t});\n\t\tmyTab.setContent(vBox);\n\t\t// Action definition for the Button Go.\n\t\tEventHandler<ActionEvent> goAction = event -> {\n\t\t\tLog.debug(\"Hitting load\");\n\t\t\tif (processNewTab(urlField.getText())) {\n\t\t\t\tLog.debug(\"Loading \" + Current_URL);\n\t\t\t\tloadUrl(Current_URL);\n\t\t\t}\n\t\t};\n\t\turlField.setOnAction(goAction);\n\t\tgoButton.setOnAction(goAction);\n\t\t// Once all components are loaded, load URL\n\t\tBowlerStudio.runLater(Duration.ofMillis(200), new Runnable() {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\tgoAction.handle(null);\n\t\t\t}\n\t\t});\n\n\t}\n\n\tpublic void loadUrl(String url) {\n\n\t\tif (processNewTab(Current_URL)) {\n\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\twebEngine.load(url);\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate boolean processNewTab(String url) {\n\t\tCurrent_URL = urlField.getText().startsWith(\"http://\") || urlField.getText().startsWith(\"https://\")\n\t\t\t\t|| urlField.getText().startsWith(\"file:\") ? urlField.getText() : \"http://\" + urlField.getText();\n\t\tif (isTutorialTab) {\n\t\t\tif (!((Current_URL.toLowerCase().contains(\"commonwealthrobotics.com\")\n\t\t\t\t\t|| Current_URL.contains(\"gist.github.com/\" + PasswordManager.getUsername())\n\t\t\t\t\t|| Current_URL.contains(\"localhost\")))) {\n\t\t\t\ttry {\n\n\t\t\t\t\tLog.error(\"Non demo page found, opening new tab \" + Current_URL);\n\t\t\t\t\tBowlerStudioController.getBowlerStudio().addTab(new WebTab(null, Current_URL), true);\n\t\t\t\t\treturn false;\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tLog.debug(\"no load new tab\");\n\t\t\tif (getScripting() != null) {\n\t\t\t\ttry {\n\t\t\t\t\tmyTab.setText(getScripting().getFileName());\n\t\t\t\t} catch (java.lang.NullPointerException ex) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tgetScripting().loadCodeFromGist(Current_URL, webEngine);\n\t\t\t\t\t\tmyTab.setText(getScripting().getFileName());\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\t\te.printStackTrace();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate void finishLoadingComponents() {\n\t\t// com.neuronrobotics.sdk.common.Log.error(\"Finalizing:\n\t\t// \"+webEngine.getLocation());\n\t\ttry {\n\n\t\t\tif (getScripting() != null) {\n\t\t\t\t// when navagating to a new file, stop the script that is running\n\t\t\t\tgetScripting().stop();\n\t\t\t}\n\t\t} catch (Exception E) {\n\t\t\tE.printStackTrace();\n\t\t}\n\t\tnew Thread() {\n\t\t\tpublic void run() {\n\t\t\t\tfinishedLoadingScriptingWidget = false;\n\t\t\t\ttry {\n\t\t\t\t\tsetScripting(new ScriptingWebWidget(null, Current_URL, webEngine));\n\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\tvBox.getChildren().add(getScripting());\n\t\t\t\t\t\tif (!isTutorialTab) {\n\t\t\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\t\t\ttry {\n\n\t\t\t\t\t\t\t\t\tmyTab.setText(getScripting().getFileName());\n\t\t\t\t\t\t\t\t} catch (java.lang.NullPointerException ex) {\n\t\t\t\t\t\t\t\t\t// web page contains no gist\n\t\t\t\t\t\t\t\t\tex.printStackTrace();\n\t\t\t\t\t\t\t\t\tmyTab.setText(\"Web\");\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tloadCode();\n\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t} else\n\t\t\t\t\t\t\tfinishedLoadingScriptingWidget = true;\n\t\t\t\t\t});\n\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\tex.printStackTrace();\n\t\t\t\t\tfinishedLoadingScriptingWidget = true;\n\t\t\t\t}\n\n\t\t\t}\n\t\t}.start();\n\t}\n\n\tprivate void loadCode() {\n\t\tnew Thread(() -> {\n\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Downloading code from \" + Current_URL);\n\t\t\ttry {\n\t\t\t\tgetScripting().loadCodeFromGist(Current_URL, webEngine);\n\t\t\t} catch (Exception e) {\n\t\t\t\t// Auto-generated catch block\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t}).start();\n\t}\n\n\tpublic String goBack() {\n\t\t// new Exception().printStackTrace(System.err);\n\t\tfinal WebHistory history = webEngine.getHistory();\n\t\tObservableList<WebHistory.Entry> entryList = history.getEntries();\n\t\tint currentIndex = history.getCurrentIndex();\n\t\t// Out(\"currentIndex = \"+currentIndex);\n\t\t// Out(entryList.toString().replace(\"],\",\"]\\n\"));\n\n\t\tBowlerStudio.runLater(() -> {\n\t\t\ttry {\n\t\t\t\thistory.go(-1);\n\t\t\t} catch (Exception e) {\n\t\t\t\t// e.printStackTrace();\n\t\t\t}\n\t\t});\n\t\treturn entryList.get(currentIndex > 0 ? currentIndex - 1 : currentIndex).getUrl();\n\t}\n\n\tpublic String goForward() {\n\t\tfinal WebHistory history = webEngine.getHistory();\n\t\tObservableList<WebHistory.Entry> entryList = history.getEntries();\n\t\tint currentIndex = history.getCurrentIndex();\n\t\t// Out(\"currentIndex = \"+currentIndex);\n\t\t// Out(entryList.toString().replace(\"],\",\"]\\n\"));\n\n\t\tBowlerStudio.runLater(() -> {\n\t\t\ttry {\n\t\t\t\thistory.go(1);\n\t\t\t} catch (IndexOutOfBoundsException ex) {\n\t\t\t}\n\t\t});\n\t\treturn entryList.get(currentIndex < entryList.size() - 1 ? currentIndex + 1 : currentIndex).getUrl();\n\t}\n\n\tpublic static String getDomainName(String url) throws URISyntaxException {\n\t\tURI uri = new URI(url);\n\t\tString domain = uri.getHost();\n\t\treturn domain.startsWith(\"www.\") ? domain.substring(4) : domain;\n\t}\n\n\t@Override\n\tpublic void handle(Event event) {\n\t\tif (getScripting() != null)\n\t\t\tgetScripting().stop();\n\t}\n\n\tpublic ScriptingWebWidget getScripting() {\n\t\treturn scripting;\n\t}\n\n\tpublic void setScripting(ScriptingWebWidget scripting) {\n\t\tthis.scripting = scripting;\n\n\t\tscripting.addIScriptEventListener(controller);\n\t}\n\n\tpublic static void setBSController(BowlerStudioController controller) {\n\t\tWebTab.controller = controller;\n\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/tabs/WebTabController.java",
    "content": "/**\n * Sample Skeleton for 'WebTabLayout.fxml' Controller Class\n */\n\npackage com.neuronrobotics.bowlerstudio.tabs;\n\nimport java.net.URL;\nimport java.time.Duration;\nimport java.util.ResourceBundle;\n\nimport com.neuronrobotics.bowlerstudio.BowlerStudio;\nimport com.neuronrobotics.bowlerstudio.assets.AssetFactory;\nimport com.neuronrobotics.bowlerstudio.assets.FontSizeManager;\n\nimport javafx.beans.value.ChangeListener;\nimport javafx.beans.value.ObservableValue;\nimport javafx.event.ActionEvent;\nimport javafx.fxml.FXML;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.ComboBox;\nimport javafx.scene.control.ScrollPane;\nimport javafx.scene.control.Tab;\nimport javafx.scene.control.TabPane;\nimport javafx.scene.control.TextField;\nimport javafx.scene.layout.VBox;\nimport javafx.scene.web.WebEngine;\nimport javafx.scene.web.WebView;\n\npublic class WebTabController {\n\tprivate boolean initialized = false;\n\n\t@FXML // ResourceBundle that was given to the FXMLLoader\n\tprivate ResourceBundle resources;\n\n\t@FXML // URL location of the FXML file that was given to the FXMLLoader\n\tprivate URL location;\n\n\t@FXML // fx:id=\"pane\"\n\tprivate TabPane pane; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"tab\"\n\tprivate Tab tab; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"back\"\n\tprivate Button back; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"forward\"\n\tprivate Button forward; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"home\"\n\tprivate Button home; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"refresh\"\n\tprivate Button refresh; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"urlField\"\n\tprivate TextField urlField; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"run\"\n\tprivate Button run; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"iconHolder\"\n\tprivate VBox iconHolder; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"copy\"\n\tprivate Button copy; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"fileCHoice\"\n\tprivate ComboBox<String> fileCHoice; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"scrollpane\"\n\tprivate ScrollPane scrollpane; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"webview\"\n\tprivate WebView webview; // Value injected by FXMLLoader\n\n\tprivate WebEngine webEngine;\n\n\tprivate String Current_URL;\n\n\t@FXML\n\tvoid onCopy(ActionEvent event) {\n\n\t}\n\n\t@FXML\n\tvoid onFileSelect(ActionEvent event) {\n\n\t}\n\n\t@FXML\n\tvoid onRunStop(ActionEvent event) {\n\n\t}\n\n\t@FXML // This method is called by the FXMLLoader when initialization is complete\n\tvoid initialize() {\n\t\tassert pane != null : \"fx:id=\\\"pane\\\" was not injected: check your FXML file 'WebTabLayout.fxml'.\";\n\t\tassert back != null : \"fx:id=\\\"back\\\" was not injected: check your FXML file 'WebTabLayout.fxml'.\";\n\t\tassert forward != null : \"fx:id=\\\"forward\\\" was not injected: check your FXML file 'WebTabLayout.fxml'.\";\n\t\tassert home != null : \"fx:id=\\\"home\\\" was not injected: check your FXML file 'WebTabLayout.fxml'.\";\n\t\tassert refresh != null : \"fx:id=\\\"refresh\\\" was not injected: check your FXML file 'WebTabLayout.fxml'.\";\n\t\tassert urlField != null : \"fx:id=\\\"urlField\\\" was not injected: check your FXML file 'WebTabLayout.fxml'.\";\n\t\tassert run != null : \"fx:id=\\\"run\\\" was not injected: check your FXML file 'WebTabLayout.fxml'.\";\n\t\tassert iconHolder != null : \"fx:id=\\\"iconHolder\\\" was not injected: check your FXML file 'WebTabLayout.fxml'.\";\n\t\tassert copy != null : \"fx:id=\\\"copy\\\" was not injected: check your FXML file 'WebTabLayout.fxml'.\";\n\t\tassert fileCHoice != null : \"fx:id=\\\"fileCHoice\\\" was not injected: check your FXML file 'WebTabLayout.fxml'.\";\n\t\t// assert scrollpane != null : \"fx:id=\\\"scrollpane\\\" was not injected: check\n\t\t// your FXML file 'WebTabLayout.fxml'.\";\n\t\tassert webview != null : \"fx:id=\\\"webview\\\" was not injected: check your FXML file 'WebTabLayout.fxml'.\";\n\t\twebEngine = webview.getEngine();\n\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\tdouble scale = ((double) fontNum - 10) / 12.0;\n\t\t\tif (scale < 1)\n\t\t\t\tscale = 1;\n\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Web scale \" + scale);\n\t\t\t// webview.setScaleX(scale);\n\t\t\t// webview.setScaleY(scale);\n\t\t\tdouble s = scale;\n\t\t\tBowlerStudio.runLater(() -> webview.setZoom(s));\n\t\t});\n\t\tscrollpane.setFitToHeight(true);\n\t\tscrollpane.setFitToWidth(true);\n\n\t\trefresh.setGraphic(AssetFactory.loadIcon(\"Go-Refresh.png\"));\n\t\thome.setGraphic(AssetFactory.loadIcon(\"Home.png\"));\n\t\tback.setGraphic(AssetFactory.loadIcon(\"Back-Button.png\"));\n\t\tforward.setGraphic(AssetFactory.loadIcon(\"Forward-Button.png\"));\n\t\tBowlerStudio.setToRunButton(run);\n\t\twebEngine.locationProperty().addListener(new ChangeListener<String>() {\n\t\t\t@Override\n\t\t\tpublic void changed(ObservableValue<? extends String> observable1, String oldValue, String newValue) {\n\n\t\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"Location Changed: \"+newValue);\n\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\turlField.setText(newValue);\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\n\t\tsetInitialized(true);\n\t\tBowlerStudio.runLater(Duration.ofMillis(200), () -> {\n\t\t\tloadUrl(\"https://commonwealthrobotics.com/BowlerStudio/Welcome-To-BowlerStudio/\");\n\t\t});\n\t}\n\n\tpublic void loadUrl(String url) {\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tCurrent_URL = url.startsWith(\"http://\") || url.startsWith(\"https://\") || url.startsWith(\"file:\")\n\t\t\t\t\t? url\n\t\t\t\t\t: \"http://\" + url;\n\t\t\twebEngine.load(Current_URL);\n\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Go TO URL \" + url);\n\t\t});\n\n\t}\n\n\t/**\n\t * @return the initialized\n\t */\n\tpublic boolean isInitialized() {\n\t\treturn initialized;\n\t}\n\n\t/**\n\t * @param initialized\n\t *            the initialized to set\n\t */\n\tprivate void setInitialized(boolean initialized) {\n\t\tthis.initialized = initialized;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/tabs/WebTabFactory.java",
    "content": "package com.neuronrobotics.bowlerstudio.tabs;\nimport javafx.scene.control.Tab;\npublic class WebTabFactory {\n\n\tpublic static Tab WebTab(String title, String Url, boolean isTutorialTab) {\n\t\treturn null;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/tabs/WebTabTest.java",
    "content": "package com.neuronrobotics.bowlerstudio.tabs;\n\nimport java.io.File;\n\nimport com.neuronrobotics.bowlerstudio.BowlerStudio;\nimport com.neuronrobotics.bowlerstudio.assets.AssetFactory;\nimport com.neuronrobotics.bowlerstudio.assets.FontSizeManager;\n\nimport eu.mihosoft.vrl.v3d.JavaFXInitializer;\nimport javafx.application.Application;\nimport javafx.fxml.FXMLLoader;\nimport javafx.scene.Parent;\nimport javafx.scene.Scene;\nimport javafx.stage.Stage;\n\npublic class WebTabTest extends Application {\n\tprivate WebTabController tw;\n\n\t@Override\n\tpublic void start(Stage primaryStage) throws Exception {\n\t\tFXMLLoader loader = AssetFactory.loadLayout(\"layout/WebTabLayout.fxml\");\n\t\tloader.setClassLoader(WebTabController.class.getClassLoader());\n\t\tParent w = loader.load();\n\n\t\ttw = loader.getController();\n\n\t\tFile layoutFile = AssetFactory.loadFile(\"layout/default.css\");\n\t\tString nwfile = layoutFile.toURI().toString().replace(\"file:/\", \"file:///\");\n\t\tScene scene = new Scene(w);\n\n\t\tscene.getStylesheets().clear();\n\t\tscene.getStylesheets().add(nwfile);\n\t\tcom.neuronrobotics.sdk.common.Log.error(\"Loading CSS from \" + nwfile);\n\n\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\tint tmp = fontNum - 10;\n\t\t\tif (tmp < 12)\n\t\t\t\ttmp = 12;\n\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Setting font size to \" + fontNum);\n\t\t\tw.setStyle(\"-fx-font-size: \" + tmp + \"pt\");\n\t\t});\n\t\tprimaryStage.setOnCloseRequest(arg0 -> {\n\t\t\tSystem.exit(0);\n\t\t});\n\n\t\tprimaryStage.setScene(scene);\n\t\tprimaryStage.setWidth(600);\n\t\tprimaryStage.setHeight(777);\n\t\tprimaryStage.setTitle(\"Web Tab Test Application\");\n\t\tprimaryStage.show();\n\n\t}\n\n\tpublic static void main(String[] args) {\n\t\tJavaFXInitializer.go();\n\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tStage s = new Stage();\n\t\t\t//\n\t\t\tWebTabTest controller = new WebTabTest();\n\t\t\ttry {\n\t\t\t\tcontroller.start(s);\n\n\t\t\t} catch (Exception e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t\tWebTabController tw = controller.getTw();\n\t\t\tnew Thread(() -> {\n\t\t\t\tFontSizeManager.setFontSize(48);\n\t\t\t}).start();\n\t\t});\n\t}\n\n\t/**\n\t * @return the tw\n\t */\n\tpublic WebTabController getTw() {\n\t\treturn tw;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/threed/AssemblySlider.java",
    "content": "package com.neuronrobotics.bowlerstudio.threed;\n\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.Optional;\nimport java.util.Set;\n\nimport com.neuronrobotics.bowlerstudio.physics.TransformFactory;\nimport com.neuronrobotics.sdk.addons.kinematics.math.TransformNR;\nimport eu.mihosoft.vrl.v3d.CSG;\nimport eu.mihosoft.vrl.v3d.PropertyStorage;\nimport eu.mihosoft.vrl.v3d.Transform;\nimport javafx.scene.control.Slider;\nimport javafx.beans.value.ChangeListener;\nimport javafx.beans.value.ObservableValue;\nimport javafx.scene.transform.Affine;\n\npublic class AssemblySlider {\n\tpublic static Slider getSlider(Set<CSG> listOfObjects) {\n\t\tint s = 0;\n\n\t\tCSG[] array = listOfObjects.toArray(new CSG[0]);\n\t\tfor (int i = 0; i < array.length; i++) {\n\t\t\tCSG c = array[i];\n\t\t\tPropertyStorage incomingGetStorage = c.getAssemblyStorage();\n\t\t\tif (incomingGetStorage.getValue(\"MaxAssemblyStep\") != Optional.empty()) {\n\t\t\t\tInteger max = (Integer) incomingGetStorage.getValue(\"MaxAssemblyStep\").get();\n\t\t\t\tif (max > s) {\n\t\t\t\t\ts = max;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tint numSteps = s;\n\t\tSlider slider = new Slider(0, numSteps, numSteps);\n\t\tslider.setShowTickMarks(true);\n\t\tslider.setShowTickLabels(true);\n\t\tslider.setMajorTickUnit(0.25f);\n\t\tslider.setBlockIncrement(0.1f);\n\t\tslider.setPrefWidth(300);\n\t\tslider.valueProperty().addListener(new ChangeListener<Number>() {\n\t\t\tpublic void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {\n\t\t\t\tint step = (int) (newValue.doubleValue() + 1);\n\t\t\t\tdouble fraction = -1 * (newValue.doubleValue() - step);\n\n\t\t\t\tfor (Iterator<CSG> iterator = listOfObjects.iterator(); iterator.hasNext();) {\n\t\t\t\t\tCSG c = iterator.next();\n\t\t\t\t\tPropertyStorage incomingGetStorage = c.getAssemblyStorage();\n\t\t\t\t\tString key = \"AssemblySteps\";\n\t\t\t\t\tif (incomingGetStorage.getValue(key) != Optional.empty()) {\n\t\t\t\t\t\tHashMap<Integer, Transform> map = (HashMap<Integer, Transform>) incomingGetStorage.getValue(key)\n\t\t\t\t\t\t\t\t.get();\n\t\t\t\t\t\tboolean set = false;\n\t\t\t\t\t\tTransformNR target = new TransformNR();\n\t\t\t\t\t\tfor (int i = step; i <= numSteps; i++) {\n\t\t\t\t\t\t\tif (map.get(i) != null) {\n\t\t\t\t\t\t\t\tdouble myScale = (i == step) ? fraction : 1;\n\t\t\t\t\t\t\t\tTransformNR scaled = TransformFactory.csgToNR(map.get(i)).scale(myScale);\n\t\t\t\t\t\t\t\ttarget = target.times(scaled);\n\t\t\t\t\t\t\t\t// println c.getName()+\" sliderval=\"+newValue+\" step=\"+step+\"\n\t\t\t\t\t\t\t\t// fraction:\"+myScale+\" || \"+i\n\t\t\t\t\t\t\t\tTransformFactory.nrToAffine(target,\n\t\t\t\t\t\t\t\t\t\t(Affine) incomingGetStorage.getValue(\"AssembleAffine\").get());\n\t\t\t\t\t\t\t\tset = true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!set) {\n\t\t\t\t\t\t\tTransformFactory.nrToAffine(new TransformNR(),\n\t\t\t\t\t\t\t\t\t(Affine) incomingGetStorage.getValue(\"AssembleAffine\").get());\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tif (numSteps == 0)\n\t\t\tslider.setDisable(true);\n\t\treturn slider;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/threed/Axis.java",
    "content": "package com.neuronrobotics.bowlerstudio.threed;\n\nimport java.util.Arrays;\n\nimport com.neuronrobotics.bowlerstudio.BowlerStudio;\n\nimport eu.mihosoft.vrl.v3d.CSG;\nimport eu.mihosoft.vrl.v3d.Cube;\nimport eu.mihosoft.vrl.v3d.TextExtrude;\nimport javafx.scene.text.Font;\n\n/*\n *      Axis.java 1.0 98/11/25\n *\n * Copyright (c) 1998 Sun Microsystems, Inc. All Rights Reserved.\n *\n * Sun grants you (\"Licensee\") a non-exclusive, royalty free, license to use,\n * modify and redistribute this software in source and binary code form,\n * provided that i) this copyright notice and license appear on all copies of\n * the software; and ii) Licensee does not utilize the software in a manner\n * which is disparaging to Sun.\n *\n * This software is provided \"AS IS,\" without a warranty of any kind. ALL\n * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY\n * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR\n * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE\n * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING\n * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS\n * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,\n * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER\n * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF\n * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGES.\n *\n * This software is not designed or intended for use in on-line control of\n * aircraft, air traffic, aircraft navigation or aircraft communications; or in\n * the design, construction, operation or maintenance of any nuclear\n * facility. Licensee represents and warrants that it will not use or\n * redistribute the Software for such purposes.\n */\n\n/*\n * Getting Started with the Java 3D API\n * written in Java 3D\n *\n * This program demonstrates:\n *   1. writing a visual object class\n *      In this program, Axis class defines a visual object\n *      This particular class extends Shape3D\n *      See the text for a discussion.\n *   2. Using LineArray to draw 3D lines.\n */\n\nimport javafx.scene.Group;\nimport javafx.scene.Node;\nimport javafx.scene.paint.Color;\nimport javafx.scene.transform.Affine;\n\n//  Auto-generated Javadoc\n/**\n * The Class Axis.\n */\npublic class Axis extends Group {\n\n\tprivate CSG xAxis;\n\tprivate CSG yAxis;\n\tprivate CSG zAxis;\n\tprivate CSG xText;\n\tprivate CSG yText;\n\tprivate CSG zText;\n\n\t/**\n\t * Instantiates a new XYZ-axis.\n\t */\n\tpublic Axis(boolean visible) {\n\t\tthis(50, visible);\n\t}\n\n\t// //////////////////////////////////////////\n\t//\n\t// Create XYZ-axis visual object\n\t/**\n\t * Instantiates a new axis.\n\t *\n\t * @param length\n\t *            the axis length\n\t */\n\t//\n\tpublic Axis(int length, boolean visible) {\n\t\tdouble strokeWidth = 0.5;\n\n\t\tFont font = new Font(Font.getDefault().getName(), 5);\n\n\t\txText = CSG.unionAll(TextExtrude.text((double) strokeWidth, \"x\", font)).rotz(90).toXMin().movex(length)\n\t\t\t\t.moveToCenterY().toZMax();\n\t\txAxis = new Cube(length, strokeWidth, strokeWidth).toCSG().toXMin().toZMax().movex(strokeWidth / 2);\n\t\txAxis.setColor(Color.RED);\n\t\txText.setColor(Color.RED);\n\n\t\tyText = CSG.unionAll(TextExtrude.text((double) strokeWidth, \"y\", font)).rotz(90).mirrory().toYMin()\n\t\t\t\t.movey(length).moveToCenterX().toZMax();\n\t\tyAxis = new Cube(strokeWidth, length, strokeWidth).toCSG().toYMin().toZMax().movey(strokeWidth / 2);\n\t\tyText.setColor(Color.GREEN);\n\t\tyAxis.setColor(Color.GREEN);\n\n\t\tAffine zTextAffine = new Affine();\n\t\tzTextAffine.setTx(length / 2);\n\t\tzTextAffine.setTz(length / 2);\n\t\tzTextAffine.appendRotation(90, 0, 0, 0, 1, 0, 1);\n\t\tzText = CSG.unionAll(TextExtrude.text((double) strokeWidth, \"z\", font)).rotx(90).rotz(90).mirrory()\n\t\t\t\t.movez(length).moveToCenterY();\n\n\t\tzAxis = new Cube(strokeWidth, strokeWidth, length).toCSG().toZMin().movez(-strokeWidth);\n\t\tzText.setColor(Color.BLUE);\n\t\tzAxis.setColor(Color.BLUE);\n\n\t\tif (visible)\n\t\t\tshow();\n\t\telse\n\t\t\thide();\n\t}\n\n\tpublic void show() {\n\t\tBowlerStudio.runLater(() -> showAll());\n\t}\n\n\tprivate void showAll() {\n\t\ttry {\n\t\t\tfor (Node n : Arrays.asList(xAxis.getMesh(), yAxis.getMesh(), zAxis.getMesh(), xText.getMesh(),\n\t\t\t\t\tyText.getMesh(), zText.getMesh())) {\n\t\t\t\ttry {\n\t\t\t\t\tn.setPickOnBounds(false);\n\t\t\t\t\tn.setMouseTransparent(true);\n\t\t\t\t\tgetChildren().add(n);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception ex) {\n\t\t\t// no exception on exit\n\t\t}\n\t}\n\n\tpublic void hide() {\n\t\tBowlerStudio.runLater(() -> getChildren().clear());\n\t}\n\n} // end of class Axis\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/threed/BowlerStudio3dEngine.java",
    "content": "package com.neuronrobotics.bowlerstudio.threed;\n\nimport com.neuronrobotics.bowlerkernel.Bezier3d.Manipulation;\nimport com.neuronrobotics.bowlerstudio.BowlerKernel;\nimport com.neuronrobotics.bowlerstudio.BowlerStudio;\n\n/*\n * Copyright (c) 2011, 2013 Oracle and/or its affiliates.\n * All rights reserved. Use is subject to license terms.\n *\n * This file is available and licensed under the following license:\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *\n *  - Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  - Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the distribution.\n *  - Neither the name of Oracle nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\nimport com.neuronrobotics.bowlerstudio.BowlerStudioController;\nimport com.neuronrobotics.bowlerstudio.BowlerStudioModularFrame;\nimport com.neuronrobotics.bowlerstudio.ConnectionManager;\n//import com.neuronrobotics.bowlerstudio.CreatureLab3dController;\nimport com.neuronrobotics.bowlerstudio.IssueReportingExceptionHandler;\n//import com.neuronrobotics.bowlerstudio.assets.AssetFactory;\nimport com.neuronrobotics.bowlerstudio.creature.CadFileExporter;\nimport com.neuronrobotics.bowlerstudio.creature.EngineeringUnitsSliderWidget;\nimport com.neuronrobotics.bowlerstudio.creature.IMobileBaseUI;\nimport com.neuronrobotics.bowlerstudio.creature.IOnEngineeringUnitsChange;\nimport com.neuronrobotics.bowlerstudio.physics.TransformFactory;\nimport com.neuronrobotics.bowlerstudio.scripting.CaDoodleLoader;\nimport com.neuronrobotics.bowlerstudio.scripting.ScriptingEngine;\nimport com.neuronrobotics.bowlerstudio.scripting.cadoodle.CaDoodleFile;\nimport com.neuronrobotics.imageprovider.AbstractImageProvider;\nimport com.neuronrobotics.imageprovider.IVirtualCameraFactory;\nimport com.neuronrobotics.imageprovider.VirtualCameraFactory;\nimport com.neuronrobotics.nrconsole.util.FileSelectionFactory;\n//import com.neuronrobotics.nrconsole.util.FileSelectionFactory;\nimport com.neuronrobotics.sdk.addons.kinematics.math.RotationNR;\nimport com.neuronrobotics.sdk.addons.kinematics.math.TransformNR;\nimport com.neuronrobotics.sdk.common.BowlerAbstractDevice;\nimport com.neuronrobotics.sdk.common.DMDevice;\nimport com.neuronrobotics.sdk.common.Log;\nimport eu.mihosoft.vrl.v3d.CSG;\nimport eu.mihosoft.vrl.v3d.Vector3d;\nimport eu.mihosoft.vrl.v3d.Vertex;\nimport eu.mihosoft.vrl.v3d.Cylinder;\nimport eu.mihosoft.vrl.v3d.JavaFXInitializer;\nimport eu.mihosoft.vrl.v3d.MissingManipulatorException;\nimport eu.mihosoft.vrl.v3d.Polygon;\nimport eu.mihosoft.vrl.v3d.parametrics.CSGDatabase;\nimport eu.mihosoft.vrl.v3d.parametrics.CSGDatabaseInstance;\nimport eu.mihosoft.vrl.v3d.parametrics.IParameterChanged;\nimport eu.mihosoft.vrl.v3d.parametrics.LengthParameter;\nimport eu.mihosoft.vrl.v3d.parametrics.Parameter;\nimport javafx.application.Platform;\nimport javafx.collections.ObservableList;\n//import javafx.embed.swing.JFXPanel;\n//import javafx.embed.swing.SwingFXUtils;\nimport javafx.event.ActionEvent;\nimport javafx.event.EventHandler;\nimport javafx.event.EventType;\nimport javafx.geometry.Point2D;\nimport javafx.geometry.Point3D;\nimport javafx.scene.*;\nimport javafx.scene.control.*;\nimport javafx.scene.effect.BlendMode;\nimport javafx.scene.image.ImageView;\nimport javafx.scene.image.PixelWriter;\nimport javafx.scene.image.WritableImage;\nimport javafx.scene.input.DragEvent;\nimport javafx.scene.input.KeyEvent;\nimport javafx.scene.input.MouseButton;\nimport javafx.scene.input.MouseEvent;\nimport javafx.scene.input.ScrollEvent;\nimport javafx.scene.layout.Pane;\nimport javafx.scene.layout.AnchorPane;\nimport javafx.scene.layout.HBox;\nimport javafx.scene.paint.*;\nimport javafx.scene.paint.Color;\nimport javafx.scene.paint.PhongMaterial;\nimport javafx.scene.shape.Box;\nimport javafx.scene.shape.CullFace;\nimport javafx.scene.shape.DrawMode;\nimport javafx.scene.shape.MeshView;\nimport javafx.scene.shape.TriangleMesh;\nimport javafx.stage.Stage;\nimport javafx.scene.transform.Affine;\nimport javafx.scene.transform.Rotate;\nimport javafx.scene.transform.Scale;\nimport javafx.scene.transform.Transform;\nimport javafx.scene.transform.NonInvertibleTransformException;\nimport javafx.geometry.Bounds;\n\n// Development, for objectDistance methode\n//import com.sun.javafx.geom.PickRay;\n//import com.sun.javafx.geom.Vec3d;\n//import com.sun.javafx.scene.NodeHelper;\n//import com.sun.javafx.scene.input.PickResultChooser;\n\nimport javax.imageio.ImageIO;\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.*;\n\n/**\n * MoleculeSampleApp.\n */\npublic class BowlerStudio3dEngine implements ICameraChangeListener, IMobileBaseUI {\n\n\tprivate volatile boolean focusing = false;\n\tprivate volatile boolean abortFocus = false;\n\tprivate int NUMBER_OF_INTERPOLATION_STEPS = 30;\n\n\t/**\n\t *\n\t */\n\tprivate static final long serialVersionUID = 6744581340628622682L;\n\n\tprivate static final TransformNR autoSpinSpeed = new TransformNR(0, 0, 0, new RotationNR(0, 0.25, 0));\n\n\t/** The root group. */\n\tprivate final Group rootGroup = new Group();\n\n\t/** The workplane group. */\n\tprivate Group workplaneGroup;\n\n\t/** The custom workplane group. */\n\tprivate Group customWorkplaneGroup = new Group();\n\n\t/** The axis group. */\n\tfinal Group axisGroup = new Group();\n\n\t/** The grid group. */\n\tfinal Group gridGroup = new Group();\n\n\t/** The XYZ-ruler group. */\n\tprivate final Group rulerGroup = new Group();\n\tprivate Affine rulerOffset = new Affine();\n\tprivate Affine rulerInWorkplaneOffset = new Affine();\n\n\t/** The world. */\n\tfinal Xform world = new Xform();\n\n\t/** The camera. */\n\tfinal PerspectiveCamera camera = new PerspectiveCamera(true);\n\n\t/** The camera distance. */\n\tfinal double cameraDistance = 4000;\n\n\t/** The molecule group. */\n\tfinal Xform moleculeGroup = new Xform();\n\n\t/** The one frame. */\n\tdouble ONE_FRAME = 1.0 / 24.0;\n\n\t/** The delta multiplier. */\n\tdouble DELTA_MULTIPLIER = 200.0;\n\n\t/** The control multiplier. */\n\tdouble CONTROL_MULTIPLIER = 0.1;\n\n\t/** The shift multiplier. */\n\tdouble SHIFT_MULTIPLIER = 0.1;\n\n\t/** The alt multiplier. */\n\tdouble ALT_MULTIPLIER = 0.5;\n\n\t/** The mouse pos x. */\n\tdouble mousePosX;\n\n\t/** The mouse pos y. */\n\tdouble mousePosY;\n\n\t/** The mouse old x. */\n\tdouble mouseOldX;\n\tprivate boolean aboveSplit = false;\n\n\t/** The mouse old y. */\n\tdouble mouseOldY;\n\n\t/** The mouse delta x. */\n\tdouble mouseDeltaX;\n\n\t/** The mouse delta y. */\n\tdouble mouseDeltaY;\n\n\t/** The manipulator group. */\n\tprivate final Group manipulatorGroup = new Group();\n\n\t/** The look group. */\n\tprivate final Group lookGroup = new Group();\n\n\t/** The camera group. */\n\tprivate final Group cameraGroup = new Group();\n\n\t/** The user group for the user defined objects and the navigation cube */\n\tprivate final Group userGroup = new Group();\n\t// Skip first userGroup nodes, which are not user objects\n\tprivate static int SKIP_USERGROUP_NODES = 0;\n\n\t/** The scene. */\n\tprivate SubScene scene;\n\n\t/** The ground group. */\n\tprivate Group groundGroup;\n\n\tprivate Group group;\n\n\tprivate boolean captureMouse = false;\n\tprivate Button export;\n\n\tprivate VirtualCameraMobileBase flyingCamera;\n\tprivate Group handGroup;\n\tprivate double upDown = 0;\n\tprivate double leftRight = 0;\n\tprivate HashMap<CSG, MeshView> csgMap = new HashMap<>();\n\tprivate HashMap<CSG, File> csgSourceFile = new HashMap<>();\n\tprivate HashMap<MeshView, Axis> axisMap = new HashMap<>();\n\tprivate String lastFileSelected = \"\";\n\tprivate int lastFileLine = 0;\n\tprivate File defaultStlDir;\n\tprivate TransformNR defaultCameraView = new TransformNR(0, 0, 0, new RotationNR(90 - 127, 24, 0));\n\t// private static final TransformNR offsetForVisualization = new\n\t// TransformNR(0, 0, 0, new RotationNR(0,0, 0));\n\n\tprivate Button back;\n\tprivate Button fwd;\n\tprivate Button home;\n\tprivate int debuggerIndex = 0;\n\tprivate ArrayList<String> debuggerList = new ArrayList<>();\n\tprivate CSG selectedCsg = null;\n\n\tprivate long lastMosueMovementTime = System.currentTimeMillis();\n\n\t// private List<CSG> selectedSet = null;\n\tprivate TransformNR previousTarget = new TransformNR();\n\n\tprivate long lastSelectedTime = System.currentTimeMillis();\n\n\tprivate long timeForAutospin = 5000;\n\n\t// private CheckBox spin;\n\t// private CheckBox autoHighlight;\n\n\tprivate boolean rebuildingUIOnerror = false;\n\t// private static int sumVert = 0;\n\tprivate CheckMenuItem autoHighlight;\n\tprivate CheckMenuItem spin;\n\tprivate HBox controlsChecks;\n\tprivate Thread autospingThread = null;\n\tprivate CheckMenuItem showRuler;\n\tprivate TransformNR targetNR;\n\tprivate TransformNR poseToMove = new TransformNR();\n\tprivate ArrayList<ICameraChangeListener> listeners = new ArrayList<>();\n\tprivate Affine gridPlacementAffine = new Affine();\n\tprivate Group controlHandleGroup = new Group();\n\tprivate AmbientLight ambientLight = new AmbientLight(Color.color(1.0, 1.0, 1.0, 0));\n\tprivate volatile boolean waitingForCompletion;\n\n\tprivate Pane overlayPane = null;\n\n\tpublic void setOverlayPane(Pane overlayP) {\n\t\tthis.overlayPane = overlayP;\n\t}\n\n\tpublic Pane getOverlayPane() {\n\t\treturn overlayPane;\n\t}\n\n\tpublic double cameraDistanceToPixelPerMM() {\n\t\tdouble fovRad = Math.toRadians(camera.getFieldOfView());\n\t\t// 500mm projection plane\n\t\treturn getSubScene().getHeight() / (1000.0 * Math.tan(fovRad / 2.0));\n\t}\n\n\t// Converts a 3D world point to a 2D point in the scene/screen (default\n\t// workplane only)\n\tpublic Point2D worldToScene(Point3D world3D) {\n\t\tPoint2D screenPt = controlHandleGroup.getChildren().get(1).localToScreen(world3D);\n\t\treturn scene.screenToLocal(screenPt);\n\t}\n\n\t// Find the 3D world Z-point for a given 3D world X, Y point and the 2D\n\t// scene/screen point\n\tpublic double sceneToWorldFixedXY_WP(Point2D scenePixel, double fixedX, double fixedY) {\n\t\tTransform wp = gridPlacementAffine;\n\n\t\tVirtualCameraMobileBase cam = getVirtualcam();\n\t\tTransformNR c2w = cam.getCamerFrame().times(new TransformNR(0, 0, cam.getZoomDepth()));\n\t\tVector3d origin = new Vector3d(c2w.getX(), c2w.getY(), c2w.getZ());\n\n\t\tdouble ppm = cameraDistanceToPixelPerMM();\n\t\tdouble cx = overlayPane.getWidth() / 2.0;\n\t\tdouble cy = overlayPane.getHeight() / 2.0;\n\t\tdouble camX = -(scenePixel.getX() - cx) / ppm;\n\t\tdouble camY = -(scenePixel.getY() - cy) / ppm;\n\n\t\tVector3d target = new Vector3d(camX, camY, 500.0).transform(TransformFactory.nrToCSG(c2w));\n\t\tVector3d dir = target.minus(origin);\n\t\tdir.normalize();\n\n\t\ttry {\n\t\t\tTransform inv = wp.createInverse();\n\t\t\tPoint3D localOrigin = inv.transform(new Point3D(origin.x, origin.y, origin.z));\n\t\t\tPoint3D localDirPt = inv.deltaTransform(new Point3D(dir.x, dir.y, dir.z));\n\t\t\tVector3d localDir = new Vector3d(localDirPt.getX(), localDirPt.getY(), localDirPt.getZ());\n\n\t\t\tdouble ox = localOrigin.getX();\n\t\t\tdouble oy = localOrigin.getY();\n\t\t\tdouble dx = localDir.x;\n\t\t\tdouble dy = localDir.y;\n\n\t\t\t// Minimize distance squared to line\n\t\t\tdouble denom = dx * dx + dy * dy;\n\t\t\tdouble t;\n\t\t\tif (denom < 1e-9)\n\t\t\t\tt = 0; // Parallel, use origin\n\t\t\telse\n\t\t\t\tt = ((fixedX - ox) * dx + (fixedY - oy) * dy) / denom;\n\n\t\t\t// Limit range to within -1200 and +1200mm\n\t\t\treturn Math.max(-1200, Math.min(localOrigin.getZ() + t * localDir.z, 1200));\n\n\t\t} catch (NonInvertibleTransformException e) {\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\t// Find the 3D world X,Y-point for a given 3D world Z point and the 2D\n\t// scene/screen point\n\tpublic Point3D sceneToWorldFixedZ_WP(Point2D scenePixel, double fixedZ) {\n\t\tTransform wp = gridPlacementAffine;\n\n\t\t// Get camera ray in world space\n\t\tVirtualCameraMobileBase cam = getVirtualcam();\n\t\tTransformNR c2w = cam.getCamerFrame().times(new TransformNR(0, 0, cam.getZoomDepth()));\n\t\tVector3d origin = new Vector3d(c2w.getX(), c2w.getY(), c2w.getZ());\n\n\t\tdouble ppm = cameraDistanceToPixelPerMM();\n\t\tdouble cx = overlayPane.getWidth() / 2.0;\n\t\tdouble cy = overlayPane.getHeight() / 2.0;\n\t\tdouble camX = -(scenePixel.getX() - cx) / ppm;\n\t\tdouble camY = -(scenePixel.getY() - cy) / ppm;\n\n\t\tVector3d target = new Vector3d(camX, camY, 500.0).transform(TransformFactory.nrToCSG(c2w));\n\t\tVector3d dir = target.minus(origin);\n\t\tdir.normalize();\n\n\t\ttry {\n\t\t\tTransform inv = wp.createInverse();\n\t\t\tPoint3D localOrigin = inv.transform(new Point3D(origin.x, origin.y, origin.z));\n\t\t\tPoint3D localDirPt = inv.deltaTransform(new Point3D(dir.x, dir.y, dir.z));\n\t\t\tVector3d localDir = new Vector3d(localDirPt.getX(), localDirPt.getY(), localDirPt.getZ());\n\n\t\t\tdouble localZDir = localDir.z;\n\t\t\tif (Math.abs(localZDir) < 1e-9)\n\t\t\t\t// Ray is parallel to the Z plane, use origin's XY at fixedZ\n\t\t\t\treturn new Point3D(localOrigin.getX(), localOrigin.getY(), fixedZ);\n\n\t\t\tdouble t = (fixedZ - localOrigin.getZ()) / localZDir;\n\n\t\t\tdouble localX = localOrigin.getX() + t * localDir.x;\n\t\t\tdouble localY = localOrigin.getY() + t * localDir.y;\n\n\t\t\treturn new Point3D(localX, localY, fixedZ);\n\n\t\t} catch (NonInvertibleTransformException e) {\n\t\t\treturn new Point3D(0, 0, fixedZ);\n\t\t}\n\t}\n\n\t// Gives the scale factor for a 1MM object to produce 1 pixel on screen\n\tpublic double screenToSceneMMscale(Point3D scenePosition) {\n\n\t\tVirtualCameraMobileBase vcam = getVirtualcam();\n\t\tTransformNR c2w = vcam.getCamerFrame().times(new TransformNR(0, 0, vcam.getZoomDepth()));\n\n\t\t// Camera to object vector\n\t\tdouble dx = scenePosition.getX() - c2w.getX();\n\t\tdouble dy = scenePosition.getY() - c2w.getY();\n\t\tdouble dz = scenePosition.getZ() - c2w.getZ();\n\n\t\t// Camera rotation matrix\n\t\tdouble fwdX = c2w.getRotation().getRotationMatrix()[0][2];\n\t\tdouble fwdY = c2w.getRotation().getRotationMatrix()[1][2];\n\t\tdouble fwdZ = c2w.getRotation().getRotationMatrix()[2][2];\n\t\t// Project position vector onto camera forward axis, 0.1mm minimum distance\n\t\tdouble minDistance = Math.max(0.1, dx * fwdX + dy * fwdY + dz * fwdZ);\n\n\t\treturn (2.0 * minDistance * Math.tan(Math.toRadians(camera.getFieldOfView()) / 2.0))\n\t\t\t\t/ getSubScene().getHeight();\n\n\t}\n\n\tpublic BowlerStudio3dEngine addListener(ICameraChangeListener listener) {\n\t\tif (!listeners.contains(listener))\n\t\t\tlisteners.add(listener);\n\t\treturn this;\n\t}\n\n\tpublic BowlerStudio3dEngine removeListener(ICameraChangeListener listener) {\n\t\tif (listeners.contains(listener))\n\t\t\tlisteners.remove(listener);\n\t\treturn this;\n\t}\n\n\tprivate IControlsMap map = null;\n\n\tprivate double mouseScale = 2.0;\n\tprivate MeshView handMesh;\n\tprivate ImageView homeIcon;\n\tprivate ImageView generateIcon;\n\tprivate ImageView clearIcon;\n\tprivate boolean move = true;\n\tprivate boolean disabeControl = false;\n\tprivate String name;\n\n\t/**\n\t * Instantiates a new jfx3d manager.\n\t *\n\t * @param string\n\t */\n\tpublic BowlerStudio3dEngine(String name) {\n\t\tthis.name = name;\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tThread.currentThread().setUncaughtExceptionHandler(new IssueReportingExceptionHandler());\n\t\t});\n\t\tcom.neuronrobotics.sdk.common.Log.debug(\"Setting Scene \" + name);\n\t\tsetSubScene(new SubScene(getRoot(), 10, 10, true, SceneAntialiasing.BALANCED));\n\n\t\t// Show JavaFX diagnostics info\n\t\tModuleLayer.boot().modules().stream().filter(m -> m.getName().startsWith(\"javafx\"))\n\t\t\t\t.forEach(m -> com.neuronrobotics.sdk.common.Log.info(m.getName() + \": \" + m.getDescriptor().version()));\n\t}\n\n\tpublic void addObject(Object o, File source) {\n\t\taddObject(o, source, null);\n\t}\n\n\tpublic void addObject(Object o, File source, ArrayList<CSG> cache) {\n\t\ttry {\n\t\t\tif (List.class.isInstance(o)) {\n\t\t\t\tList<Object> c = (List<Object>) o;\n\t\t\t\tfor (int i = 0; i < c.size(); i++) {\n\t\t\t\t\t// Log.warning(\"Loading array Lists with removals \" + c.get(i));\n\t\t\t\t\taddObject(c.get(i), source, cache);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (CaDoodleFile.class.isInstance(o)) {\n\t\t\t\taddObject(CaDoodleLoader.process((CaDoodleFile) o, true), source, cache);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tjavafx.scene.paint.Color color = new javafx.scene.paint.Color(Math.random() * 0.5 + 0.5,\n\t\t\t\t\tMath.random() * 0.5 + 0.5, Math.random() * 0.5 + 0.5, 1);\n\t\t\tdouble stroke = 0.5;\n\t\t\tif (CSG.class.isInstance(o)) {\n\t\t\t\tCSG csg = (CSG) o;\n\t\t\t\tif (cache == null) {\n\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\t// new RuntimeException().printStackTrace();\n\t\t\t\t\t\taddObject(csg, source, csg.getColor().getOpacity(), CSGDatabase.getInstance());\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tcache.add(csg);\n\t\t\t\t}\n\n\t\t\t\treturn;\n\n\t\t\t} else if (Tab.class.isInstance(o)) {\n\n\t\t\t\tBowlerStudioController.getBowlerStudio().addTab((Tab) o, true);\n\n\t\t\t\treturn;\n\n\t\t\t} else if (Node.class.isInstance(o)) {\n\n\t\t\t\taddUserNode((Node) o);\n\t\t\t\treturn;\n\n\t\t\t} else if (Polygon.class.isInstance(o)) {\n\t\t\t\tPolygon poly = (Polygon) o;\n\t\t\t\tList<Vertex> vertices = poly.getVertices();\n\n\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t// for (int i = 0; i < vertices.size(); i++) {\n\t\t\t\t\t// CSG csg= new Cylinder(0,stroke/2,stroke,3).toCSG()\n\t\t\t\t\t// .move(vertices.get(i))\n\t\t\t\t\t// .setColor(new javafx.scene.paint.Color(Math.random() * 0.5 + 0.5,\n\t\t\t\t\t// Math.random() * 0.5 + 0.5, Math.random() * 0.5 + 0.5, 1));\n\t\t\t\t\t// csg.setIsWireFrame(true);\n\t\t\t\t\t// getBowlerStudio().addNode(csg.getMesh());\n\t\t\t\t\t// }\n\n\t\t\t\t\tMeshView current = BowlerStudioController.createPolygonOutlineMesh(vertices);\n\t\t\t\t\tPhongMaterial material = new PhongMaterial(poly.getColor());\n\t\t\t\t\t// Set diffuse color to black and use self-illumination\n\t\t\t\t\tmaterial.setDiffuseColor(poly.getColor());\n\t\t\t\t\tmaterial.setSelfIlluminationMap(null); // Reset any existing map\n\n\t\t\t\t\t// Use specular color for the line color (works without lighting)\n\t\t\t\t\tmaterial.setSpecularColor(poly.getColor());\n\t\t\t\t\tmaterial.setSpecularPower(1.0);\n\t\t\t\t\tcurrent.setMaterial(material);\n\t\t\t\t\tcurrent.setCullFace(CullFace.NONE);\n\t\t\t\t\taddUserNode(current);\n\n\t\t\t\t});\n\t\t\t\tsetSelectedCsg(poly.getVertices().get(0).pos);\n\t\t\t\treturn;\n\t\t\t} else if (Vertex.class.isInstance(o)) {\n\t\t\t\tVertex v = (Vertex) o;\n\t\t\t\tCSG csg = new Cylinder(0, stroke / 2, stroke, 3).toCSG().move(v).setColor(new javafx.scene.paint.Color(\n\t\t\t\t\t\tMath.random() * 0.5 + 0.5, Math.random() * 0.5 + 0.5, Math.random() * 0.5 + 0.5, 0.25));\n\t\t\t\taddUserNode(csg.getMesh());\n\t\t\t\treturn;\n\t\t\t} else if (Vector3d.class.isInstance(o)) {\n\t\t\t\tVector3d v = (Vector3d) o;\n\t\t\t\tsetSelectedCsg(v);\n\t\t\t\treturn;\n\t\t\t} else if (TransformNR.class.isInstance(o)) {\n\t\t\t\tTransformNR v = (TransformNR) o;\n\t\t\t\tsetSelectedCsg(v);\n\t\t\t\treturn;\n\t\t\t} else if (BowlerAbstractDevice.class.isInstance(o)) {\n\t\t\t\tBowlerAbstractDevice bad = (BowlerAbstractDevice) o;\n\t\t\t\tConnectionManager.addConnection((BowlerAbstractDevice) o, bad.getScriptingName());\n\t\t\t\treturn;\n\t\t\t} else if (DMDevice.wrappable(o)) {\n\t\t\t\tBowlerAbstractDevice bad;\n\t\t\t\ttry {\n\t\t\t\t\tbad = new DMDevice(o);\n\t\t\t\t\tConnectionManager.addConnection(bad, bad.getScriptingName());\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\n\n\t\t\t}\n\t\t} catch (Throwable t) {\n\t\t\tLog.error(t);\n\t\t}\n\t}\n\n\tpublic void setSelectedCsg(Vector3d v) {\n\t\tTransformNR poseToMove = new TransformNR(v.x, v.y, v.z, new RotationNR());\n\t\tsetSelectedCsg(poseToMove);\n\t}\n\n\tpublic void setSelectedCsg(TransformNR poseToMove) {\n\t\tAffine manipulator2 = new Affine();\n\t\tfocusToAffine(poseToMove, manipulator2);\n\t}\n\n\tpublic void rebuild(boolean b) {\n\t\trebuildingUIOnerror = true;\n\n\t\tcom.neuronrobotics.sdk.common.Log.info(\"Rebuilding scene \" + name);\n\t\tbuildScene();\n\n\t\tcom.neuronrobotics.sdk.common.Log.info(\"Rebuilding camera \" + name);\n\t\tbuildCamera(b);\n\n\t\tcom.neuronrobotics.sdk.common.Log.info(\"Rebuilding axis \" + name);\n\t\tbuildAxes(b);\n\n\t\t// Stop[] stops = null;\n\t\t// com.neuronrobotics.sdk.common.Log.info(\"Rebuilding gradient \" + name);\n\t\t// getSubScene().setFill(new LinearGradient(125, 0, 225, 0, false,\n\t\t// CycleMethod.NO_CYCLE, stops));\n\n\t\tgroup = new Group(getSubScene());\n\t\tScene s = new Scene(group);\n\t\t// handleKeyboard(s);\n\t\thandleMouse(getSubScene());\n\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tgetFlyingCamera().setGlobalToFiducialTransform(defaultCameraView);\n\t\t\t// setScene(s);\n\t\t\trebuildingUIOnerror = false;\n\t\t\tgetControlsBox(homeIcon, generateIcon, clearIcon);\n\t\t});\n\t}\n\n\t/**\n\t * Reattach mouse event handlers to the SubScene to restore camera controls.\n\t * Call this after UI stack changes to ensure mouse events are properly routed\n\t * to the controls map.\n\t */\n\tpublic void reattachMouseHandlers() {\n\t\tdisabeControl = false;\n\t\tif (getSubScene() != null) {\n\t\t\thandleMouse(getSubScene());\n\t\t\tgetSubScene().setMouseTransparent(false);\n\t\t} else {\n\t\t\tLog.error(new Exception(\"Failed to set up mouse\"));\n\t\t}\n\t}\n\n\tprivate void highlightDebugIndex(int index, java.awt.Color c) {\n\t\tString trace = debuggerList.get(index);\n\t\tBowlerStudioController.getBowlerStudio().setHighlight(locateFile(getFilenameFromTrace(trace), getSelectedCsg()),\n\t\t\t\tgetLineNumbereFromTrace(trace), c);\n\t}\n\n\tprivate String getFilenameFromTrace(String trace) {\n\t\tString[] parts = trace.split(\":\");\n\t\treturn parts[0];\n\t}\n\n\tprivate int getLineNumbereFromTrace(String trace) {\n\t\tString[] parts = trace.split(\":\");\n\t\treturn Integer.parseInt(parts[1]);\n\t}\n\n\tpublic void setControls(CheckMenuItem showRuler, CheckMenuItem idlespin, CheckMenuItem autohighlight) {\n\t\tthis.showRuler = showRuler;\n\t\trebuild(true);\n\t\tthis.spin = idlespin;\n\t\tthis.autoHighlight = autohighlight;\n\t\tidlespin.setOnAction((event) -> {\n\t\t\tresetMouseTime();\n\t\t\tif (spin.isSelected()) {\n\t\t\t\tautospingThread = new Thread(() -> {\n\t\t\t\t\twhile (spin.isSelected()) {\n\t\t\t\t\t\tBowlerStudio.runLater(new Runnable() {\n\t\t\t\t\t\t\t@Override\n\t\t\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\t\t\tautoSpin();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tThread.sleep(30);\n\t\t\t\t\t\t} catch (InterruptedException e) {\n\t\t\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(e);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.debug(\"Autospin Thread clean exit \" + name);\n\t\t\t\t});\n\t\t\t\tautospingThread.setName(\"UI Autospin Thread \" + name);\n\t\t\t\tautospingThread.start();\n\t\t\t}\n\t\t});\n\n\t\tshowRuler.setOnAction((event) -> {\n\t\t\tboolean selected = showRuler.isSelected();\n\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"CheckBox Action (selected: \" +\n\t\t\t// selected +\n\t\t\t// \")\");\n\t\t\tif (selected)\n\t\t\t\tshowAxis();\n\t\t\telse\n\t\t\t\thideAxis();\n\t\t});\n\t\tsetControlsMap(new IControlsMap() {\n\n\t\t\t@Override\n\t\t\tpublic boolean timeToCancel(MouseEvent event) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic boolean isZoom(ScrollEvent t) {\n\t\t\t\treturn (ScrollEvent.SCROLL == t.getEventType());\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic boolean isSlowMove(MouseEvent event) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic boolean isRotate(MouseEvent me) {\n\t\t\t\tboolean shiftDown = me.isShiftDown();\n\t\t\t\tboolean primaryButtonDown = me.isPrimaryButtonDown();\n\t\t\t\tboolean secondaryButtonDown = me.isSecondaryButtonDown();\n\t\t\t\tboolean ctrl = me.isControlDown();\n\t\t\t\tif (ctrl && primaryButtonDown && (!shiftDown))\n\t\t\t\t\treturn true;\n\t\t\t\tif ((!shiftDown) && secondaryButtonDown)\n\t\t\t\t\treturn true;\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic boolean isMove(MouseEvent me) {\n\t\t\t\tboolean shiftDown = me.isShiftDown();\n\t\t\t\tboolean primaryButtonDown = me.isPrimaryButtonDown();\n\t\t\t\tboolean secondaryButtonDown = me.isSecondaryButtonDown();\n\t\t\t\tboolean middle = me.isMiddleButtonDown();\n\t\t\t\tboolean ctrl = me.isControlDown();\n\t\t\t\tif (middle)\n\t\t\t\t\treturn true;\n\t\t\t\tif ((shiftDown) && secondaryButtonDown)\n\t\t\t\t\treturn true;\n\t\t\t\tif (ctrl && shiftDown && primaryButtonDown)\n\t\t\t\t\treturn true;\n\n\t\t\t\treturn false;\n\t\t\t}\n\t\t});\n\t}\n\n\tpublic Group getControlsBox(ImageView homeIcon, ImageView generateIcon, ImageView clearIcon) {\n\n\t\tthis.homeIcon = homeIcon;\n\t\tthis.generateIcon = generateIcon;\n\t\tthis.clearIcon = clearIcon;\n\t\tHBox controls = new HBox(10);\n\t\thome = new Button(\"Home\");\n\t\thome.setTooltip(new javafx.scene.control.Tooltip(\"Home the camera\"));\n\n\t\tif (homeIcon != null)\n\t\t\thome.setGraphic(homeIcon);\n\n\t\thome.setOnAction(event -> {\n\t\t\tfocusOrientation(new TransformNR(0, 0, 0, new RotationNR(0, 45, -45)), new TransformNR(),\n\t\t\t\t\tgetFlyingCamera().getDefaultZoomDepth());\n\t\t});\n\n\t\texport = new Button(\"Export\");\n\n\t\tif (generateIcon != null)\n\t\t\texport.setGraphic(generateIcon);\n\n\t\texport.setOnAction(event -> {\n\t\t\tif (!getCsgMap().isEmpty()) {\n\t\t\t\texportAll(false);\n\n\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\texport.setDisable(true);\n\t\t\t\t});\n\t\t\t} else\n\t\t\t\tcom.neuronrobotics.sdk.common.Log.debug(\"Nothing to export!\");\n\n\t\t});\n\n\t\tfinal Tooltip tooltip = new Tooltip();\n\t\ttooltip.setText(\"\\nExport all of the parts on the screen\\n\" + \"to manufacturing. STL and SVG\\n\");\n\t\texport.setTooltip(tooltip);\n\t\tButton clear = new Button(\"Clear\");\n\t\tif (clearIcon != null)\n\t\t\tclear.setGraphic(clearIcon);\n\n\t\tclear.setOnAction(event -> {\n\t\t\tclearUserNode();\n\t\t\tremoveObjects();\n\t\t});\n\n\t\tjavafx.scene.layout.VBox allControls = new javafx.scene.layout.VBox();\n\t\tcontrolsChecks = new HBox(10);\n\n\t\tBowlerStudio.runLater(() -> controls.getChildren().addAll(home, export, clear));\n\n\t\tBowlerStudio.runLater(() -> allControls.getChildren().addAll(controlsChecks, controls));\n\n\t\treturn new Group(allControls);\n\t}\n\n\tprivate void exportAll(boolean makePrintBed) {\n\t\tnew Thread() {\n\t\t\tpublic void run() {\n\t\t\t\tsetName(\"Exporting the CAD objects\");\n\t\t\t\tArrayList<CSG> csgs = new ArrayList<CSG>(getCsgMap().keySet());\n\t\t\t\t// if (makePrintBed) {\n\t\t\t\t// }\n\n\t\t\t\tcom.neuronrobotics.sdk.common.Log.debug(\"Exporting \" + csgs.size() + \" parts\");\n\t\t\t\tFile baseDirForFiles = FileSelectionFactory.GetDirectory(getDefaultStlDir());\n\t\t\t\ttry {\n\t\t\t\t\tArrayList<File> files = new CadFileExporter(BowlerStudioController.getMobileBaseUI())\n\t\t\t\t\t\t\t.generateManufacturingParts(csgs, baseDirForFiles);\n\t\t\t\t\tfor (File f : files) {\n\t\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.debug(\"Exported \" + f.getAbsolutePath());\n\n\t\t\t\t\t}\n\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.debug(\"Success! \" + files.size() + \" parts exported\");\n\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\tBowlerStudio.printStackTrace(e);\n\t\t\t\t}\n\n\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\texport.setDisable(false);\n\t\t\t\t});\n\t\t\t}\n\t\t}.start();\n\t}\n\n\tpublic boolean isAutoHightlight() {\n\t\tif (autoHighlight != null)\n\t\t\treturn autoHighlight.isSelected();\n\t\treturn false;\n\t}\n\n\tpublic Group getDebuggerBox() {\n\t\tHBox controls = new HBox(10);\n\n\t\tback = new Button(\"Back\");\n\t\tfwd = new Button(\"Forward\");\n\n\t\tfwd.setOnAction(event -> {\n\t\t\tBowlerStudioController.getBowlerStudio().clearHighlits();\n\t\t\thighlightDebugIndex(debuggerIndex, java.awt.Color.PINK);\n\t\t\tdebuggerIndex--;\n\t\t\tif (debuggerIndex == 0) {\n\t\t\t\tfwd.disableProperty().set(true);\n\t\t\t}\n\t\t\tback.disableProperty().set(false);\n\t\t\thighlightDebugIndex(debuggerIndex, java.awt.Color.GREEN);\n\t\t});\n\n\t\tback.setOnAction(event -> {\n\t\t\tBowlerStudioController.getBowlerStudio().clearHighlits();\n\t\t\thighlightDebugIndex(debuggerIndex, java.awt.Color.PINK);\n\t\t\tdebuggerIndex++;\n\t\t\tif (debuggerIndex >= debuggerList.size()) {\n\t\t\t\tback.disableProperty().set(true);\n\t\t\t\tdebuggerIndex--;\n\t\t\t}\n\t\t\tif (debuggerIndex > 0)\n\t\t\t\tfwd.disableProperty().set(false);\n\t\t\thighlightDebugIndex(debuggerIndex, java.awt.Color.GREEN);\n\t\t});\n\n\t\tfwd.disableProperty().set(true);\n\t\tback.disableProperty().set(true);\n\n\t\tBowlerStudio.runLater(() -> controls.getChildren().addAll(new Label(\"Cad Debugger\"), back, fwd));\n\t\treturn new Group(controls);\n\t}\n\n\t/**\n\t * Removes the objects.\n\t */\n\tpublic void removeObjects() {\n\t\t// for (CSG previousCsg:getCsgMap().keySet())\n\t\t// for (Polygon poly:previousCsg.getPolygons())\n\t\t// sumVert-=(poly.vertices.size());\n\t\t// com.neuronrobotics.sdk.common.Log.error(\"Total Verts = \"+sumVert);\n\n\t\tlookGroup.getChildren().clear();\n\t\tgetCsgMap().clear();\n\t\tcsgSourceFile.clear();\n\t\taxisMap.clear();\n\t}\n\n\t/**\n\t * Removes the object.\n\t *\n\t * @param previousCsg the previous\n\t */\n\tpublic void removeObject(CSG previousCsg) {\n\t\t// for (Polygon poly:previousCsg.getPolygons())\n\t\t// sumVert-=(poly.vertices.size());\n\t\t// com.neuronrobotics.sdk.common.Log.error(\"Total Verts = \"+sumVert);\n\n\t\t// com.neuronrobotics.sdk.common.Log.error(\" Removing a CSG from file:\n\t\t// \"+previousCsg+\" from\n\t\t// file \"+csgSourceFile.get(previousCsg));\n\t\tMeshView previous = getCsgMap().get(previousCsg);\n\t\tif (previous != null) {\n\t\t\tlookGroup.getChildren().remove(previous);\n\t\t\tlookGroup.getChildren().remove(axisMap.get(previous));\n\t\t\taxisMap.remove(previous);\n\t\t}\n\t\tgetCsgMap().remove(previousCsg);\n\t\tcsgSourceFile.remove(previousCsg);\n\t}\n\n\tprivate void fireRegenerate(String key, File source, Set<CSG> currentObjectsToCheck) {\n\t\tnew Thread() {\n\t\t\tpublic void run() {\n\t\t\t\tArrayList<CSG> toAdd = new ArrayList<>();\n\t\t\t\tArrayList<CSG> toRemove = new ArrayList<>();\n\n\t\t\t\tObject[] array = null;\n\t\t\t\t// synchronized (currentObjectsToCheck) {\n\t\t\t\tarray = (Object[]) currentObjectsToCheck.toArray();\n\t\t\t\t// }\n\t\t\t\tfor (int i = 0; i < currentObjectsToCheck.size(); i++) {\n\t\t\t\t\tcom.neuronrobotics.sdk.common.Log\n\t\t\t\t\t\t\t.debug(\"Testing for Regenerating \" + i + \" of \" + currentObjectsToCheck.size());\n\t\t\t\t\ttry {\n\t\t\t\t\t\tCSG tester = (CSG) array[i];\n\t\t\t\t\t\tfor (String p : tester.getParameters(CSGDatabase.getInstance())) {\n\t\t\t\t\t\t\tif (p.contentEquals(key) && !toRemove.contains(tester)) {\n\t\t\t\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.debug(\"Regenerating \" + i + \" on key \" + p);\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tCSG ret = tester.regenerate();\n\t\t\t\t\t\t\t\t\ttoRemove.add(tester);\n\t\t\t\t\t\t\t\t\ttoAdd.add(ret);\n\t\t\t\t\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\t\t\t\t\tex.printStackTrace(System.out);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\t\tex.printStackTrace(System.out);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\tfor (CSG add : toRemove)\n\t\t\t\t\t\tremoveObject(add);\n\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\tfor (CSG ret : toAdd)\n\t\t\t\t\t\t\taddObject(ret, source);\n\t\t\t\t\t});\n\t\t\t\t});\n\n\t\t\t\tcom.neuronrobotics.sdk.common.Log.debug(\"Saving CSG database\");\n\t\t\t\ttry {\n\t\t\t\t\tCSGDatabase.getInstance().saveDatabase();\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t// TODO Auto-generated catch block\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\n\t\t\t}\n\t\t}.start();\n\t}\n\n\t/**\n\t * Adds the object.\n\t *\n\t * @param currentCsg the current\n\t * @return the mesh view\n\t */\n\t@Deprecated\n\tpublic MeshView addObject(CSG currentCsg, File source) {\n\t\treturn addObject(currentCsg, source, currentCsg.getColor().getOpacity(), CSGDatabase.getInstance());\n\t}\n\n\tpublic MeshView addObject(CSG currentCsg, File source, double opacity, CSGDatabaseInstance instance) {\n\t\tif (currentCsg == null)\n\t\t\treturn new MeshView();\n\t\t// for (Polygon poly:currentCsg.getPolygons())\n\t\t// sumVert+=(poly.vertices.size());\n\t\t// com.neuronrobotics.sdk.common.Log.error(\"Total Verts = \"+sumVert);\n\t\tBowlerStudioModularFrame bowlerStudioModularFrame = BowlerStudioModularFrame.getBowlerStudioModularFrame();\n\t\tif (bowlerStudioModularFrame != null)\n\t\t\tbowlerStudioModularFrame.showCreatureLab();\n\t\t// com.neuronrobotics.sdk.common.Log.error(\" Adding a CSG from file:\n\t\t// \"+source.getName());\n\t\tif (getCsgMap().get(currentCsg) != null)\n\t\t\treturn getCsgMap().get(currentCsg);\n\n\t\tMeshView mesh = currentCsg.getMesh();\n\t\tgetCsgMap().put(currentCsg, mesh);\n\t\tBowlerStudio.runLater(() -> controlsChecks.getChildren().clear());\n\t\tSlider slider = AssemblySlider.getSlider(getCsgMap().keySet());\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tcontrolsChecks.getChildren().addAll(slider);\n\t\t});\n\t\tcsgSourceFile.put(currentCsg, source);\n\t\tOptional<Object> m = currentCsg.getStorage().getValue(\"manipulator\");\n\t\tHashMap<javafx.event.EventType<MouseEvent>, EventHandler<MouseEvent>> eventForManipulation = null;\n\t\ttry {\n\t\t\tif (HashMap.class.isInstance(m.get())) {\n\t\t\t\teventForManipulation = (HashMap<javafx.event.EventType<MouseEvent>, EventHandler<MouseEvent>>) m.get();\n\t\t\t}\n\t\t} catch (java.util.NoSuchElementException ex) {\n\t\t\teventForManipulation = null;\n\t\t}\n\n\t\tMeshView current = getCsgMap().get(currentCsg);\n\t\tif (opacity > 0) {\n\t\t\tPhongMaterial phongMaterial = (PhongMaterial) current.getMaterial();\n\t\t\tColor diffuseColor = phongMaterial.getDiffuseColor();\n\t\t\tdiffuseColor = Color.color(diffuseColor.getRed(), diffuseColor.getGreen(), diffuseColor.getBlue(), opacity);\n\t\t\tphongMaterial.setDiffuseColor(diffuseColor);\n\t\t}\n\t\tcurrent.setViewOrder(0);\n\t\tcurrent.setCullFace(CullFace.BACK);// backs are tranparent\n\t\t// current.setCullFace(CullFace.NONE);// backs are black\n\t\t// ((PhongMaterial)\n\t\t// current.getMaterial()).setSpecularColor(javafx.scene.paint.Color.WHITE);\n\t\t// ((PhongMaterial)\n\t\t// current.getMaterial()).setDiffuseColor(javafx.scene.paint.Color.GRAY);\n\n\t\tContextMenu cm = new ContextMenu();\n\t\tMenu infomenu = new Menu(\"Info...\");\n\t\tinfomenu.getItems().add(new MenuItem(\"Name = \" + currentCsg.getName()));\n\t\tinfomenu.getItems().add(new MenuItem(\"Mass = \" + (currentCsg.getMassKG(0.001) * 1000) + \" grams \"));\n\t\tinfomenu.getItems().add(new MenuItem(\"Total X = \" + currentCsg.getTotalX()));\n\t\tinfomenu.getItems().add(new MenuItem(\"Total Y = \" + currentCsg.getTotalY()));\n\t\tinfomenu.getItems().add(new MenuItem(\"Total Z = \" + currentCsg.getTotalZ()));\n\n\t\tinfomenu.getItems().add(new MenuItem(\"Maximums: \"));\n\t\tinfomenu.getItems().add(new MenuItem(\"Max X = \" + currentCsg.getMaxX()));\n\t\tinfomenu.getItems().add(new MenuItem(\"Max Y = \" + currentCsg.getMaxY()));\n\t\tinfomenu.getItems().add(new MenuItem(\"Max Z = \" + currentCsg.getMaxZ()));\n\n\t\tinfomenu.getItems().add(new MenuItem(\"Minimums: \"));\n\t\tinfomenu.getItems().add(new MenuItem(\"Min X = \" + currentCsg.getMinX()));\n\t\tinfomenu.getItems().add(new MenuItem(\"Min Y = \" + currentCsg.getMinY()));\n\t\tinfomenu.getItems().add(new MenuItem(\"Min Z = \" + currentCsg.getMinZ()));\n\n\t\tcm.getItems().add(infomenu);\n\n\t\tSet<String> params = currentCsg.getParameters(instance);\n\n\t\tif (params != null) {\n\t\t\tMenu parameters = new Menu(\"Parameters...\");\n\t\t\tparameters.setMnemonicParsing(false);\n\t\t\tfor (String key : params) {\n\t\t\t\tParameter param = instance.get(key);\n\t\t\t\tcurrentCsg.setParameterIfNull(instance, key);\n\t\t\t\tif (LengthParameter.class.isInstance(param)) {\n\n\t\t\t\t\tLengthParameter lp = (LengthParameter) param;\n\n\t\t\t\t\tString string = null;\n\t\t\t\t\tString string2 = null;\n\t\t\t\t\tif (lp.getOptions().size() > 1)\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tstring = lp.getOptions().get(1).toString();\n\t\t\t\t\t\t\tstring2 = lp.getOptions().get(0).toString();\n\t\t\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\t\t\t// some parameters from cadoodle do not work here...\n\t\t\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(ex);;\n\t\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tstring = lp.getMM() + \"\";\n\t\t\t\t\t\tstring2 = lp.getMM() + \"\";\n\t\t\t\t\t}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tEngineeringUnitsSliderWidget widget = new EngineeringUnitsSliderWidget(\n\t\t\t\t\t\t\t\tnew IOnEngineeringUnitsChange() {\n\n\t\t\t\t\t\t\t\t\t@Override\n\t\t\t\t\t\t\t\t\tpublic void onSliderMoving(EngineeringUnitsSliderWidget s, double newAngleDegrees) {\n\t\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\t\tcurrentCsg.setParameterNewValue(instance, key, newAngleDegrees);\n\n\t\t\t\t\t\t\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\t\t\t\t\t\t\tBowlerStudioController.highlightException(source, ex);\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t@Override\n\t\t\t\t\t\t\t\t\tpublic void onSliderDoneMoving(EngineeringUnitsSliderWidget s,\n\t\t\t\t\t\t\t\t\t\t\tdouble newAngleDegrees) {\n\t\t\t\t\t\t\t\t\t\t// Get the set of objects to check for\n\t\t\t\t\t\t\t\t\t\t// regeneration after the initial\n\t\t\t\t\t\t\t\t\t\t// regeneration cycle.\n\t\t\t\t\t\t\t\t\t\tSet<CSG> objects = getCsgMap().keySet();\n\t\t\t\t\t\t\t\t\t\tcm.hide();// hide this menu because the new\n\t\t\t\t\t\t\t\t\t\t\t\t\t// CSG talks to the new menu\n\n\t\t\t\t\t\t\t\t\t\tfireRegenerate(key, source, objects);\n\t\t\t\t\t\t\t\t\t\tresetMouseTime();\n\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}, Double.parseDouble(string), Double.parseDouble(string2), lp.getMM(), 400, key);\n\t\t\t\t\t\tCustomMenuItem customMenuItem = new CustomMenuItem(widget);\n\t\t\t\t\t\tcustomMenuItem.setHideOnClick(false);\n\t\t\t\t\t\tparameters.getItems().add(customMenuItem);\n\t\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(ex);;\n\t\t\t\t\t}\n\t\t\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"Adding Length Paramater \" +\n\t\t\t\t\t// lp.getName());\n\t\t\t\t} else {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tParameter lp = (Parameter) param;\n\t\t\t\t\t\tif (lp != null) {\n\t\t\t\t\t\t\tMenu paramTypes = new Menu(lp.getName() + \" \" + lp.getStrValue());\n\t\t\t\t\t\t\tparamTypes.setMnemonicParsing(false);\n\t\t\t\t\t\t\tfor (String opt : lp.getOptions()) {\n\t\t\t\t\t\t\t\tString myVal = opt;\n\t\t\t\t\t\t\t\tMenuItem customMenuItem = new MenuItem(myVal);\n\t\t\t\t\t\t\t\tcustomMenuItem.setMnemonicParsing(false);\n\t\t\t\t\t\t\t\tcustomMenuItem.setOnAction(event -> {\n\t\t\t\t\t\t\t\t\tresetMouseTime();\n\t\t\t\t\t\t\t\t\tcom.neuronrobotics.sdk.common.Log\n\t\t\t\t\t\t\t\t\t\t\t.debug(\"Updating \" + lp.getName() + \" to \" + myVal);\n\t\t\t\t\t\t\t\t\tlp.setStrValue(myVal);\n\t\t\t\t\t\t\t\t\tinstance.get(lp.getName()).setStrValue(myVal);\n\t\t\t\t\t\t\t\t\tfor (IParameterChanged l : instance.getParamListeners(lp.getName())) {\n\t\t\t\t\t\t\t\t\t\tl.parameterChanged(lp.getName(), lp);\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t// Get the set of objects to check for\n\t\t\t\t\t\t\t\t\t// regeneration after the initioal\n\t\t\t\t\t\t\t\t\t// regeneration cycle.\n\t\t\t\t\t\t\t\t\tSet<CSG> objects = getCsgMap().keySet();\n\t\t\t\t\t\t\t\t\tcm.hide();// hide this menu because the new\n\t\t\t\t\t\t\t\t\t\t\t\t// CSG talks to the new menu\n\t\t\t\t\t\t\t\t\tfireRegenerate(key, source, objects);\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\tparamTypes.getItems().add(customMenuItem);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tparameters.getItems().add(paramTypes);\n\t\t\t\t\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"Adding String Paramater \" +\n\t\t\t\t\t\t\t// lp.getName());\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(ex);;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcm.getItems().add(parameters);\n\t\t}\n\n\t\tMenuItem exportObj = new MenuItem(\"Export OBJ...\");\n\t\texportObj.setOnAction(new EventHandler<ActionEvent>() {\n\n\t\t\t@Override\n\t\t\tpublic void handle(ActionEvent event) {\n\t\t\t\tcurrentCsg.addExportFormat(\"obj\");\n\t\t\t\texportManufacturingPart(currentCsg, source);\n\t\t\t}\n\n\t\t});\n\n\t\tMenuItem exportDXF = new MenuItem(\"Export SVG...\");\n\t\texportDXF.setOnAction(new EventHandler<ActionEvent>() {\n\n\t\t\t@Override\n\t\t\tpublic void handle(ActionEvent event) {\n\t\t\t\tcurrentCsg.addExportFormat(\"svg\");\n\t\t\t\texportManufacturingPart(currentCsg, source);\n\t\t\t}\n\n\t\t});\n\t\tcm.getItems().add(exportDXF);\n\t\tMenuItem blend = new MenuItem(\"Export Blender File...\");\n\t\tblend.setOnAction(new EventHandler<ActionEvent>() {\n\t\t\t@Override\n\t\t\tpublic void handle(ActionEvent event) {\n\t\t\t\tcurrentCsg.addExportFormat(\"blend\");\n\t\t\t\texportManufacturingPart(currentCsg, source);\n\t\t\t}\n\t\t});\n\t\tcm.getItems().add(blend);\n\n\t\tMenuItem export = new MenuItem(\"Export STL...\");\n\t\texport.setOnAction(new EventHandler<ActionEvent>() {\n\t\t\t@Override\n\t\t\tpublic void handle(ActionEvent event) {\n\t\t\t\tcurrentCsg.addExportFormat(\"stl\");\n\t\t\t\texportManufacturingPart(currentCsg, source);\n\t\t\t}\n\t\t});\n\t\tcm.getItems().add(export);\n\t\tcm.getItems().add(exportObj);\n\n\t\tMenuItem toWireframe = new MenuItem(\"To Wire Frame\");\n\t\ttoWireframe.setOnAction(new EventHandler<ActionEvent>() {\n\t\t\t@Override\n\t\t\tpublic void handle(ActionEvent event) {\n\t\t\t\tresetMouseTime();\n\t\t\t\tif (current.getDrawMode() == DrawMode.FILL) {\n\t\t\t\t\ttoWireframe.setText(\"To Solid Fill\");\n\t\t\t\t\tcurrent.setDrawMode(DrawMode.LINE);\n\t\t\t\t} else {\n\t\t\t\t\tcurrent.setDrawMode(DrawMode.FILL);\n\t\t\t\t\ttoWireframe.setText(\"To Wire Frame\");\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tcm.getItems().add(toWireframe);\n\n\t\tMenuItem hide = new MenuItem(\"Hide Object\");\n\t\thide.setOnAction(new EventHandler<ActionEvent>() {\n\t\t\t@Override\n\t\t\tpublic void handle(ActionEvent event) {\n\t\t\t\tresetMouseTime();\n\t\t\t\tremoveObject(currentCsg);\n\t\t\t}\n\t\t});\n\n\t\tcm.getItems().add(hide);\n\n\t\tMenuItem cut = new MenuItem(\"Read Source\");\n\t\tcut.setOnAction(new EventHandler<ActionEvent>() {\n\t\t\t@Override\n\t\t\tpublic void handle(ActionEvent event) {\n\t\t\t\tresetMouseTime();\n\t\t\t\tsetSelectedCsg(currentCsg);\n\t\t\t\tnew Thread() {\n\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\tselectObjectsSourceFile(selectedCsg);\n\t\t\t\t\t}\n\t\t\t\t}.start();\n\t\t\t}\n\t\t});\n\t\tcm.getItems().add(cut);\n\t\tcm.addEventFilter(MouseEvent.MOUSE_RELEASED, new EventHandler<MouseEvent>() {\n\t\t\t@Override\n\t\t\tpublic void handle(MouseEvent event) {\n\t\t\t\tif (event.getButton() == MouseButton.SECONDARY) {\n\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"consuming right release button in cm filter\");\n\t\t\t\t\tevent.consume();\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tclass closeTheMenuHandler implements EventHandler<MouseEvent> {\n\t\t\tlong lastClickedTime = 0;\n\n\t\t\t@Override\n\t\t\tpublic void handle(MouseEvent event) {\n\t\t\t\tif (event.isSecondaryButtonDown())\n\t\t\t\t\tcm.show(current, event.getScreenX() - 10, event.getScreenY() - 10);\n\t\t\t\telse if (event.isPrimaryButtonDown()) {\n\t\t\t\t\tif (System.currentTimeMillis() - lastClickedTime < 500) {\n\t\t\t\t\t\tBowlerStudio.runLater(java.time.Duration.ofMillis(200), new Runnable() {\n\t\t\t\t\t\t\t@Override\n\t\t\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\t\t\tsetSelectedCsg(currentCsg);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\n\t\t\t\t\t}\n\t\t\t\t\tlastClickedTime = System.currentTimeMillis();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcloseTheMenuHandler cmh = new closeTheMenuHandler();\n\t\tif (!currentCsg.isWireFrame()) {\n\t\t\tBowlerStudio.runLater(() -> current.addEventHandler(MouseEvent.MOUSE_PRESSED, cmh));\n\t\t\tif (eventForManipulation != null) {\n\t\t\t\tHashMap<javafx.event.EventType<MouseEvent>, EventHandler<MouseEvent>> manip = eventForManipulation;\n\t\t\t\tfor (javafx.event.EventType<MouseEvent> e : manip.keySet())\n\t\t\t\t\tBowlerStudio.runLater(() -> current.addEventHandler(e, manip.get(e)));\n\t\t\t}\n\t\t} else {\n\t\t\tcurrent.setDrawMode(DrawMode.LINE);\n\t\t\tcurrent.setPickOnBounds(false);\n\t\t\tcurrent.setMouseTransparent(true);\n\t\t}\n\t\t// cm.getScene().addEventHandler(MouseEvent.MOUSE_EXITED, cmh);\n\t\tif (current == null)\n\t\t\treturn new MeshView();\n\n\t\tif (!lookGroup.getChildren().contains(current)) {\n\n\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\ttry {\n\t\t\t\t\tlookGroup.getChildren().add(current);\n\t\t\t\t} catch (Throwable e) {\n\t\t\t\t\t// duplicate\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tif (showRuler != null) {\n\t\t\t\tAxis axes = new Axis(showRuler.isSelected());\n\t\t\t\tif (currentCsg.hasManipulator())\n\n\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\taxes.getTransforms().add(currentCsg.getManipulator());\n\t\t\t\t\t\t} catch (MissingManipulatorException e) {\n\t\t\t\t\t\t\t// TODO Auto-generated catch block\n\t\t\t\t\t\t\te.printStackTrace();\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\n\t\t\t\taxisMap.put(current, axes);\n\t\t\t\tBowlerStudio.runLater(() -> lookGroup.getChildren().add(axes));\n\t\t\t}\n\t\t}\n\n\t\t// Log.warning(\"Adding new axes\");\n\t\treturn current;\n\t}\n\n\tpublic void exportManufacturingPart(CSG currentCsg, File source) {\n\t\tresetMouseTime();\n\n\t\tnew Thread() {\n\n\t\t\tpublic void run() {\n\t\t\t\ttry {\n\n\t\t\t\t\tsetDefaultStlDir(new CadFileExporter(BowlerStudioController.getMobileBaseUI())\n\t\t\t\t\t\t\t.generateManufacturingParts(Arrays.asList(currentCsg),\n\t\t\t\t\t\t\t\t\tFileSelectionFactory.GetDirectory(getDefaultStlDir()))\n\t\t\t\t\t\t\t.get(0));\n\t\t\t\t} catch (Exception e1) {\n\t\t\t\t\tBowlerStudioController.highlightException(source, e1);\n\t\t\t\t}\n\n\t\t\t}\n\t\t}.start();\n\t}\n\n\tprivate void prepAllItems(ObservableList<MenuItem> items, EventHandler<MouseEvent> exited,\n\t\t\tEventHandler<MouseEvent> entered) {\n\t\tfor (MenuItem item : items) {\n\t\t\tif (Menu.class.isInstance(item)) {\n\t\t\t\tMenu m = (Menu) item;\n\t\t\t\tprepAllItems(m.getItems(), exited, entered);\n\t\t\t} else {\n\t\t\t\titem.addEventHandler(MouseEvent.MOUSE_EXITED, entered);\n\t\t\t\titem.addEventHandler(MouseEvent.MOUSE_ENTERED, entered);\n\t\t\t}\n\n\t\t}\n\t}\n\n\t/**\n\t * Save to png.\n\t *\n\t * @param f the f\n\t */\n\tpublic void saveToPng(File f) {\n\t\tString fName = f.getAbsolutePath();\n\n\t\tif (!fName.toLowerCase().endsWith(\".png\")) {\n\t\t\tfName += \".png\";\n\t\t}\n\n\t\tint snWidth = 1024;\n\t\tint snHeight = 1024;\n\n\t\tdouble realWidth = getRoot().getBoundsInLocal().getWidth();\n\t\tdouble realHeight = getRoot().getBoundsInLocal().getHeight();\n\n\t\tdouble scaleX = snWidth / realWidth;\n\t\tdouble scaleY = snHeight / realHeight;\n\n\t\tdouble scale = Math.min(scaleX, scaleY);\n\n\t\tPerspectiveCamera snCam = new PerspectiveCamera(false);\n\t\tsnCam.setTranslateZ(-200);\n\n\t\tSnapshotParameters snapshotParameters = new SnapshotParameters();\n\t\tsnapshotParameters.setTransform(new Scale(scale, scale));\n\t\tsnapshotParameters.setCamera(snCam);\n\t\tsnapshotParameters.setDepthBuffer(true);\n\t\tsnapshotParameters.setFill(Color.TRANSPARENT);\n\n\t\tWritableImage snapshot = new WritableImage(snWidth, (int) (realHeight * scale));\n\n\t\tgetRoot().snapshot(snapshotParameters, snapshot);\n\n\t\ttry {\n\t\t\tImageIO.write(javafx.embed.swing.SwingFXUtils.fromFXImage(snapshot, null), \"png\", new File(fName));\n\t\t} catch (IOException ex) {\n\t\t\tcom.neuronrobotics.sdk.common.Log.error(ex);\n\t\t}\n\t}\n\n\tprivate static int webColorToArgb(Color color) {\n\t\treturn (int) (color.getOpacity() * 255) << 24 | (int) (color.getRed() * 255) << 16\n\t\t\t\t| (int) (color.getGreen() * 255) << 8 | (int) (color.getBlue() * 255);\n\t}\n\n\tprivate static Color argbToColor(int argb) {\n\t\treturn Color.color(((argb >> 16) & 0xFF) / 255.0, ((argb >> 8) & 0xFF) / 255.0, (argb & 0xFF) / 255.0,\n\t\t\t\t((argb >> 24) & 0xFF) / 255.0);\n\t}\n\n\t// Create textured work-plane based on tiles of custom size\n\tpublic Group createTexturedWorkplane(double xSizeMM, double ySizeMM) {\n\n\t\t// Build square textured tile in MM\n\t\tfinal float TILE_SIZE_MM = 10.0f;\n\t\tfinal int TILE_BIG_GRID_PX = 200;\n\t\tfinal int TILE_SMALL_GRID_PX = 20;\n\n\t\t// Build square textured tile in inches\n\t\t// final float TILE_SIZE_MM = 25.4f;\n\t\t// final int TILE_BIG_GRID_PX = 200;\n\t\t// final int TILE_SMALL_GRID_PX = 20; // 1/10th inch\n\n\t\t// Build square textured tile in inches\n\t\t// final float TILE_SIZE_MM = 25.4f;\n\t\t// final int TILE_BIG_GRID_PX = 256;\n\t\t// final int TILE_SMALL_GRID_PX = 16; // 1/16th inch\n\n\t\t// Build square textured tile in half inche\n\t\t// final float TILE_SIZE_MM = 12.7f;\n\t\t// final int TILE_BIG_GRID_PX = 254;\n\t\t// final int TILE_SMALL_GRID_PX = 127;\n\n\t\t// Upscale work plane texture\n\t\tfinal int wpUpscale = 4;\n\n\t\t// Work plane noise in percentage [0-100%]\n\t\tint wpNoise = 25;\n\n\t\t// Work plane texture colors\n\t\tint wpColor = webColorToArgb(Color.web(\"#3838A8\")); // Higher is lighter color\n\t\tint grid1Color = webColorToArgb(Color.web(\"#202060\"));\n\t\tint grid10Color = webColorToArgb(Color.web(\"#0000FF\"));\n\n\t\tfloat workplaneX = (float) xSizeMM;\n\t\tfloat workplaneY = (float) ySizeMM;\n\n\t\tfinal float TILE_HALF_PIXEL_SIZE = TILE_SIZE_MM / (TILE_BIG_GRID_PX * 2);\n\n\t\t// Calculate texture offsets. Note X and Y are swapped in the 3D view\n\t\tfloat xTextureOffset = (float) ((int) (ySizeMM / (TILE_SIZE_MM * 2)) - ySizeMM / (TILE_SIZE_MM * 2));\n\t\tfloat yTextureOffset = (float) ((int) (xSizeMM / (TILE_SIZE_MM * 2)) - xSizeMM / (TILE_SIZE_MM * 2));\n\n\t\tint[] src = new int[TILE_BIG_GRID_PX * TILE_BIG_GRID_PX];\n\n\t\t// Set work plane background (done when adding noise)\n\t\t// Arrays.fill(src, wpColor);\n\n\t\t// Add some noise to make the work plane look real\n\t\tRandom rnd = new Random();\n\t\tint r = (wpColor >> 16) & 0xFF;\n\t\tint g = (wpColor >> 8) & 0xFF;\n\t\tint b = wpColor & 0xFF;\n\t\tfor (int i = 0; i < src.length; i++) {\n\t\t\tint n = 100 + rnd.nextInt(wpNoise + 1) - (wpNoise / 2);\n\t\t\tsrc[i] = 0xFF000000 | (Math.min(255, (r * n) / 100) << 16) | (Math.min(255, (g * n) / 100) << 8)\n\t\t\t\t\t| (Math.min(255, (b * n) / 100));\n\t\t}\n\n\t\t// Draw small grid, 1 line\n\t\tfor (int x1 = 0; x1 < TILE_BIG_GRID_PX; x1 += TILE_SMALL_GRID_PX) {\n\t\t\tfor (int y = 0; y < TILE_BIG_GRID_PX; y++) {\n\t\t\t\tsrc[y * TILE_BIG_GRID_PX + x1] = grid1Color;\n\t\t\t\tsrc[x1 * TILE_BIG_GRID_PX + y] = grid1Color;\n\t\t\t}\n\t\t}\n\n\t\t// Draw big grid, 3 lines\n\t\tint last = TILE_BIG_GRID_PX - 1;\n\t\tfor (int i = 0; i < TILE_BIG_GRID_PX; i++) {\n\t\t\tsrc[i + TILE_BIG_GRID_PX] = grid10Color;\n\t\t\tsrc[i * TILE_BIG_GRID_PX + 1] = grid10Color;\n\n\t\t\tsrc[i] = grid10Color;\n\t\t\tsrc[i * TILE_BIG_GRID_PX] = grid10Color;\n\n\t\t\tsrc[i * TILE_BIG_GRID_PX + last] = grid10Color;\n\t\t\tsrc[last * TILE_BIG_GRID_PX + i] = grid10Color;\n\t\t}\n\n\t\t// Scale up with nearest neighbor algorithm\n\t\tint upscaledX = TILE_BIG_GRID_PX * wpUpscale;\n\t\tint upscaledY = TILE_BIG_GRID_PX * wpUpscale;\n\t\tWritableImage tile = new WritableImage(upscaledX, upscaledY);\n\t\tPixelWriter pw = tile.getPixelWriter();\n\n\t\tfor (int y = 0; y < upscaledY; y++) {\n\t\t\tint sy = y / wpUpscale;\n\t\t\tfor (int x = 0; x < upscaledX; x++) {\n\t\t\t\tint sx = x / wpUpscale;\n\t\t\t\tpw.setArgb(x, y, src[sy * TILE_BIG_GRID_PX + sx]);\n\t\t\t}\n\t\t}\n\n\t\t// Create the work plane material\n\t\tPhongMaterial material = new PhongMaterial();\n\t\t// Sharp edges, edges with aliasing\n\t\t// material.setDiffuseMap(tile);\n\t\t// material.setDiffuseColor(new Color(1, 1, 0, 0.33));\n\t\t// material.setSpecularColor(Color.BLACK);\n\t\t// material.setSelfIlluminationMap(tile);\n\n\t\t// Set work plane texture\n\t\tmaterial.setDiffuseMap(tile);\n\n\t\t// Control work plane transparency\n\t\tColor transWhite = new Color(1, 1, 1, 0.35);\n\t\tmaterial.setDiffuseColor(transWhite); // Work plane color\n\t\tmaterial.setSpecularColor(Color.BLACK); // No shiny spots\n\n\t\t// WritableImage selfIlluminationImage = new WritableImage(1, 1);\n\t\t// selfIlluminationImage.getPixelWriter().setColor(0, 0, Color.color(0.1, 0.1,\n\t\t// 0.1, 1.0)); // RGBA\n\t\t// material.setSelfIlluminationMap(selfIlluminationImage);\n\n\t\t// Create the work plane outline material\n\t\tPhongMaterial material2 = new PhongMaterial();\n\t\tWritableImage outlineImage = new WritableImage(1, 1);\n\t\toutlineImage.getPixelWriter().setColor(0, 0, argbToColor(grid10Color));\n\t\tmaterial2.setDiffuseMap(outlineImage);\n\t\tmaterial2.setDiffuseColor(transWhite); // Work plane color\n\t\tmaterial2.setSpecularColor(Color.BLACK); // No shiny spots\n\t\t// material2.setSelfIlluminationMap(selfIlluminationImage);\n\n\t\t// Create the work plane mesh, draw at slight offset to align pixel to line\n\t\t// centre\n\t\tTriangleMesh topMesh = new TriangleMesh();\n\t\ttopMesh.getPoints().setAll(-workplaneX / 2 - TILE_HALF_PIXEL_SIZE, -workplaneY / 2 - TILE_HALF_PIXEL_SIZE, 0f,\n\t\t\t\tworkplaneX / 2 - TILE_HALF_PIXEL_SIZE, -workplaneY / 2 - TILE_HALF_PIXEL_SIZE, 0f,\n\t\t\t\tworkplaneX / 2 - TILE_HALF_PIXEL_SIZE, workplaneY / 2 - TILE_HALF_PIXEL_SIZE, 0f,\n\t\t\t\t-workplaneX / 2 - TILE_HALF_PIXEL_SIZE, workplaneY / 2 - TILE_HALF_PIXEL_SIZE, 0f);\n\n\t\t// Map texture to mesh\n\t\ttopMesh.getTexCoords().setAll(xTextureOffset, yTextureOffset, // bottom-left\n\t\t\t\txTextureOffset, yTextureOffset + workplaneX / TILE_SIZE_MM, // top-left\n\t\t\t\txTextureOffset + workplaneY / TILE_SIZE_MM, yTextureOffset + workplaneX / TILE_SIZE_MM, // top-right\n\t\t\t\txTextureOffset + workplaneY / TILE_SIZE_MM, yTextureOffset); // bottom-right\n\n\t\ttopMesh.getFaces().setAll(0, 0, 1, 1, 2, 2, 0, 0, 2, 2, 3, 3);\n\n\t\tMeshView topView = new MeshView(topMesh);\n\t\ttopView.setMaterial(material);\n\t\ttopView.setBlendMode(BlendMode.SRC_OVER);\n\t\ttopView.setCullFace(CullFace.NONE);\n\t\t// topView.setCache(false); // keeps JavaFX from scaling the image\n\n\t\t// Create the work plane outline mesh\n\t\tfinal float OUT = 2.0f; // outwards mm\n\t\tfinal float IN = 0.0f; // inwards mm\n\n\t\tTriangleMesh outlineMesh = new TriangleMesh();\n\t\toutlineMesh.getPoints().setAll(\n\t\t\t\t// inside\n\t\t\t\tIN - workplaneX / 2 - TILE_HALF_PIXEL_SIZE, IN - workplaneY / 2 - TILE_HALF_PIXEL_SIZE, 0f,\n\t\t\t\t-IN + workplaneX / 2 - TILE_HALF_PIXEL_SIZE, IN - workplaneY / 2 - TILE_HALF_PIXEL_SIZE, 0f,\n\t\t\t\t-IN + workplaneX / 2 - TILE_HALF_PIXEL_SIZE, -IN + workplaneY / 2 - TILE_HALF_PIXEL_SIZE, 0f,\n\t\t\t\tIN - workplaneX / 2 - TILE_HALF_PIXEL_SIZE, -IN + workplaneY / 2 - TILE_HALF_PIXEL_SIZE, 0f,\n\t\t\t\t// outside\n\t\t\t\t-OUT - workplaneX / 2 - TILE_HALF_PIXEL_SIZE, -OUT - workplaneY / 2 - TILE_HALF_PIXEL_SIZE, 0f,\n\t\t\t\tOUT + workplaneX / 2 - TILE_HALF_PIXEL_SIZE, -OUT - workplaneY / 2 - TILE_HALF_PIXEL_SIZE, 0f,\n\t\t\t\tOUT + workplaneX / 2 - TILE_HALF_PIXEL_SIZE, OUT + workplaneY / 2 - TILE_HALF_PIXEL_SIZE, 0f,\n\t\t\t\t-OUT - workplaneX / 2 - TILE_HALF_PIXEL_SIZE, OUT + workplaneY / 2 - TILE_HALF_PIXEL_SIZE, 0f);\n\n\t\toutlineMesh.getTexCoords().setAll(0, 0, 1, 0, 1, 1, 0, 1, // inside\n\t\t\t\t0, 0, 1, 0, 1, 1, 0, 1); // outide\n\n\t\t// 8 triangles (4 quads)\n\t\toutlineMesh.getFaces().setAll(0, 0, 4, 4, 5, 5, 0, 0, 5, 5, 1, 1, // bottom\n\t\t\t\t1, 1, 5, 5, 6, 6, 1, 1, 6, 6, 2, 2, // right\n\t\t\t\t2, 2, 6, 6, 7, 7, 2, 2, 7, 7, 3, 3, // top\n\t\t\t\t3, 3, 7, 7, 4, 4, 3, 3, 4, 4, 0, 0); // left\n\n\t\tMeshView outlineView = new MeshView(outlineMesh);\n\t\toutlineView.setMaterial(material2);\n\t\toutlineView.setBlendMode(BlendMode.SRC_OVER);\n\t\toutlineView.setCullFace(CullFace.NONE);\n\n\t\tGroup wp = new Group(topView, outlineView);\n\n\t\twp.setMouseTransparent(true);\n\n\t\treturn wp;\n\t}\n\n\t/**\n\t * Builds the scene.\n\t */\n\tprivate void buildScene() {\n\t\tworld.ry.setAngle(-90); // point z upwards\n\t\tworld.ry.setAngle(180); // arm out towards user\n\t\tBowlerStudio.runLater(() -> getRoot().getChildren().add(world));\n\t}\n\n\tpublic void hideHand() {\n\t\thandGroup.getChildren().remove(handMesh);\n\t}\n\n\tpublic void showHand() {\n\t\thandGroup.getChildren().add(handMesh);\n\t}\n\n\t/**\n\t * Builds the camera.\n\t */\n\tprivate void buildCamera(boolean addHand) {\n\n\t\t// Setup scene illumination\n\t\tcameraGroup.getChildren().setAll(camera);\n\n\t\t/*\n\t\t * // Fixed directional light from the top DirectionalLight sunLight1 = new\n\t\t * DirectionalLight(); sunLight1.setColor(Color.color(0.3, 0.3, 0.3));\n\t\t * sunLight1.setDirection(new Point3D(0, 0, -1)); sunLight1.setLightOn(false);\n\t\t * cameraGroup.getChildren().add(sunLight1);\n\t\t *\n\t\t * // Point light sun high above the work plane PointLight sunLight2 = new\n\t\t * PointLight(Color.color(0.2, 0.2, 0.2)); sunLight2.setConstantAttenuation(1);\n\t\t * sunLight2.setLinearAttenuation(0); sunLight2.setQuadraticAttenuation(0);\n\t\t * sunLight2.getTransforms().add(new Translate(0, 0, 10000));\n\t\t * sunLight2.setLightOn(false); cameraGroup.getChildren().add(sunLight2);\n\t\t *\n\t\t * // Ambient lighting AmbientLight ambientLight = new\n\t\t * AmbientLight(Color.color(0.1, 0.1, 0.1)); ambientLight.setLightOn(false);\n\t\t * cameraGroup.getChildren().add(ambientLight);\n\t\t *\n\t\t * // Directional light follows the camera view angle DirectionalLight\n\t\t * directionalCameraLight = new DirectionalLight(Color.color(1.0, 1.0, 1.0));\n\t\t * camera.localToSceneTransformProperty().addListener((obs, oldT, newT) -> {\n\t\t * Point3D d = camera.localToScene(0, 0, -1).subtract(camera.localToScene(0, 0,\n\t\t * 0)).normalize(); directionalCameraLight.setDirection(new Point3D(d.getX(),\n\t\t * -d.getY(), d.getZ())); // Y inverted });\n\t\t * directionalCameraLight.setLightOn(false);\n\t\t * cameraGroup.getChildren().add(directionalCameraLight);\n\t\t */\n\n\t\t// Point light behind camera, similar to default JavaFX light\n\t\tPointLight cameraLight = new PointLight(Color.color(0.8, 0.8, 0.8));\n\t\tcameraLight.setConstantAttenuation(1);\n\t\tcameraLight.setLinearAttenuation(0);\n\t\tcameraLight.setQuadraticAttenuation(0);\n\t\tcameraLight.setLightOn(true);\n\t\tcameraGroup.getChildren().add(cameraLight);\n\t\t// listener keeps the light at the camera\n\t\tcamera.localToSceneTransformProperty().addListener((obs, oldT, newT) -> {\n\t\t\tfinal float distanceBehindCamera = 10000;\n\t\t\tPoint3D p = camera.localToScene(1000, 1000, -distanceBehindCamera);\n\t\t\tcameraLight.setTranslateX(-p.getX());\n\t\t\tcameraLight.setTranslateY(p.getY());\n\t\t\tcameraLight.setTranslateZ(-p.getZ());\n\t\t});\n\t\t// Ambient light - illuminates all faces equally regardless of normals\n\t\tAmbientLight ambientLight = new AmbientLight(Color.color(0.1, 0.1, 0.1));\n\t\tcameraGroup.getChildren().add(ambientLight);\n\t\t// Enable point light illumination for selected groups\n\t\tcameraLight.getScope().addAll(userGroup, controlHandleGroup, lookGroup);\n\t\tambientLight.getScope().addAll(userGroup, controlHandleGroup, lookGroup);\n\n\t\tCSG cylinder = new Cylinder(0, 2.5, 10, 20) // Top radius, bottom radius, height, nr. segments\n\t\t\t\t.toCSG().roty(90).setColor(Color.BLACK);\n\n\t\thandMesh = cylinder.getMesh();\n\n\t\thandGroup = new Group();\n\t\tif (addHand)\n\t\t\tshowHand();\n\n\t\tcamera.setNearClip(0.1);\n\t\t// camera.setFarClip(1000.0); // this is set in VirtualCameraMobileBase\n\t\tgetSubScene().setCamera(camera);\n\n\t\t// Flip the camera upside down\n\t\tcamera.setRotationAxis(Rotate.Z_AXIS);\n\t\tcamera.setRotate(180);\n\n\t\tcamera.setDepthTest(DepthTest.ENABLE);\n\t\tsetVirtualcam(new VirtualCameraMobileBase(camera, handGroup, this, name));\n\t\tVirtualCameraFactory.setFactory(new IVirtualCameraFactory() {\n\t\t\t@Override\n\t\t\tpublic AbstractImageProvider getVirtualCamera() {\n\t\t\t\tthrow new RuntimeException(\"No virtual camera available!\");\n\t\t\t}\n\t\t});\n\n\t\t// TODO reorient the start camera\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tgetFlyingCamera().setGlobalToFiducialTransform(defaultCameraView);\n\t\t});\n\n\t}\n\n\t/**\n\t * Gets the camera field of view property.\n\t *\n\t * @return the camera field of view property\n\t */\n\t// public DoubleProperty getCameraFieldOfViewProperty() {\n\t// return camera.fieldOfViewProperty();\n\t// }\n\n\t/**\n\t * Builds the axes.\n\t *\n\t * @param showAxes\n\t */\n\tprivate void buildAxes(boolean showAxes) {\n\n\t\t// int gridSize=1000;\n\t\t// int gridDensity=gridSize/10;\n\t\t//\n\t\t// PhongMaterial phongMaterial = new PhongMaterial();\n\t\t// phongMaterial.setDiffuseColor(Color.BLACK);\n\t\t// for (int i=-gridSize;i<gridSize;i++){\n\t\t// for (int j=-gridSize;j<gridSize;j++){\n\t\t// if (i%gridDensity==0 &&j%gridDensity==0){\n\t\t// Sphere s = new Sphere(1);\n\t\t// s.setMaterial(phongMaterial);\n\t\t// Affine sp=new Affine();\n\t\t// sp.setTy(i);\n\t\t// sp.setTx(j);\n\t\t// //com.neuronrobotics.sdk.common.Log.error(\"Placing sphere at \"+i+\" , \"+j);\n\t\t// s.getTransforms().add(sp);\n\t\t// ground.getChildren().add(s);\n\t\t// }\n\t\t// }\n\t\t// }\n\n\t\tnew Thread() {\n\n\t\t\tpublic void run() {\n\t\t\t\ttry {\n\t\t\t\t\t// Image ruler = AssetFactory.loadAsset(\"ruler.png\");\n\t\t\t\t\t// Image ruler = new Image(BowlerStudio.class.getResourceAsStream(\"ruler.png\"));\n\t\t\t\t\t// Image groundLocal = AssetFactory.loadAsset(\"ground.png\");\n\n\t\t\t\t\t// Create the rulers\n\t\t\t\t\tdouble scale = 1;\n\t\t\t\t\tAffine xRuler = new Affine();\n\t\t\t\t\txRuler.appendScale(scale, scale, scale);\n\t\t\t\t\txRuler.appendRotation(180, 0, 0, 0, 1, 0, 0);\n\t\t\t\t\tAffine xRulerZoffset = new Affine();\n\t\t\t\t\txRulerZoffset.setTz(-0.01); // Raise xRuler up a bit\n\n\t\t\t\t\tAffine yRuler = new Affine();\n\t\t\t\t\tyRuler.appendScale(scale, scale, scale);\n\t\t\t\t\tyRuler.appendRotation(90, 0, 0, 0, 0, 0, 1);\n\t\t\t\t\tAffine yRulerZoffset = new Affine();\n\t\t\t\t\tyRulerZoffset.setTz(0.01); // Raise yRuler up a bit\n\n\t\t\t\t\tAffine zRuler = new Affine();\n\t\t\t\t\tzRuler.appendScale(scale, scale, scale);\n\t\t\t\t\t// zRuler.appendRotation(-180, 0, 0, 0, 1, 0, 0);\n\t\t\t\t\t// zRuler.appendRotation( -90, 0, 0, 0, 0, 0, 1);\n\t\t\t\t\t// zRuler.appendRotation( 90, 0, 0, 0, 0, 1, 0);\n\t\t\t\t\t// zRuler.appendRotation(-180, 0, 0, 0, 1, 0, 0);\n\t\t\t\t\tzRuler.appendRotation(120, 0, 0, 0, 1, -1, 1);\n\n\t\t\t\t\t// Create the workplane\n\t\t\t\t\t// workplaneGroup = createGridMesh(1000, 1000, 20);\n\t\t\t\t\tworkplaneGroup = createTexturedWorkplane(1000, 1000);\n\n\t\t\t\t\tboolean selected = (showRuler != null) ? showRuler.isSelected() : true;\n\n\t\t\t\t\tAxis axes = new Axis(showAxes ? selected : false);\n\n\t\t\t\t\t// Lower XY-axes a bit\n\t\t\t\t\tAffine axisOffset = new Affine();\n\t\t\t\t\taxisOffset.setTz(-0.01);\n\t\t\t\t\taxes.getTransforms().add(axisOffset);\n\n\t\t\t\t\tBowlerStudio.runLater(() -> {\n\n\t\t\t\t\t\tNode xrulerImage = MakeRuler.createRuler(true);\n\t\t\t\t\t\tNode yrulerImage = MakeRuler.createRuler(false);\n\t\t\t\t\t\tNode zrulerImage = MakeRuler.createRuler(true);\n\n\t\t\t\t\t\txrulerImage.getTransforms().addAll(getRulerInWorkplaneOffset(), getRulerOffset(), xRuler,\n\t\t\t\t\t\t\t\txRulerZoffset);\n\t\t\t\t\t\tyrulerImage.getTransforms().addAll(getRulerInWorkplaneOffset(), getRulerOffset(), yRuler,\n\t\t\t\t\t\t\t\tyRulerZoffset);\n\t\t\t\t\t\tzrulerImage.getTransforms().addAll(getRulerInWorkplaneOffset(), getRulerOffset(), zRuler);\n\n\t\t\t\t\t\trulerGroup.getChildren().addAll(xrulerImage, yrulerImage, zrulerImage);\n\t\t\t\t\t\tgridGroup.getChildren().addAll(rulerGroup);\n\n\t\t\t\t\t\tAffine groundPlacement = new Affine();\n\t\t\t\t\t\tgroundPlacement.setTz(-1);\n\t\t\t\t\t\t// groundGroup.setOpacity(.5);\n\t\t\t\t\t\tgroundGroup = new Group();\n\t\t\t\t\t\tgroundGroup.getTransforms().add(groundPlacement);\n\n\t\t\t\t\t\tcameraGroup.getChildren().add(getVirtualcam().getCameraFrame());\n\n\t\t\t\t\t\tif (showAxes) {\n\t\t\t\t\t\t\tgridGroup.getChildren().addAll(axes, groundGroup);\n\t\t\t\t\t\t\tshowAxis();\n\t\t\t\t\t\t\tcustomWorkplaneGroup.getChildren().add(workplaneGroup);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Count how many nodes are already present in the userGroup, they are not user\n\t\t\t\t\t\t// objects\n\t\t\t\t\t\tif (showAxes)\n\t\t\t\t\t\t\tSKIP_USERGROUP_NODES = userGroup.getChildren().size();\n\n\t\t\t\t\t\t// Create the world group\n\t\t\t\t\t\tworld.getChildren().addAll(lookGroup, cameraGroup, userGroup, axisGroup, customWorkplaneGroup,\n\t\t\t\t\t\t\t\tcontrolHandleGroup, ambientLight);\n\n\t\t\t\t\t\t// Use ambient illumination for workplanes and axes, ruler is black so no need\n\t\t\t\t\t\t// to illuminate\n\t\t\t\t\t\tambientLight.getScope().addAll(customWorkplaneGroup, axisGroup);\n\t\t\t\t\t});\n\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(e);\n\t\t\t\t}\n\t\t\t}\n\n\t\t}.start();\n\n\t}\n\n\tpublic Group getWorkplaneGroup() {\n\t\treturn workplaneGroup;\n\t}\n\n\tpublic Group createGridMesh(int width, int height, int cellSize) {\n\t\treturn createGridMesh(width, height, cellSize, 0.05);\n\t}\n\n\tpublic Group createGridMesh(int width, int height, int cellSize, double lineThickness) {\n\t\tAffine groundMove = new Affine();\n\t\tgroundMove.setTx(-width / 2.0);\n\t\tgroundMove.setTy(-height / 2.0);\n\n\t\tGroup gridMeshGroup = new Group();\n\n\t\t// Create material for lines\n\t\tPhongMaterial material = new PhongMaterial();\n\t\tmaterial.setDiffuseColor(Color.LIGHTBLUE);\n\n\t\tint numXLines = (width / cellSize) + 1;\n\t\tint numYLines = (height / cellSize) + 1;\n\n\t\t// Create horizontal lines\n\t\tfor (int y = 0; y < numYLines; y++) {\n\t\t\tdouble yPos = y * cellSize;\n\t\t\tBox horizontalLine = new Box(width, lineThickness, lineThickness);\n\t\t\thorizontalLine.setMaterial(material);\n\t\t\thorizontalLine.setTranslateX(width / 2.0);\n\t\t\thorizontalLine.setTranslateY(yPos);\n\t\t\thorizontalLine.setTranslateZ(-lineThickness);\n\t\t\thorizontalLine.setMouseTransparent(true);\n\t\t\thorizontalLine.setCullFace(CullFace.NONE);// backs are black\n\t\t\tgridMeshGroup.getChildren().add(horizontalLine);\n\t\t}\n\n\t\t// Create vertical lines\n\t\tfor (int x = 0; x < numXLines; x++) {\n\t\t\tdouble xPos = x * cellSize;\n\t\t\tBox verticalLine = new Box(lineThickness, height, lineThickness);\n\t\t\tverticalLine.setMaterial(material);\n\t\t\tverticalLine.setTranslateX(xPos);\n\t\t\tverticalLine.setTranslateY(height / 2.0);\n\t\t\tverticalLine.setTranslateZ(-lineThickness);\n\t\t\tverticalLine.setMouseTransparent(true);\n\t\t\tverticalLine.setCullFace(CullFace.NONE);// backs are black\n\t\t\tgridMeshGroup.getChildren().add(verticalLine);\n\t\t}\n\n\t\tgridMeshGroup.getTransforms().addAll(gridPlacementAffine, groundMove);\n\n\t\treturn gridMeshGroup;\n\t}\n\n\t// Add the control nodes (handles/edit boxes) at the end so they are always\n\t// visible\n\tpublic void addControlNode(Node n) {\n\t\tBowlerStudioModularFrame bowlerStudioModularFrame = BowlerStudioModularFrame.getBowlerStudioModularFrame();\n\t\tif (bowlerStudioModularFrame != null)\n\t\t\tbowlerStudioModularFrame.showCreatureLab();\n\n\t\tif (Platform.isFxApplicationThread())\n\t\t\tcontrolHandleGroup.getChildren().add(n);\n\t\telse\n\t\t\tBowlerStudio.runLater(() -> controlHandleGroup.getChildren().add(n));\n\t}\n\n\tpublic void removeControlNode(Node n) {\n\t\tBowlerStudio.runLater(() -> controlHandleGroup.getChildren().remove(n));\n\t}\n\n\t// Check if the userGroup contains a node\n\tpublic boolean contains(Node n) {\n\t\treturn userGroup.getChildren().contains(n);\n\t}\n\n\t// Add nodes to the userGroup\n\tpublic void addUserNode(Node n) {\n\t\tBowlerStudioModularFrame bowlerStudioModularFrame = BowlerStudioModularFrame.getBowlerStudioModularFrame();\n\t\tif (bowlerStudioModularFrame != null)\n\t\t\tbowlerStudioModularFrame.showCreatureLab();\n\n\t\tif (Platform.isFxApplicationThread())\n\t\t\tuserGroup.getChildren().add(n);\n\t\telse\n\t\t\tBowlerStudio.runLater(() -> userGroup.getChildren().add(n));\n\t}\n\n\t// Add nodes to the customWorkplaneGroup\n\tpublic void addCustomWorkplaneNode(Node n) {\n\t\tBowlerStudioModularFrame bowlerStudioModularFrame = BowlerStudioModularFrame.getBowlerStudioModularFrame();\n\t\tif (bowlerStudioModularFrame != null)\n\t\t\tbowlerStudioModularFrame.showCreatureLab();\n\t\tif (Platform.isFxApplicationThread())\n\t\t\tcustomWorkplaneGroup.getChildren().add(n);\n\t\telse\n\t\t\tBowlerStudio.runLater(() -> customWorkplaneGroup.getChildren().add(n));\n\t}\n\n\t// Remove nodes from the userGroup\n\tpublic void removeUserNode(Node n) {\n\t\tBowlerStudio.runLater(() -> userGroup.getChildren().remove(n));\n\t}\n\n\t// Clear all object from the userGroup\n\tpublic void clearUserNode() {\n\t\t// new RuntimeException(\"Clearing all user nodes!\");\n\t\tBowlerStudio.runLater(() -> userGroup.getChildren().clear());\n\t}\n\n\tpublic void showAxis() {\n\t\tBowlerStudio.runLater(() -> axisGroup.getChildren().add(gridGroup));\n\t\tfor (MeshView a : axisMap.keySet()) {\n\t\t\taxisMap.get(a).show();\n\t\t}\n\t}\n\n\tpublic void hideAxis() {\n\t\tBowlerStudio.runLater(() -> axisGroup.getChildren().remove(gridGroup));\n\t\tfor (MeshView a : axisMap.keySet()) {\n\t\t\taxisMap.get(a).hide();\n\t\t}\n\t}\n\n\tprivate void autoSpin() {\n\t\ttry {\n\t\t\tlong diff = System.currentTimeMillis() - getLastMosueMovementTime();\n\t\t\tif (spin != null)\n\t\t\t\tif (diff > timeForAutospin && spin.isSelected()) {\n\t\t\t\t\t// TODO start spinning\n\t\t\t\t\tdouble scale = 0.5;\n\t\t\t\t\tlong finaSpeedScale = timeForAutospin + (timeForAutospin / 2);\n\t\t\t\t\tif (diff < finaSpeedScale) {\n\t\t\t\t\t\tdouble finaSpeedDiff = ((double) (finaSpeedScale - diff));\n\t\t\t\t\t\tdouble sineScale = (finaSpeedDiff / ((double) (timeForAutospin / 2)));\n\t\t\t\t\t\tscale = 1 - Math.sin(sineScale * (Math.PI / 2));\n\t\t\t\t\t\tmoveCamera(new TransformNR(0, 0, 0, new RotationNR(0, 0.5 * scale, 0)));\n\t\t\t\t\t} else {\n\t\t\t\t\t\tmoveCamera(autoSpinSpeed);\n\t\t\t\t\t}\n\n\t\t\t\t}\n\t\t} catch (Exception | Error e) {\n\t\t\t// com.neuronrobotics.sdk.common.Log.error(e);\n\t\t}\n\n\t}\n\n\t/**\n\t * Handle mouse.\n\t *\n\t * @param scene the scene\n\t */\n\n\tpublic void handleMouse(Node scene) {\n\t\tif (disabeControl) {\n\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"No mouse control added \" + name);\n\t\t\tscene.setPickOnBounds(false);\n\t\t\treturn;\n\t\t}\n\n\t\tcom.neuronrobotics.sdk.common.Log.debug(\"Setting up Mouse Handelers \" + name);\n\t\tscene.addEventFilter(MouseEvent.MOUSE_CLICKED, event -> {\n\t\t\tresetMouseTime();\n\t\t\tif (getControlsMap().timeToCancel(event))\n\t\t\t\tcancelSelection();\n\t\t});\n\n\t\tscene.addEventFilter(MouseEvent.MOUSE_PRESSED, new EventHandler<MouseEvent>() {\n\t\t\t@Override\n\t\t\tpublic void handle(MouseEvent me) {\n\t\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"Bowler 3d start \" + name);\n\t\t\t\tmousePosX = me.getSceneX();\n\t\t\t\tmousePosY = me.getSceneY();\n\t\t\t\tmouseOldX = me.getSceneX();\n\t\t\t\tmouseOldY = me.getSceneY();\n\t\t\t\tif (me.isPrimaryButtonDown())\n\t\t\t\t\tcaptureMouse = true;\n\t\t\t\telse\n\t\t\t\t\tcaptureMouse = false;\n\t\t\t\tresetMouseTime();\n\t\t\t}\n\t\t});\n\n\t\tscene.addEventFilter(MouseEvent.MOUSE_DRAGGED, new EventHandler<MouseEvent>() {\n\n\t\t\t@Override\n\t\t\tpublic void handle(MouseEvent me) {\n\t\t\t\tresetMouseTime();\n\n\t\t\t\tNode node = (Node) me.getSource();\n\t\t\t\tdouble mouseY = me.getY(); // Y position relative to the node itself\n\t\t\t\tdouble nodeHeight = node.getBoundsInLocal().getHeight();\n\t\t\t\taboveSplit = mouseY < (nodeHeight / 2);\n\n\t\t\t\tmouseOldX = mousePosX;\n\t\t\t\tmouseOldY = mousePosY;\n\t\t\t\tmousePosX = me.getSceneX();\n\t\t\t\tmousePosY = me.getSceneY();\n\t\t\t\tmouseDeltaX = (mousePosX - mouseOldX);\n\t\t\t\tmouseDeltaY = (mousePosY - mouseOldY);\n\t\t\t\tdouble modifier = 1.0;\n\t\t\t\tdouble modifierFactor = 0.1;\n\n\t\t\t\tif (getControlsMap().isSlowMove(me))\n\t\t\t\t\tmodifier = 0.1;\n\n\t\t\t\tif (getControlsMap().isRotate(me)) {\n\t\t\t\t\tdouble el = getVirtualcam().getTiltAngle();\n\t\t\t\t\tboolean above = (el > 0);\n\t\t\t\t\t// if (aboveSplit) {\n\t\t\t\t\t// above=!above;\n\t\t\t\t\t// }\n\t\t\t\t\t// System.out.println(\"Above = \"+el);\n\t\t\t\t\tdouble i = above ? -1 : 1;\n\t\t\t\t\tTransformNR trans = new TransformNR(0, 0, 0,\n\t\t\t\t\t\t\tnew RotationNR(mouseDeltaY * modifierFactor * modifier * mouseScale,\n\t\t\t\t\t\t\t\t\ti * mouseDeltaX * modifierFactor * modifier * mouseScale, 0));\n\t\t\t\t\tmoveCamera(trans);\n\t\t\t\t}\n\n\t\t\t\tif (getControlsMap().isMove(me) && move) {\n\t\t\t\t\tdouble depth = -100 / getVirtualcam().getZoomDepth();\n\n\t\t\t\t\t// Limit smallest movement amount\n\t\t\t\t\tdepth = Math.min(100, depth);\n\t\t\t\t\tTransformNR newPose = new TransformNR(\n\t\t\t\t\t\t\tmouseDeltaX * modifierFactor * modifier * (mouseScale / 2) / depth,\n\t\t\t\t\t\t\tmouseDeltaY * modifierFactor * modifier * (mouseScale / 2) / depth, 0, new RotationNR());\n\t\t\t\t\tmoveCamera(newPose);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tscene.addEventHandler(ScrollEvent.ANY, t -> {\n\t\t\tif (getControlsMap().isZoom(t)) {\n\t\t\t\tdouble deltaY = t.getDeltaY();\n\t\t\t\tzoomIncrement(deltaY);\n\t\t\t}\n\t\t\tt.consume();\n\t\t});\n\n\t}\n\n\t/*\n\t * DEVELOPMENT --add-exports javafx.graphics/com.sun.javafx.scene=ALL-UNNAMED\n\t * --add-exports javafx.graphics/com.sun.javafx.geom=ALL-UNNAMED --add-exports\n\t * javafx.graphics/com.sun.javafx.scene.input=ALL-UNNAMED --add-exports\n\t * javafx.graphics/com.sun.javafx.geom.transform=ALL-UNNAMED public double\n\t * objectDistance() {\n\t *\n\t * Point3D p = camera.localToScene(0, 0, 0); Vec3d camPos = new Vec3d(-p.getX(),\n\t * p.getY(), -p.getZ());\n\t *\n\t * Point3D dir = camera.localToScene(0, 0, -1).subtract(camera.localToScene(0,\n\t * 0, 0)).normalize(); Vec3d camDir = new Vec3d(dir.getX(), -dir.getY(),\n\t * dir.getZ());\n\t *\n\t * //System.out.println(\"\\nCamera position : \" + camPos); //System.out.println(\n\t * \"Camera direction: \" + camDir);\n\t *\n\t * PickRay ray = new PickRay(camPos, camDir, 0.1, 499);\n\t *\n\t * PickResultChooser chooser = new PickResultChooser();\n\t * NodeHelper.pickNode(userGroup, ray, chooser);\n\t *\n\t * PickResult pr = chooser.toPickResult();\n\t *\n\t * if ((pr != null) && (pr.getIntersectedNode() != null)) { double dist =\n\t * pr.getIntersectedDistance();\n\t *\n\t * //System.out.println(\">>> HIT POINT: \" + pr.getIntersectedPoint() +\n\t * \" Distance: \" + (int)dist);\n\t *\n\t * return dist; }\n\t *\n\t * return Double.POSITIVE_INFINITY; }\n\t */\n\n\tpublic double getCamDistanceToClosestObject() {\n\n\t\tPoint3D camPos = camera.localToScene(0, 0, 0);\n\n\t\t// Adjust for camera rotation\n\t\tcamPos = new Point3D(-camPos.getX(), camPos.getY(), -camPos.getZ());\n\t\tPoint3D camDir = camera.localToScene(0, 0, -1).subtract(camera.localToScene(0, 0, 0)).normalize();\n\t\tcamDir = new Point3D(camDir.getX(), -camDir.getY(), camDir.getZ());\n\n\t\tint counter = 0;\n\t\tdouble minDist = Double.MAX_VALUE;\n\t\tPoint3D closestPoint = new Point3D(0, 0, 0);\n\n\t\tList<Node> children = userGroup.getChildren();\n\t\tfor (int i = SKIP_USERGROUP_NODES; i < children.size(); i++) {\n\n\t\t\tNode node = children.get(i);\n\n\t\t\t// Find closest point to camera on bounding box\n\t\t\tBounds b = node.getBoundsInParent();\n\t\t\tdouble closestX = Math.max(b.getMinX(), Math.min(camPos.getX(), b.getMaxX()));\n\t\t\tdouble closestY = Math.max(b.getMinY(), Math.min(camPos.getY(), b.getMaxY()));\n\t\t\tdouble closestZ = Math.max(b.getMinZ(), Math.min(camPos.getZ(), b.getMaxZ()));\n\n\t\t\tPoint3D boxPoint = new Point3D(closestX, closestY, closestZ);\n\n\t\t\t// Direction from camera to closest point on box\n\t\t\tPoint3D toObject = boxPoint.subtract(camPos);\n\t\t\tdouble distToPoint = toObject.magnitude();\n\n\t\t\t// Inside object - skip FOV check\n\t\t\tif (distToPoint < 0.001) {\n\t\t\t\tif (distToPoint < minDist) {\n\t\t\t\t\tminDist = 0;\n\t\t\t\t\tclosestPoint = boxPoint;\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Skip objects behind the camera (negative dot product)\n\t\t\tdouble dot = camDir.dotProduct(toObject);\n\t\t\tif (dot <= 0)\n\t\t\t\tcontinue;\n\n\t\t\t// Check FOV with dot product, 60dg FOV, cos(30)\n\t\t\tdouble cosAngle = dot / toObject.magnitude();\n\t\t\tif (cosAngle < Math.cos(Math.toRadians(30)))\n\t\t\t\tcontinue;\n\n\t\t\tif (distToPoint < minDist) {\n\t\t\t\tminDist = distToPoint;\n\t\t\t\tclosestPoint = boxPoint;\n\t\t\t}\n\t\t}\n\n\t\treturn Math.max(2, minDist);\n\t}\n\n\tpublic void zoomIncrement(double deltaY) {\n\t\tdouble zoomFactor = -deltaY * getVirtualcam().getZoomDepth() / 500;\n\n\t\t/*\n\t\t * EXPERIMENTAL FEATURE, SLOW DOWN ZOOM WHEN CLOSE TO OBJECT\n\t\t *\n\t\t * double distance = getCamDistanceToClosestObject();\n\t\t *\n\t\t * // Parameters to control zoom in behavior final double ZOOM_IN_START_DISTANCE\n\t\t * = 5; final double ZOOM_IN_STEP_REDUCTION = 2; if (ZOOM_IN_START_DISTANCE *\n\t\t * zoomFactor > distance) zoomFactor = distance / (ZOOM_IN_START_DISTANCE *\n\t\t * ZOOM_IN_STEP_REDUCTION);\n\t\t *\n\t\t * // Parameters to control zoom out behavior final double\n\t\t * ZOOM_OUT_START_DISTANCE = 3; final double ZOOM_OUT_STEP_REDUCTION = 2; if\n\t\t * (-ZOOM_OUT_START_DISTANCE * zoomFactor > distance) zoomFactor = -distance /\n\t\t * (ZOOM_OUT_START_DISTANCE * ZOOM_OUT_STEP_REDUCTION);\n\t\t */\n\t\t// double z = camera.getTranslateY();\n\t\t// double newZ = z + zoomFactor;\n\t\t// camera.setTranslateY(newZ);\n\t\t// com.neuronrobotics.sdk.common.Log.error(\"Z = \"+zoomFactor);\n\n\t\tgetVirtualcam().setZoomDepth(getVirtualcam().getZoomDepth() + zoomFactor);\n\n\t\t// In addition to the zoom also move a bit closer, gives unlimited zoom\n\t\tdouble moveCloser = (deltaY > 0) ? 0.1 : -0.1;\n\t\tTransformNR zoomMove = new TransformNR();\n\t\tzoomMove.translateZ(moveCloser);\n\n\t\tmoveCamera(zoomMove);\n\t}\n\n\tpublic void moveCamera(TransformNR newPose) {\n\t\tgetFlyingCamera().DriveArc(newPose);\n\t}\n\n\tprivate void selectObjectsSourceFile(CSG source) {\n\t\tnew Thread(() -> {\n\t\t\t// this code is thread safed\n\t\t\tBowlerStudioController.getBowlerStudio().clearHighlits();\n\t\t\tdebuggerList.clear();\n\t\t\tdebuggerIndex = 0;\n\n\t\t\tfor (String ex : source.getCreationEventStackTraceList()) {\n\t\t\t\t// Thread safed\n\t\t\t\tString fileName = getFilenameFromTrace(ex);\n\t\t\t\tint linNum = getLineNumbereFromTrace(ex);\n\n\t\t\t\tboolean duplicate = false;\n\t\t\t\tfor (String have : debuggerList) {\n\t\t\t\t\tif (getFilenameFromTrace(have).contentEquals(fileName) && getLineNumbereFromTrace(have) == linNum)\n\t\t\t\t\t\tduplicate = true;\n\t\t\t\t}\n\t\t\t\tif (!duplicate)\n\t\t\t\t\tdebuggerList.add(0, ex);\n\n\t\t\t\tlastFileSelected = fileName;\n\t\t\t\tlastFileLine = linNum;\n\t\t\t\t// this code is thread safed\n\t\t\t\tBowlerStudioController.getBowlerStudio().setHighlight(locateFile(fileName, source), linNum,\n\t\t\t\t\t\tjava.awt.Color.PINK);\n\n\t\t\t}\n\t\t\tdebuggerIndex = debuggerList.size() - 1;\n\t\t}).start();\n\t\t// BowlerStudio.runLater(()->{\n\t\t// fwd.disableProperty().set(false);\n\t\t// back.disableProperty().set(true);\n\t\t// });\n\n\t}\n\n\tprivate File locateFile(String fileName, CSG source) {\n\t\tFile f = csgSourceFile.get(source);\n\t\tif (f != null && f.getName().contains(fileName))\n\t\t\treturn f;\n\t\treturn ScriptingEngine.getFileEngineRunByName(fileName);\n\t}\n\n\t// @Override\n\t// public void start(Stage primaryStage) {\n\t//\n\t//\n\t// }\n\n\t/**\n\t * Gets the sub scene.\n\t *\n\t * @return the sub scene\n\t */\n\tprivate SubScene getSubScene() {\n\t\treturn scene;\n\t}\n\n\t/**\n\t * Sets the sub scene.\n\t *\n\t * @param scene the new sub scene\n\t */\n\tpublic void setSubScene(SubScene scene) {\n\t\tcom.neuronrobotics.sdk.common.Log.debug(\"Setting UI scene\");\n\t\tthis.scene = scene;\n\t}\n\n\t/**\n\t * Gets the root.\n\t *\n\t * @return the root\n\t */\n\tpublic Group getRoot() {\n\t\treturn rootGroup;\n\t}\n\n\t/**\n\t * Removes the arm.\n\t */\n\tpublic void removeArm() {\n\t\tworld.getChildren().remove(manipulatorGroup);\n\t}\n\n\tpublic VirtualCameraMobileBase getVirtualcam() {\n\t\treturn flyingCamera;\n\t}\n\n\tpublic void setVirtualcam(VirtualCameraMobileBase virtualcam) {\n\t\tthis.flyingCamera = virtualcam;\n\t}\n\n\tpublic VirtualCameraMobileBase getFlyingCamera() {\n\t\treturn flyingCamera;\n\t}\n\n\tpublic void setFlyingCamera(VirtualCameraMobileBase flyingCamera) {\n\t\tthis.flyingCamera = flyingCamera;\n\t}\n\n\t// public static TransformNR getOffsetforvisualization() {\n\t// return offsetForVisualization;\n\t// }\n\n\tpublic CSG getSelectedCsg() {\n\t\treturn selectedCsg;\n\t}\n\n\tpublic void cancelSelection() {\n\t\tfor (CSG key : getCsgMap().keySet()) {\n\n\t\t\tBowlerStudio.runLater(() -> getCsgMap().get(key).setMaterial(new PhongMaterial(key.getColor())));\n\t\t}\n\n\t\tthis.selectedCsg = null;\n\t\t// new Exception().printStackTrace();\n\t\tfocusToAffine(new TransformNR(), new Affine());\n\t\tresetMouseTime();\n\t}\n\n\t/**\n\t * Select a provided affine that is in a given global pose\n\t *\n\t * @param startingLocation the starting pose\n\t * @param rootListener     what affine to attach to\n\t */\n\tpublic void setSelected(TransformNR startingLocation, Affine rootListener) {\n\t\tfocusToAffine(startingLocation, rootListener);\n\t}\n\n\t/**\n\t * Select a provided affine that is in a given global pose\n\t *\n\t * @param startingLocation the starting pose\n\t * @param rootListener     what affine to attach to\n\t */\n\tpublic void setSelected(Affine rootListener) {\n\t\tfocusToAffine(new TransformNR(), rootListener);\n\t}\n\n\tpublic void setSelectedCsg(List<CSG> selectedCsg) {\n\t\t// com.neuronrobotics.sdk.common.Log.error(\"Selecting group\");\n\t\tsetSelectedCsg(selectedCsg.get(selectedCsg.size() - 1));\n\t\ttry {\n\n\t\t\tfor (int in = 0; in < selectedCsg.size() - 1; in++) {\n\t\t\t\tint i = in;\n\t\t\t\tMeshView mesh = getCsgMap().get(selectedCsg.get(i));\n\t\t\t\tif (mesh != null)\n\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tmesh.setMaterial(new PhongMaterial(Color.GOLD));\n\t\t\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t}\n\t\t} catch (java.lang.NullPointerException ex0) {\n\t\t} // if a selection is called before the limb is loaded\n\t\tresetMouseTime();\n\t}\n\n\tpublic void setSelectedCsg(CSG scg) {\n\t\tsetSelectedCsg(scg, false);\n\t}\n\n\tpublic void setSelectedCsg(CSG scg, boolean justHighlight) {\n\n\t\tif (scg == this.selectedCsg)\n\t\t\treturn;\n\n\t\tif (focusing)\n\t\t\treturn;\n\n\t\tif (scg == null)\n\t\t\treturn;\n\n\t\tthis.selectedCsg = scg;\n\n\t\tfor (CSG key : getCsgMap().keySet()) {\n\n\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\ttry {\n\t\t\t\t\tgetCsgMap().get(key).setMaterial(new PhongMaterial(key.getColor()));\n\t\t\t\t} catch (Throwable ex) {\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\tlastSelectedTime = System.currentTimeMillis();\n\n\t\tBowlerStudio.runLater(() -> {\n\t\t\ttry {\n\t\t\t\tgetCsgMap().get(selectedCsg).setMaterial(new PhongMaterial(Color.GOLD));\n\t\t\t} catch (Exception e) {\n\t\t\t}\n\t\t});\n\n\t\tif (!justHighlight) {\n\t\t\tdouble xcenter = selectedCsg.getMaxX() / 2 + selectedCsg.getMinX() / 2;\n\t\t\tdouble ycenter = selectedCsg.getMaxY() / 2 + selectedCsg.getMinY() / 2;\n\t\t\tdouble zcenter = selectedCsg.getMaxZ() / 2 + selectedCsg.getMinZ() / 2;\n\n\t\t\tTransformNR poseToMove = new TransformNR();\n\t\t\tCSG finalCSG = selectedCsg;\n\t\t\tif ((selectedCsg.getMaxX() < 1) || (selectedCsg.getMinX() > -1)) {\n\t\t\t\tfinalCSG = finalCSG.movex(-xcenter);\n\t\t\t\tposeToMove.translateX(xcenter);\n\t\t\t}\n\n\t\t\tif ((selectedCsg.getMaxY() < 1) || (selectedCsg.getMinY() > -1)) {\n\t\t\t\tfinalCSG = finalCSG.movey(-ycenter);\n\t\t\t\tposeToMove.translateY(ycenter);\n\t\t\t}\n\n\t\t\tif ((selectedCsg.getMaxZ() < 1) || (selectedCsg.getMinZ() > -1)) {\n\t\t\t\tfinalCSG = finalCSG.movez(-zcenter);\n\t\t\t\tposeToMove.translateZ(zcenter);\n\t\t\t}\n\n\t\t\tAffine manipulator2;\n\t\t\ttry {\n\t\t\t\tmanipulator2 = selectedCsg.hasManipulator() ? selectedCsg.getManipulator() : new Affine();\n\t\t\t\tfocusToAffine(poseToMove, manipulator2);\n\t\t\t} catch (MissingManipulatorException e) {\n\t\t\t\t// TODO Auto-generated catch block\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\n\t\t}\n\t\tresetMouseTime();\n\t}\n\n\tpublic void focusTo(TransformNR poseToMove) {\n\t\tfocusToAffine(poseToMove, new Affine());\n\t}\n\n\tpublic void focusToAffine(TransformNR poseToMove, Affine manipulator2) {\n\t\tif (focusing)\n\t\t\treturn;\n\n\t\tif (manipulator2 == null) {\n\t\t\tcom.neuronrobotics.sdk.common.Log.error(new RuntimeException(\"Can not focus on null affine\"));\n\t\t\treturn;\n\t\t}\n\n\t\tfocusing = true;\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tAffine centering = TransformFactory.nrToAffine(poseToMove);\n\t\t\t// this section keeps the camera oriented the same way to avoid whipping\n\t\t\t// around\n\n\t\t\tTransformNR rotationOnlyCOmponentOfManipulator = TransformFactory.affineToNr(manipulator2);\n\t\t\trotationOnlyCOmponentOfManipulator.setX(0);\n\t\t\trotationOnlyCOmponentOfManipulator.setY(0);\n\t\t\trotationOnlyCOmponentOfManipulator.setZ(0);\n\t\t\tTransformNR reverseRotation = rotationOnlyCOmponentOfManipulator.inverse();\n\t\t\tTransformNR startSelectNr = previousTarget.copy();\n\t\t\tTransformNR targetNR;// =\n\t\t\t\t\t\t\t\t\t// TransformFactory.affineToNr(selectedCsg.getManipulator());\n\t\t\tif ((Math.abs(manipulator2.getTx()) > 0.1) || (Math.abs(manipulator2.getTy()) > 0.1)\n\t\t\t\t\t|| (Math.abs(manipulator2.getTz()) > 0.1)) {\n\t\t\t\ttargetNR = TransformFactory.affineToNr(manipulator2);\n\t\t\t} else {\n\t\t\t\ttargetNR = TransformFactory.affineToNr(centering);\n\t\t\t}\n\t\t\tthis.poseToMove = targetNR;\n\t\t\tAffine interpolator = new Affine();\n\t\t\tAffine correction = TransformFactory.nrToAffine(reverseRotation);\n\t\t\tinterpolator.setTx(startSelectNr.getX() - targetNR.getX());\n\t\t\tinterpolator.setTy(startSelectNr.getY() - targetNR.getY());\n\t\t\tinterpolator.setTz(startSelectNr.getZ() - targetNR.getZ());\n\t\t\tremoveAllFocusTransforms();\n\t\t\tcameraGroup.getTransforms().add(interpolator);\n\t\t\ttry {\n\t\t\t\tif ((Math.abs(manipulator2.getTx()) > 0.1) || (Math.abs(manipulator2.getTy()) > 0.1)\n\t\t\t\t\t\t|| (Math.abs(manipulator2.getTz()) > 0.1)) {\n\t\t\t\t\t// BowlerStudio.runLater(() -> {\n\t\t\t\t\tcameraGroup.getTransforms().add(manipulator2);\n\t\t\t\t\tcameraGroup.getTransforms().add(correction);\n\t\t\t\t\t// });\n\n\t\t\t\t} else\n\t\t\t\t\t// BowlerStudio.runLater(() -> {\n\t\t\t\t\tcameraGroup.getTransforms().add(centering);\n\t\t\t\t// });\n\t\t\t} catch (Exception ex) {\n\n\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(ex);\n\t\t\t}\n\n\t\t\tfocusInterpolate(startSelectNr, targetNR, NUMBER_OF_INTERPOLATION_STEPS, interpolator);\n\t\t});\n\t}\n\n\tpublic void targetAndFollow(TransformNR poseToMove, Affine manipulator2) {\n\t\tthis.poseToMove = poseToMove;\n\n\t\tif (focusing)\n\t\t\treturn;\n\n\t\tif (manipulator2 == null) {\n\t\t\tcom.neuronrobotics.sdk.common.Log.error(new RuntimeException(\"Can not focus on null affine\"));\n\t\t\treturn;\n\t\t}\n\n\t\tfocusing = true;\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tAffine referenceFrame = TransformFactory.nrToAffine(poseToMove);\n\t\t\t// this section keeps the camera oriented the same way to avoid whipping around\n\n\t\t\tTransformNR rotationOnlyCOmponentOfManipulator2 = poseToMove.copy();\n\t\t\trotationOnlyCOmponentOfManipulator2.setX(0);\n\t\t\trotationOnlyCOmponentOfManipulator2.setY(0);\n\t\t\trotationOnlyCOmponentOfManipulator2.setZ(0);\n\t\t\tTransformNR reverseRotation2 = rotationOnlyCOmponentOfManipulator2.inverse();\n\t\t\tAffine correction2 = TransformFactory.nrToAffine(reverseRotation2);\n\n\t\t\tTransformNR rotationOnlyCOmponentOfManipulator = TransformFactory.affineToNr(manipulator2);\n\t\t\trotationOnlyCOmponentOfManipulator.setX(0);\n\t\t\trotationOnlyCOmponentOfManipulator.setY(0);\n\t\t\trotationOnlyCOmponentOfManipulator.setZ(0);\n\t\t\tTransformNR reverseRotation = rotationOnlyCOmponentOfManipulator.inverse();\n\t\t\tAffine correction = TransformFactory.nrToAffine(reverseRotation);\n\n\t\t\tTransformNR startSelectNr = previousTarget.copy();\n\t\t\t// =\n\t\t\t// TransformFactory.affineToNr(selectedCsg.getManipulat/or());\n\n\t\t\ttargetNR = poseToMove.times(TransformFactory.affineToNr(manipulator2));\n\n\t\t\tAffine interpolator = new Affine();\n\t\t\tinterpolator.setTx(startSelectNr.getX() - targetNR.getX());\n\t\t\tinterpolator.setTy(startSelectNr.getY() - targetNR.getY());\n\t\t\tinterpolator.setTz(startSelectNr.getZ() - targetNR.getZ());\n\t\t\tremoveAllFocusTransforms();\n\t\t\tcameraGroup.getTransforms().add(interpolator);\n\t\t\tcameraGroup.getTransforms().add(referenceFrame);\n\t\t\ttry {\n\t\t\t\tcameraGroup.getTransforms().add(manipulator2);\n\t\t\t\tcameraGroup.getTransforms().add(correction);\n\t\t\t\tcameraGroup.getTransforms().add(correction2);\n\t\t\t} catch (Exception ex) {\n\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(ex);\n\t\t\t}\n\t\t\tfocusInterpolate(startSelectNr, targetNR, NUMBER_OF_INTERPOLATION_STEPS, interpolator);\n\t\t});\n\t}\n\n\tprivate void resetMouseTime() {\n\t\t// com.neuronrobotics.sdk.common.Log.error(\"Resetting mouse\");\n\t\tthis.lastMosueMovementTime = System.currentTimeMillis();\n\t}\n\n\tdouble bound180(double in) {\n\t\twhile (in > 180)\n\t\t\tin -= 360;\n\t\twhile (in < -180)\n\t\t\tin += 360;\n\t\treturn in;\n\t}\n\n\tpublic void focusOrientation(TransformNR orient) {\n\t\tfocusOrientation(orient, null, getFlyingCamera().getDefaultZoomDepth());\n\t}\n\n\tpublic void focusOrientation(TransformNR orient, TransformNR trans, double zoom) {\n\n\t\tabortFocus = true;\n\n\t\tif ((orient != null) || (trans != null)) {\n\n\t\t\t// Wait until possible previous focus aborts\n\t\t\tnew Thread(() -> {\n\t\t\t\twhile (focusing) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tThread.sleep(8);\n\t\t\t\t\t} catch (InterruptedException e) {\n\t\t\t\t\t\tfocusing = false;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfocusing = true;\n\t\t\t\tabortFocus = false;\n\t\t\t\trunSyncFocus(orient, trans, zoom);\n\t\t\t}).start();\n\t\t}\n\t}\n\n\tprivate void runSyncFocus(TransformNR orient, TransformNR trans, double zoom) {\n\n\t\tdouble az = (orient == null)\n\t\t\t\t? 0\n\t\t\t\t: bound180(getFlyingCamera().getPanAngle() - 90\n\t\t\t\t\t\t+ Math.toDegrees(orient.getRotation().getRotationAzimuthRadians()));\n\n\t\tdouble el = (orient == null)\n\t\t\t\t? 0\n\t\t\t\t: bound180(getFlyingCamera().getTiltAngle() + 90\n\t\t\t\t\t\t+ Math.toDegrees(orient.getRotation().getRotationElevationRadians()));\n\t\t// com.neuronrobotics.sdk.common.Log.error(\"Focus from\\n\\taz:\" + az + \" \\n\\tel:\"\n\t\t// + el);\n\t\tdouble x = 0;\n\t\tdouble y = 0;\n\t\tdouble z = 0;\n\t\tdouble zoomDelta = zoom - getFlyingCamera().getZoomDepth();\n\n\t\tif (trans != null) {\n\t\t\tx = trans.getX() - getFlyingCamera().getGlobalX();\n\t\t\ty = trans.getY() - getFlyingCamera().getGlobalY();\n\t\t\tz = trans.getZ() - getFlyingCamera().getGlobalZ();\n\t\t}\n\n\t\tint interpolationSteps = Math.max((int) (Math.abs(x) / 6), (int) (Math.abs(y) / 6));\n\t\tinterpolationSteps = Math.max((int) (Math.abs(z) / 6), interpolationSteps);\n\t\tinterpolationSteps = Math.max((int) (Math.abs(el) / 5), interpolationSteps);\n\t\tinterpolationSteps = Math.max((int) (Math.abs(az) / 5), interpolationSteps);\n\t\tif (!getFlyingCamera().isZoomLocked())\n\t\t\tinterpolationSteps = Math.max((int) (Math.abs(zoomDelta) / 10), interpolationSteps);\n\n\t\tinterpolationSteps = Math.min(interpolationSteps, NUMBER_OF_INTERPOLATION_STEPS);\n\t\tfinal int steps = interpolationSteps;\n\n\t\ttry {\n\t\t\tfor (int i = 0; (i < steps) && !abortFocus; i++) {\n\t\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"\\tFocus to \\n\\t\\taz:\" + aztmp + \"\n\t\t\t\t// \\n\\t\\tel:\" + eltmp);\n\t\t\t\tdouble mx = x / steps;\n\t\t\t\tdouble my = y / steps;\n\t\t\t\tdouble mz = z / steps;\n\n\t\t\t\twaitingForCompletion = true;\n\t\t\t\tlong startTime = System.currentTimeMillis();\n\n\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\tmoveCamera(new TransformNR(0, 0, 0, new RotationNR(-el / steps, -az / steps, 0)));\n\n\t\t\t\t\tgetFlyingCamera().DrivePositionAbsolute(mx, my, mz);\n\n\t\t\t\t\tif (!getFlyingCamera().isZoomLocked())\n\t\t\t\t\t\tgetFlyingCamera().setZoomDepth(getFlyingCamera().getZoomDepth() + (zoomDelta / steps));\n\t\t\t\t\twaitingForCompletion = false;\n\t\t\t\t});\n\n\t\t\t\t// Wait for 36ms including the processing of the update\n\t\t\t\twhile (waitingForCompletion || ((System.currentTimeMillis() - startTime) < 36) && !abortFocus) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tThread.sleep(6);\n\t\t\t\t\t} catch (InterruptedException e) {\n\t\t\t\t\t\tabortFocus = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif (!abortFocus) {\n\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\tgetFlyingCamera().SetOrientation(orient);\n\t\t\t\t\tgetFlyingCamera().SetPosition(trans);\n\t\t\t\t});\n\t\t\t}\n\n\t\t} catch (Throwable t) {\n\t\t\tcom.neuronrobotics.sdk.common.Log.error(t);\n\t\t}\n\n\t\tfocusing = false;\n\t\tabortFocus = false;\n\t}\n\n\tprivate void focusInterpolate(TransformNR start, TransformNR target, int interpolationSteps, Affine interpolator) {\n\n\t\tnew Thread(() -> {\n\t\t\tint depth = 0;\n\t\t\twhile (focusing) {\n\t\t\t\ttry {\n\t\t\t\t\tThread.sleep(16);\n\t\t\t\t} catch (InterruptedException e) {\n\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(e);\n\t\t\t\t\tfocusing = false;\n\t\t\t\t}\n\t\t\t\tdouble depthScale = 1 - (double) depth / (double) interpolationSteps;\n\t\t\t\tdouble sinunsoidalScale = Math.sin(depthScale * (Math.PI / 2));\n\n\t\t\t\t// double xIncrement =target.getX()- ((start.getX() - target.getX()) *\n\t\t\t\t// depthScale) + start.getX();\n\t\t\t\tdouble difference = start.getX() - target.getX();\n\t\t\t\tdouble scaledDifference = (difference * sinunsoidalScale);\n\n\t\t\t\tdouble xIncrement = scaledDifference;\n\t\t\t\tdouble yIncrement = ((start.getY() - target.getY()) * sinunsoidalScale);\n\t\t\t\tdouble zIncrement = ((start.getZ() - target.getZ()) * sinunsoidalScale);\n\n\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\tinterpolator.setTx(xIncrement);\n\t\t\t\t\tinterpolator.setTy(yIncrement);\n\t\t\t\t\tinterpolator.setTz(zIncrement);\n\t\t\t\t});\n\t\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"Interpolation step \" + depth + \" x \"\n\t\t\t\t// + xIncrement\n\t\t\t\t// + \" y \" + yIncrement + \" z \" + zIncrement);\n\t\t\t\tif (depth >= interpolationSteps) {\n\t\t\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"Camera intrpolation done\");\n\t\t\t\t\tBowlerStudio.runLater(() -> {\n\t\t\t\t\t\tcameraGroup.getTransforms().remove(interpolator);\n\t\t\t\t\t});\n\t\t\t\t\tpreviousTarget = target.copy();\n\t\t\t\t\tpreviousTarget.setRotation(new RotationNR());\n\t\t\t\t\tfocusing = false;\n\t\t\t\t}\n\n\t\t\t\tdepth++;\n\t\t\t}\n\t\t}).start();\n\n\t}\n\n\tprivate void removeAllFocusTransforms() {\n\t\tcameraGroup.getTransforms().clear();\n\t}\n\n\tpublic HashMap<CSG, MeshView> getCsgMap() {\n\t\treturn csgMap;\n\t}\n\n\tpublic void setCsgMap(HashMap<CSG, MeshView> csgMap) {\n\t\tthis.csgMap = csgMap;\n\t}\n\n\tpublic void setSelectedCsg(File script, int lineNumber) {\n\n\t\tArrayList<CSG> objsFromScriptLine = new ArrayList<>();\n\t\t// check all visable CSGs\n\t\tfor (CSG checker : getCsgMap().keySet()) {\n\t\t\tfor (String trace : checker.getCreationEventStackTraceList()) {\n\t\t\t\tString[] traceParts = trace.split(\":\");\n\t\t\t\tif (traceParts[0].trim().toLowerCase().contains(script.getName().toLowerCase().trim())) {\n\t\t\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"Script matches\");\n\t\t\t\t\ttry {\n\t\t\t\t\t\tint num = Integer.parseInt(traceParts[1].trim());\n\n\t\t\t\t\t\tif (num == lineNumber) {\n\t\t\t\t\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"MATCH\");\n\t\t\t\t\t\t\tobjsFromScriptLine.add(checker);\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(e);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (objsFromScriptLine.size() > 0) {\n\n\t\t\tsetSelectedCsg(objsFromScriptLine);\n\t\t}\n\t}\n\n\tpublic long getLastMosueMovementTime() {\n\t\treturn lastMosueMovementTime;\n\t}\n\n\t/**\n\t * @return the defaultStlDir\n\t */\n\tpublic File getDefaultStlDir() {\n\t\tif (defaultStlDir == null)\n\t\t\tdefaultStlDir = new File(System.getProperty(\"user.home\") + \"/bowler-workspace/STL/\");\n\t\tif (!defaultStlDir.exists()) {\n\t\t\tdefaultStlDir.mkdirs();\n\t\t}\n\n\t\treturn defaultStlDir;\n\t}\n\n\t/**\n\t * @param defaultStlDir the defaultStlDir to set\n\t */\n\tpublic void setDefaultStlDir(File defaultStlDir) {\n\t\tthis.defaultStlDir = defaultStlDir;\n\t}\n\n\tpublic void focusToAffine(Affine af) {\n\t\tfocusToAffine(new TransformNR(), af);\n\t}\n\n\tpublic TransformNR getTargetNR() {\n\t\t// Auto-generated method stub\n\t\treturn poseToMove;\n\t}\n\n\t/**\n\t * The main() method is ignored in correctly deployed JavaFX application. main()\n\t * serves only as fallback in case the application can not be launched through\n\t * deployment artifacts, e.g., in IDEs with limited FX support. NetBeans ignores\n\t * main().\n\t *\n\t * @param args the command line arguments\n\t */\n\tpublic static void main(String[] args) {\n\t\tJavaFXInitializer.go();\n\t\tSystem.setProperty(\"prism.dirtyopts\", \"false\");\n\n\t\tAnchorPane view3d = new AnchorPane();\n\t\tBowlerStudio3dEngine engine = new BowlerStudio3dEngine(\"Test\");\n\t\tengine.rebuild(true);\n\t\tengine.setFocusTraversable(true);\n\n\t\tengine.addTo(view3d);\n\t\tengine.bind(view3d);\n\t\tengine.handleMouse(view3d);\n\n\n\t\tBowlerKernel.runLater(() -> {\n\t\t\tStage newStage = new Stage();\n\t\t\tScene scene = new Scene(view3d, 1024, 960, true);\n\t\t\tnewStage.setScene(scene);\n\t\t\tscene.getRoot().setStyle(\"-fx-font-family: 'Arial';\");\n\t\t\tscene.getRoot().applyCss();\n\t\t\tscene.getRoot().layout();\n\t\t\t// Add a close request handler\n\t\t\tnewStage.setOnCloseRequest(event -> {\n\t\t\t\t// Exit the JVM when the window is closed\n\t\t\t\tSystem.exit(0);\n\t\t\t});\n\t\t\tnewStage.show();\n\t\t});\n\t}\n\n\tpublic IControlsMap getControlsMap() {\n\t\tif (map == null) {\n\t\t\tmap = new IControlsMap() {\n\t\t\t\tlong lastClickedTimeLocal = 0;\n\t\t\t\tlong offset = 500;\n\n\t\t\t\tpublic boolean timeToCancel(MouseEvent event) {\n\t\t\t\t\tlong lastClickedDifference = (System.currentTimeMillis() - lastClickedTimeLocal);\n\t\t\t\t\tlong differenceIntime = System.currentTimeMillis() - lastSelectedTime;\n\t\t\t\t\tboolean ret = false;\n\t\t\t\t\tif (differenceIntime > 2000) {\n\t\t\t\t\t\t// reset only if an object is not being selected\n\t\t\t\t\t\tif (lastClickedDifference < offset) {\n\n\t\t\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.debug(\"Cancel event detected\");\n\t\t\t\t\t\t\tret = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tlastClickedTimeLocal = System.currentTimeMillis();\n\t\t\t\t\treturn ret;\n\t\t\t\t}\n\n\t\t\t\tpublic boolean isSlowMove(MouseEvent event) {\n\t\t\t\t\treturn Manipulation.isControlOrCommandPressed(event);\n\t\t\t\t}\n\n\t\t\t\tpublic boolean isRotate(MouseEvent me) {\n\t\t\t\t\tboolean shiftDown = me.isShiftDown();\n\t\t\t\t\tboolean primaryButtonDown = me.isPrimaryButtonDown();\n\n\t\t\t\t\treturn (me.isPrimaryButtonDown() && primaryButtonDown && !shiftDown);\n\t\t\t\t}\n\n\t\t\t\tpublic boolean isMove(MouseEvent me) {\n\t\t\t\t\tboolean shiftDown = me.isShiftDown();\n\t\t\t\t\tboolean primaryButtonDown = me.isPrimaryButtonDown();\n\t\t\t\t\tboolean secondaryButtonDown = me.isSecondaryButtonDown();\n\t\t\t\t\treturn (secondaryButtonDown || (primaryButtonDown && shiftDown));\n\t\t\t\t}\n\n\t\t\t\tpublic boolean isZoom(javafx.scene.input.ScrollEvent t) {\n\t\t\t\t\treturn ScrollEvent.SCROLL == t.getEventType();\n\t\t\t\t}\n\n\t\t\t};\n\t\t}\n\t\treturn map;\n\t}\n\n\tpublic void setControlsMap(IControlsMap map) {\n\t\tthis.map = map;\n\t}\n\n\tpublic void setZoom(int i) {\n\t\tflyingCamera.setZoomDepth(i);\n\t}\n\n\tpublic void setMouseScale(double mouseScale) {\n\t\tthis.mouseScale = mouseScale;\n\t}\n\n\tpublic void lockZoom() {\n\t\tgetFlyingCamera().lockZoom();\n\t}\n\n\t@Override\n\tpublic void onChange(VirtualCameraMobileBase camera) {\n\t\tfor (ICameraChangeListener c : listeners) {\n\t\t\ttry {\n\t\t\t\tc.onChange(camera);\n\t\t\t} catch (Throwable t) {\n\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(t);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void lockMove() {\n\t\tmove = false;\n\t\tgetFlyingCamera().lockMove();\n\t}\n\n\tpublic void disableControls() {\n\t\t// Auto-generated method stub\n\t\tdisabeControl = true;\n\t}\n\n\tpublic void placeGrid(TransformNR workplane) {\n\t\tBowlerKernel.runLater(() -> {\n\t\t\tTransformFactory.nrToAffine(workplane, gridPlacementAffine);\n\t\t});\n\t}\n\n\t@Override\n\tpublic void setAllCSG(Collection<CSG> toAdd, File source) {\n\t\tclearUserNode();\n\t\taddCSG(toAdd, source);\n\t}\n\n\t@Override\n\tpublic void addCSG(Collection<CSG> toAdd, File source) {\n\t\tfor (CSG c : toAdd)\n\t\t\taddObject(c, source);\n\t}\n\n\t@Override\n\tpublic void highlightException(File fileEngineRunByName, Throwable ex) {\n\n\t}\n\n\t@Override\n\tpublic Set<CSG> getVisibleCSGs() {\n\t\treturn getCsgMap().keySet();\n\t}\n\n\t@Override\n\tpublic void setSelectedCsg(Collection<CSG> selectedCsg) {\n\t\tfor (CSG c : selectedCsg)\n\t\t\tselectObjectsSourceFile(c);\n\t}\n\n\tpublic Affine getRulerOffset() {\n\t\treturn rulerOffset;\n\t}\n\n\tpublic void setRulerOffset(Affine rulerOffset) {\n\t\tthis.rulerOffset = rulerOffset;\n\t}\n\n\tpublic Affine getRulerInWorkplaneOffset() {\n\t\treturn rulerInWorkplaneOffset;\n\t}\n\n\tpublic void setRulerInWorkplaneOffset(Affine rulerInWorkplaneOffset) {\n\t\tthis.rulerInWorkplaneOffset = rulerInWorkplaneOffset;\n\t}\n\n\tpublic Group getRulerGroup() {\n\t\treturn rulerGroup;\n\t}\n\n\tpublic void setFocusTraversable(boolean b) {\n\t\t//\t\tif (!b)\n\t\t//\t\t\tLog.error(new Exception(\"Make Scene Non Traversable \" + b));\n\t\tgetSubScene().setFocusTraversable(b);\n\t}\n\n\tpublic void addTo(AnchorPane view3d) {\n\t\tview3d.getChildren().add(getSubScene());\n\t\t// anchor it\n\t\tAnchorPane.setTopAnchor(getSubScene(), 0.0);\n\t\tAnchorPane.setRightAnchor(getSubScene(), 0.0);\n\t\tAnchorPane.setLeftAnchor(getSubScene(), 0.0);\n\t\tAnchorPane.setBottomAnchor(getSubScene(), 0.0);\n\t}\n\n\tpublic Stage getWindow() {\n\t\treturn (Stage) getSubScene().getScene().getWindow();\n\t}\n\n\tpublic void setOnDragOver(EventHandler<? super DragEvent> object) {\n\t\tgetSubScene().setOnDragOver(object);\n\t}\n\n\tpublic void setOnDragDropped(EventHandler<? super DragEvent> object) {\n\t\tgetSubScene().setOnDragDropped(object);\n\t}\n\n\tpublic void setHeight(double doubleValue) {\n\t\tgetSubScene().setHeight(doubleValue);\n\t}\n\n\tpublic void setWidth(double doubleValue) {\n\t\tgetSubScene().setWidth(doubleValue);\n\t}\n\n\tpublic boolean isSubScene(Object gestureSource) {\n\t\treturn gestureSource == getSubScene();\n\t}\n\n\tpublic double getWidth() {\n\t\treturn getSubScene().getWidth();\n\t}\n\n\tpublic double getHeight() {\n\t\treturn getSubScene().getHeight();\n\t}\n\n\tpublic void bind(AnchorPane viewContainer) {\n\t\tgetSubScene().widthProperty().bind(viewContainer.widthProperty());\n\t\tgetSubScene().heightProperty().bind(viewContainer.heightProperty());\n\t}\n\n\tpublic void addMouseFilter(EventType<MouseEvent> mouseDragged, EventHandler<? super MouseEvent> eventFilter) {\n\t\tgetSubScene().addEventFilter(mouseDragged, eventFilter);\n\t}\n\n\tpublic void addKeyFilter(EventType<KeyEvent> mouseDragged, EventHandler<? super KeyEvent> eventFilter) {\n\t\tgetSubScene().addEventFilter(mouseDragged, eventFilter);\n\t}\n\n\tpublic void requestFocus() {\n\t\tgetSubScene().requestFocus();\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/threed/CreaturelLabController.java",
    "content": "package com.neuronrobotics.bowlerstudio.threed;\n\n/**\n * Sample Skeleton for \"CreatureLab.fxml\" Controller Class\n * You can copy and paste this code into your favorite IDE\n **/\n\nimport java.net.URL;\nimport java.util.ResourceBundle;\nimport javafx.fxml.FXML;\nimport javafx.scene.control.ScrollPane;\nimport javafx.scene.layout.AnchorPane;\n\npublic class CreaturelLabController {\n\n\t@FXML // ResourceBundle that was given to the FXMLLoader\n\tprivate ResourceBundle resources;\n\n\t@FXML // URL location of the FXML file that was given to the FXMLLoader\n\tprivate URL location;\n\n\t@FXML // fx:id=\"CadControlsAnchor\"\n\tprivate AnchorPane CadControlsAnchor; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"DriveControlsAnchor\"\n\tprivate AnchorPane DriveControlsAnchor; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"TempControlsAnchor\"\n\tprivate AnchorPane TempControlsAnchor; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"jfx3dControls\"\n\tprivate AnchorPane jfx3dControls; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"overlayScrollPanel\"\n\tprivate ScrollPane overlayScrollPanel; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"viewContainer\"\n\tprivate AnchorPane viewContainer; // Value injected by FXMLLoader\n\n\t@FXML // This method is called by the FXMLLoader when initialization is complete\n\tvoid initialize() {\n\t\tassert CadControlsAnchor != null\n\t\t\t\t: \"fx:id=\\\"CadControlsAnchor\\\" was not injected: check your FXML file 'CreatureLab.fxml'.\";\n\t\tassert DriveControlsAnchor != null\n\t\t\t\t: \"fx:id=\\\"DriveControlsAnchor\\\" was not injected: check your FXML file 'CreatureLab.fxml'.\";\n\t\tassert TempControlsAnchor != null\n\t\t\t\t: \"fx:id=\\\"TempControlsAnchor\\\" was not injected: check your FXML file 'CreatureLab.fxml'.\";\n\t\tassert jfx3dControls != null\n\t\t\t\t: \"fx:id=\\\"jfx3dControls\\\" was not injected: check your FXML file 'CreatureLab.fxml'.\";\n\t\tassert overlayScrollPanel != null\n\t\t\t\t: \"fx:id=\\\"overlayScrollPanel\\\" was not injected: check your FXML file 'CreatureLab.fxml'.\";\n\t\tassert viewContainer != null\n\t\t\t\t: \"fx:id=\\\"viewContainer\\\" was not injected: check your FXML file 'CreatureLab.fxml'.\";\n\n\t\t// Initialize your logic here: all @FXML variables will have been injected\n\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/threed/ICameraChangeListener.java",
    "content": "package com.neuronrobotics.bowlerstudio.threed;\n\npublic interface ICameraChangeListener {\n\tvoid onChange(VirtualCameraMobileBase camera);\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/threed/IControlsMap.java",
    "content": "package com.neuronrobotics.bowlerstudio.threed;\n\npublic interface IControlsMap {\n\tpublic boolean timeToCancel(javafx.scene.input.MouseEvent event);\n\n\tpublic boolean isSlowMove(javafx.scene.input.MouseEvent event);\n\n\tpublic boolean isMove(javafx.scene.input.MouseEvent ev);\n\n\tpublic boolean isRotate(javafx.scene.input.MouseEvent me);\n\n\tpublic boolean isZoom(javafx.scene.input.ScrollEvent t);\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/threed/Jbullet.java",
    "content": "package com.neuronrobotics.bowlerstudio.threed;\n\nimport javax.vecmath.Matrix4f;\nimport javax.vecmath.Quat4f;\nimport javax.vecmath.Vector3f;\n\nimport com.bulletphysics.collision.broadphase.BroadphaseInterface;\nimport com.bulletphysics.collision.broadphase.DbvtBroadphase;\nimport com.bulletphysics.collision.dispatch.CollisionDispatcher;\nimport com.bulletphysics.collision.dispatch.DefaultCollisionConfiguration;\nimport com.bulletphysics.collision.shapes.CollisionShape;\nimport com.bulletphysics.collision.shapes.SphereShape;\nimport com.bulletphysics.collision.shapes.StaticPlaneShape;\nimport com.bulletphysics.dynamics.DiscreteDynamicsWorld;\nimport com.bulletphysics.dynamics.RigidBody;\nimport com.bulletphysics.dynamics.RigidBodyConstructionInfo;\nimport com.bulletphysics.dynamics.constraintsolver.SequentialImpulseConstraintSolver;\nimport com.bulletphysics.linearmath.DefaultMotionState;\nimport com.bulletphysics.linearmath.Transform;\n\npublic class Jbullet {\n\n\tpublic static void main(String[] args) {\n\n\t\tBroadphaseInterface broadphase = new DbvtBroadphase();\n\t\tDefaultCollisionConfiguration collisionConfiguration = new DefaultCollisionConfiguration();\n\t\tCollisionDispatcher dispatcher = new CollisionDispatcher(collisionConfiguration);\n\n\t\tSequentialImpulseConstraintSolver solver = new SequentialImpulseConstraintSolver();\n\n\t\tDiscreteDynamicsWorld dynamicsWorld = new DiscreteDynamicsWorld(dispatcher, broadphase, solver,\n\t\t\t\tcollisionConfiguration);\n\n\t\t// set the gravity of our world\n\t\tdynamicsWorld.setGravity(new Vector3f(0, -10, 0));\n\n\t\t// setup our collision shapes\n\t\tCollisionShape groundShape = new StaticPlaneShape(new Vector3f(0, 1, 0), 1);\n\t\tCollisionShape fallShape = new SphereShape(1);\n\n\t\t// setup the motion state\n\t\tDefaultMotionState groundMotionState = new DefaultMotionState(\n\t\t\t\tnew Transform(new Matrix4f(new Quat4f(0, 0, 0, 1), new Vector3f(0, -1, 0), 1.0f)));\n\n\t\tRigidBodyConstructionInfo groundRigidBodyCI = new RigidBodyConstructionInfo(0, groundMotionState, groundShape,\n\t\t\t\tnew Vector3f(0, 0, 0));\n\t\tRigidBody groundRigidBody = new RigidBody(groundRigidBodyCI);\n\n\t\tdynamicsWorld.addRigidBody(groundRigidBody); // add our ground to the\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t// dynamic world..\n\n\t\t// setup the motion state for the ball\n\t\tDefaultMotionState fallMotionState = new DefaultMotionState(\n\t\t\t\tnew Transform(new Matrix4f(new Quat4f(0, 0, 0, 1), new Vector3f(0, 100, 0), 1.0f)));\n\n\t\t// This we're going to give mass so it responds to gravity\n\t\tint mass = 1;\n\n\t\tVector3f fallInertia = new Vector3f(0, 0, 0);\n\t\tfallShape.calculateLocalInertia(mass, fallInertia);\n\n\t\tRigidBodyConstructionInfo fallRigidBodyCI = new RigidBodyConstructionInfo(mass, fallMotionState, fallShape,\n\t\t\t\tfallInertia);\n\t\tRigidBody fallRigidBody = new RigidBody(fallRigidBodyCI);\n\n\t\t// now we add it to our physics simulation\n\t\tdynamicsWorld.addRigidBody(fallRigidBody);\n\n\t\tfor (int i = 0; i < 300; i++) {\n\t\t\tdynamicsWorld.stepSimulation(1 / 60.f, 10);\n\n\t\t\tTransform trans = new Transform();\n\t\t\tfallRigidBody.getMotionState().getWorldTransform(trans);\n\n\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"sphere height: \" + trans.origin.y);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/threed/MakeRuler.java",
    "content": "package com.neuronrobotics.bowlerstudio.threed;\n\nimport java.util.HashMap;\n\nimport com.neuronrobotics.bowlerstudio.BowlerKernel;\n\nimport eu.mihosoft.vrl.v3d.CSG;\nimport javafx.scene.Group;\nimport javafx.scene.shape.CullFace;\nimport javafx.scene.shape.TriangleMesh;\nimport javafx.scene.shape.MeshView;\nimport javafx.scene.shape.MeshView;\nimport javafx.scene.shape.TriangleMesh;\nimport javafx.scene.paint.PhongMaterial;\nimport javafx.scene.paint.Color;\nimport javafx.scene.transform.Affine;\n\npublic class MakeRuler {\n\t// SVG paths for numbers 0-9\n\tprivate static HashMap<Integer, CSG> numbers = new HashMap<>();\n\n\tpublic static Group createRuler(boolean flipNumber) {\n\t\treturn createRuler(flipNumber, 30); // 30 cm default length\n\t}\n\n\tpublic static Group createRuler(boolean flipNumber, int rulerLengthCM) {\n\t\tdouble baseWidth = 0.15; // The width of the ruler base in mm\n\t\tdouble tickLength = 8; // The length of a tick in mm\n\t\tdouble tickWidth = 0.25; // The width of a tick in mm\n\t\tGroup ruler = new Group();\n\n\t\tnew Thread(() -> {\n\t\t\t// Create base mesh for the ruler line\n\t\t\tTriangleMesh baseMesh = createRectangleMesh(rulerLengthCM * 10 + tickWidth, baseWidth);\n\t\t\tMeshView baseView = new MeshView(baseMesh);\n\t\t\tbaseView.setMouseTransparent(true);\n\t\t\tbaseView.setCullFace(CullFace.NONE);\n\t\t\tPhongMaterial phongMaterial = new PhongMaterial(Color.BLACK);\n\n\t\t\tbaseView.setMaterial(phongMaterial);\n\n\t\t\t// Position the base line in the middle\n\t\t\tAffine baseTransform = new Affine();\n\t\t\tbaseTransform.setTx(-tickWidth / 2.0);\n\t\t\tbaseTransform.setTy(-baseWidth / 2.0);\n\t\t\tbaseView.getTransforms().add(baseTransform);\n\t\t\tBowlerKernel.runLater(() -> ruler.getChildren().add(baseView));\n\n\t\t\t// Draw tick marks and labels\n\t\t\tfor (int i = 0; i <= rulerLengthCM * 10; i++) {\n\t\t\t\tTriangleMesh tickMesh;\n\n\t\t\t\t// Determine tick type based on position\n\t\t\t\tif (i % 10 == 0) {\n\t\t\t\t\t// Centimeter tick marks\n\t\t\t\t\ttickMesh = createRectangleMesh(tickWidth, tickLength);\n\t\t\t\t\t// Always draw a number at the end of the ruler\n\t\t\t\t\tif ((i % 20 == 0) || (i == (int) (rulerLengthCM * 10))) {\n\t\t\t\t\t\t// Add centimeter number using SVGPath\n\t\t\t\t\t\tint number = i / 10;\n\t\t\t\t\t\t// Prevent double \"0\" at origin \"(flipNumber || (i != 0))\"\n\n\t\t\t\t\t\tif ((numbers.get(number) == null) || (flipNumber || (i != 0))) {\n\t\t\t\t\t\t\tnumbers.put(number, CSG.textToSize(\"\" + i, 4, 6, 0.1).movey(tickLength + 0.5)\n\t\t\t\t\t\t\t\t\t.moveToCenterX().setColor(Color.BLACK));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tCSG csg = numbers.get(number);\n\t\t\t\t\t\tif (csg != null) {\n\t\t\t\t\t\t\tCSG movey = flipNumber ? csg.roty(180) : csg;\n\t\t\t\t\t\t\tint index = i;\n\t\t\t\t\t\t\tif (movey != null)\n\t\t\t\t\t\t\t\tBowlerKernel.runLater(() -> {\n\t\t\t\t\t\t\t\t\tMeshView numberGroup = movey.newMesh();\n\t\t\t\t\t\t\t\t\tnumberGroup.setMouseTransparent(true);\n\t\t\t\t\t\t\t\t\t// Scale and position the number\n\t\t\t\t\t\t\t\t\tAffine numberTransform = new Affine();\n\t\t\t\t\t\t\t\t\tnumberTransform.appendTranslation(index, 0);\n\t\t\t\t\t\t\t\t\tnumberGroup.getTransforms().add(numberTransform);\n\t\t\t\t\t\t\t\t\truler.getChildren().add(numberGroup);\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else if (i % 5 == 0) {\n\t\t\t\t\t// 5mm tick marks\n\t\t\t\t\ttickMesh = createRectangleMesh(tickWidth, tickLength / 2);\n\t\t\t\t} else {\n\t\t\t\t\t// 1mm tick marks\n\t\t\t\t\ttickMesh = createRectangleMesh(tickWidth, tickLength / 4);\n\t\t\t\t}\n\n\t\t\t\t// Create and position tick mark\n\t\t\t\tMeshView tickView = new MeshView(tickMesh);\n\t\t\t\ttickView.setMouseTransparent(true);\n\t\t\t\ttickView.setMaterial(phongMaterial);\n\n\t\t\t\t// Use Affine transform for tick positioning\n\t\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"Tick for \" + i);\n\t\t\t\tAffine tickTransform = new Affine();\n\t\t\t\ttickTransform.setTx(i - tickWidth / 2);\n\t\t\t\ttickView.getTransforms().add(tickTransform);\n\t\t\t\ttickView.setCullFace(CullFace.NONE);\n\t\t\t\tBowlerKernel.runLater(() -> ruler.getChildren().add(tickView));\n\t\t\t}\n\t\t}).start();\n\t\treturn ruler;\n\t}\n\n\tprivate static TriangleMesh createRectangleMesh(double width, double tickLength) {\n\t\tfloat[] points = {0, 0, 0, // point 0\n\t\t\t\t(float) width, 0, 0, // point 1\n\t\t\t\t(float) width, (float) tickLength, 0, // point 2\n\t\t\t\t0, (float) tickLength, 0 // point 3\n\t\t};\n\n\t\tfloat[] texCoords = {0, 0, 1, 0, 1, 1, 0, 1};\n\n\t\tint[] faces = {0, 0, 1, 1, 2, 2, // First triangle\n\t\t\t\t0, 0, 2, 2, 3, 3 // Second triangle\n\t\t};\n\n\t\tTriangleMesh mesh = new TriangleMesh();\n\t\tmesh.getPoints().addAll(points);\n\t\tmesh.getTexCoords().addAll(texCoords);\n\t\tmesh.getFaces().addAll(faces);\n\n\t\treturn mesh;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/threed/VirtualCameraMobileBase.java",
    "content": "package com.neuronrobotics.bowlerstudio.threed;\n\nimport java.util.ArrayList;\n\nimport com.neuronrobotics.bowlerstudio.BowlerStudio;\nimport com.neuronrobotics.bowlerstudio.physics.TransformFactory;\nimport com.neuronrobotics.sdk.addons.kinematics.math.RotationNR;\nimport com.neuronrobotics.sdk.addons.kinematics.math.TransformNR;\n\nimport javafx.scene.Group;\nimport javafx.scene.PerspectiveCamera;\nimport javafx.scene.transform.Affine;\n\npublic class VirtualCameraMobileBase {\n\tpublic static final TransformNR CameraGlobalOffset = new TransformNR(0, 0, 0, new RotationNR(180, 0, 0));\n\tprivate TransformNR myGlobal = new TransformNR();\n\t// double azOffset = 0;\n\t// double elOffset = 0;\n\t// double tlOffset = 0;\n\tprivate static final int DEFAULT_ZOOM_DEPTH = -1500;\n\tprivate PerspectiveCamera camera;\n\tprivate Group hand;\n\tprivate final Group cameraFrame = new Group();\n\n\tprivate double zoomDepth = getDefaultZoomDepth();\n\tprivate Affine zoomAffine = new Affine();\n\tprivate static final Affine offset = new Affine();\n\tprivate Group manipulationFrame;\n\tlong timeSinceLastUpdate = System.currentTimeMillis() - 17;\n\tboolean error = false;\n\tprivate boolean move = true;\n\n\tprivate Affine camerUserPerspective = new Affine();\n\tprivate ArrayList<VirtualCameraMobileBase> flyingCamera = new ArrayList<>();\n\tprivate boolean zoomlock;\n\tprivate ArrayList<ICameraChangeListener> listeners = new ArrayList<>();\n\tprivate String name;\n\n\tpublic VirtualCameraMobileBase(PerspectiveCamera camera, Group hand, ICameraChangeListener lis, String name) {\n\t\tthis.hand = hand;\n\t\tthis.name = name;\n\t\tthis.setCamera(camera);\n\t\taddListener(lis);\n\t\t// com.neuronrobotics.sdk.common.Log.error(\"Setting camera frame transform\");\n\n\t\tmanipulationFrame = new Group();\n\t\tcamera.getTransforms().add(zoomAffine);\n\t\tBowlerStudio.runLater(() -> TransformFactory.nrToAffine(CameraGlobalOffset, offset));\n\t\tcameraFrame.getTransforms().add(getOffset());\n\t\tmanipulationFrame.getChildren().addAll(camera, hand);\n\t\tmanipulationFrame.getTransforms().add(camerUserPerspective);\n\t\tcameraFrame.getChildren().add(manipulationFrame);\n\t\tsetZoomDepth(DEFAULT_ZOOM_DEPTH);\n\t}\n\n\tpublic VirtualCameraMobileBase addListener(ICameraChangeListener l) {\n\t\tif (!listeners.contains(l))\n\t\t\tlisteners.add(l);\n\t\treturn this;\n\t}\n\n\tpublic VirtualCameraMobileBase removeListener(ICameraChangeListener l) {\n\t\tif (listeners.contains(l))\n\t\t\tlisteners.remove(l);\n\t\treturn this;\n\t}\n\n\tpublic void fireUpdate() {\n\t\tsynchronizePositionWithOtherFlyingCamera(myGlobal);\n\t\tfor (ICameraChangeListener c : listeners) {\n\t\t\ttry {\n\t\t\t\tc.onChange(this);\n\t\t\t} catch (Throwable t) {\n\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(t);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void setGlobalToFiducialTransform(TransformNR defautcameraView) {\n\t\tmyGlobal = defautcameraView;\n\t\tupdatePositions();\n\t\tfireUpdate();\n\t}\n\n\tpublic void updatePositions() {\n\n\t\tif (System.currentTimeMillis() - timeSinceLastUpdate > 16) {\n\t\t\ttimeSinceLastUpdate = System.currentTimeMillis();\n\t\t\terror = false;\n\t\t\tTransformFactory.nrToAffine(myGlobal, camerUserPerspective);\n\t\t} else {\n\t\t\t// too soon\n\t\t\terror = true;\n\t\t}\n\t}\n\n\tpublic TransformNR getFiducialToGlobalTransform() {\n\t\treturn myGlobal;\n\t}\n\n\tpublic void DrivePositionAbsolute(double x, double y, double z) {\n\t\tTransformNR global = getFiducialToGlobalTransform().copy().translateX(x).translateY(y).translateZ(z);\n\t\tsetGlobalToFiducialTransform(global);\n\t}\n\n\tpublic void DriveArc(TransformNR newPose) {\n\t\tTransformNR pureTrans = new TransformNR();\n\t\tif (move) {\n\t\t\tpureTrans.setX(newPose.getX());\n\t\t\tpureTrans.setY(newPose.getY());\n\t\t\tpureTrans.setZ(newPose.getZ());\n\t\t}\n\n\t\tTransformNR global = getFiducialToGlobalTransform().times(pureTrans);\n\t\tdouble rotationTiltRadians = newPose.getRotation().getRotationTiltRadians();\n\t\tdouble rotationAzimuthRadians = newPose.getRotation().getRotationAzimuthRadians();\n\t\tdouble rotationElevationRadians = newPose.getRotation().getRotationElevationRadians();\n\n\t\tglobal.setRotation(new RotationNR(\n\t\t\t\t(Math.toDegrees(rotationTiltRadians + global.getRotation().getRotationTiltRadians()) % 360),\n\t\t\t\t(Math.toDegrees(rotationAzimuthRadians + global.getRotation().getRotationAzimuthRadians()) % 360),\n\t\t\t\tMath.toDegrees(rotationElevationRadians + global.getRotation().getRotationElevationRadians())));\n\n\t\t// global.getRotation().setStorage(nr);\n\t\t// com.neuronrobotics.sdk.common.Log.error(\"Camera tilt=\"+global);\n\t\t// New target calculated appliaed to global offset\n\t\tsetGlobalToFiducialTransform(global);\n\t}\n\n\tpublic void SetPosition(TransformNR newPose) {\n\t\tif ((newPose == null) || !move)\n\t\t\treturn;\n\t\tsetGlobalToFiducialTransform(newPose.copy().setRotation(getFiducialToGlobalTransform().getRotation()));\n\t}\n\n\tpublic void SetOrientation(TransformNR newPose) {\n\t\tif (newPose == null)\n\t\t\treturn;\n\t\t// newPose = CameraGlobalOffset.times(newPose);\n\t\t// TransformNR pureTrans = new TransformNR();\n\t\t//\n\t\t// // Auto-generated method stub\n\t\t// pureTrans.setX(newPose.getX());\n\t\t// pureTrans.setY(newPose.getY());\n\t\t// pureTrans.setZ(newPose.getZ());\n\n\t\tTransformNR global = getFiducialToGlobalTransform().copy();\n\t\t// use the camera global fraame elevation\n\t\tdouble rotationElevationDegrees = -newPose.getRotation().getRotationElevationDegrees() - 90;\n\t\tdouble azimuthDegrees = 90 - newPose.getRotation().getRotationAzimuthDegrees();\n\t\tdouble globalElevationDegrees = global.getRotation().getRotationElevationDegrees();\n\n\t\t// Apply globals to the internal camer frame\n\t\tglobal.setRotation(new RotationNR(rotationElevationDegrees, azimuthDegrees, globalElevationDegrees));\n\t\tsetGlobalToFiducialTransform(global);\n\t}\n\n\tpublic double getPanAngle() {\n\t\treturn Math.toDegrees(getFiducialToGlobalTransform().getRotation().getRotationAzimuthRadians());\n\t}\n\n\tpublic double getTiltAngle() {\n\t\treturn Math.toDegrees(getFiducialToGlobalTransform().getRotation().getRotationTiltRadians());\n\t}\n\n\tpublic double getGlobalX() {\n\t\treturn getFiducialToGlobalTransform().getX();\n\t}\n\n\tpublic double getGlobalY() {\n\t\treturn getFiducialToGlobalTransform().getY();\n\t}\n\n\tpublic double getGlobalZ() {\n\t\treturn getFiducialToGlobalTransform().getZ();\n\t}\n\n\tpublic TransformNR getCamerFrame() {\n\t\tTransformNR offset = TransformFactory.affineToNr(getOffset());\n\t\tTransformNR fiducialToGlobalTransform = getFiducialToGlobalTransform();\n\t\treturn offset.times(fiducialToGlobalTransform);\n\t}\n\n\tpublic PerspectiveCamera getCamera() {\n\t\treturn camera;\n\t}\n\n\tpublic Group getCameraGroup() {\n\t\treturn getCameraFrame();\n\t}\n\n\tprivate void setCamera(PerspectiveCamera camera) {\n\t\tthis.camera = camera;\n\t}\n\n\tpublic Group getCameraFrame() {\n\t\treturn cameraFrame;\n\t}\n\n\tpublic double getZoomDepth() {\n\t\treturn zoomDepth;\n\t}\n\n\tpublic void setZoomDepth(double zoomDepth) {\n\t\tif (zoomlock)\n\t\t\tthrow new RuntimeException(\"Zoom can not be set when locked\");\n\n\t\t// Clamp zoomDepth between -9000 and -2\n\t\tzoomDepth = Math.max(-9000, Math.min(-2, zoomDepth));\n\n\t\tthis.zoomDepth = zoomDepth;\n\n\t\t// Dynamically adjust setFarClip to reduce Z-fighting\n\t\tcamera.setFarClip(Math.max(6000, -zoomDepth * 2));\n\n\t\tzoomAffine.setTz(zoomDepth);\n\n\t\tfireUpdate();\n\t}\n\n\tpublic static int getDefaultZoomDepth() {\n\t\treturn DEFAULT_ZOOM_DEPTH;\n\t}\n\n\tpublic static Affine getOffset() {\n\t\treturn offset;\n\t}\n\n\tpublic void bind(VirtualCameraMobileBase f) {\n\t\tif (flyingCamera.contains(f))\n\t\t\treturn;\n\n\t\tthis.flyingCamera.add(f);\n\t}\n\n\tprivate void synchronizePositionWithOtherFlyingCamera(TransformNR n) {\n\n\t\tfor (VirtualCameraMobileBase cam : flyingCamera) {\n\t\t\tRotationNR rotation = getFiducialToGlobalTransform().getRotation();\n\t\t\tif (!zoomlock && !cam.zoomlock && ((int) cam.getZoomDepth()) != ((int) getZoomDepth())) {\n\t\t\t\t// com.neuronrobotics.sdk.common.Log.error(name+\" Sync zoom to \"+cam.name);\n\t\t\t\tcam.setZoomDepth(zoomDepth);\n\t\t\t}\n\n\t\t\tif (rotation == cam.myGlobal.getRotation())\n\t\t\t\tcontinue;\n\n\t\t\t// com.neuronrobotics.sdk.common.Log.error(name+\" pusing update to \"+cam.name);\n\t\t\tif (!cam.move || !move) {\n\t\t\t\tTransformNR newGlob = cam.getFiducialToGlobalTransform().copy().setRotation(rotation);\n\t\t\t\tcam.setGlobalToFiducialTransform(newGlob);\n\t\t\t} else\n\t\t\t\tcam.setGlobalToFiducialTransform(n.copy().setRotation(rotation));\n\n\t\t}\n\t}\n\n\tpublic void lockZoom() {\n\t\tzoomlock = true;\n\t}\n\n\tpublic boolean isZoomLocked() {\n\t\treturn zoomlock;\n\t}\n\n\tpublic void lockMove() {\n\t\tmove = false;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/threed/Xform.java",
    "content": "/*\n * Copyright (c) 2011, 2013 Oracle and/or its affiliates.\n * All rights reserved. Use is subject to license terms.\n *\n * This file is available and licensed under the following license:\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *\n *  - Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  - Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the distribution.\n *  - Neither the name of Oracle nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\npackage com.neuronrobotics.bowlerstudio.threed;\n\nimport javafx.scene.Group;\nimport javafx.scene.transform.Rotate;\nimport javafx.scene.transform.Scale;\nimport javafx.scene.transform.Translate;\n\n//  Auto-generated Javadoc\n/**\n * The Class Xform.\n */\npublic class Xform extends Group {\n\n\t/**\n\t * The Enum RotateOrder.\n\t */\n\tpublic enum RotateOrder {\n\n\t\t/** The xyz. */\n\t\tXYZ,\n\t\t/** The xzy. */\n\t\tXZY,\n\t\t/** The yxz. */\n\t\tYXZ,\n\t\t/** The yzx. */\n\t\tYZX,\n\t\t/** The zxy. */\n\t\tZXY,\n\t\t/** The zyx. */\n\t\tZYX\n\t}\n\n\t/** The t. */\n\tpublic Translate t = new Translate();\n\n\t/** The p. */\n\tpublic Translate p = new Translate();\n\n\t/** The ip. */\n\tpublic Translate ip = new Translate();\n\n\t/** The rx. */\n\tpublic Rotate rx = new Rotate();\n\t{\n\t\trx.setAxis(Rotate.X_AXIS);\n\t}\n\n\t/** The ry. */\n\tpublic Rotate ry = new Rotate();\n\t{\n\t\try.setAxis(Rotate.Y_AXIS);\n\t}\n\n\t/** The rz. */\n\tpublic Rotate rz = new Rotate();\n\t{\n\t\trz.setAxis(Rotate.Z_AXIS);\n\t}\n\n\t/** The s. */\n\tpublic Scale s = new Scale();\n\n\t/**\n\t * Instantiates a new xform.\n\t */\n\tpublic Xform() {\n\t\tsuper();\n\t\tgetTransforms().addAll(t, rz, ry, rx, s);\n\t}\n\n\t/**\n\t * Instantiates a new xform.\n\t *\n\t * @param rotateOrder\n\t *            the rotate order\n\t */\n\tpublic Xform(RotateOrder rotateOrder) {\n\t\tsuper();\n\t\t// choose the order of rotations based on the rotateOrder\n\t\tswitch (rotateOrder) {\n\t\t\tcase XYZ :\n\t\t\t\tgetTransforms().addAll(t, p, rz, ry, rx, s, ip);\n\t\t\t\tbreak;\n\t\t\tcase XZY :\n\t\t\t\tgetTransforms().addAll(t, p, ry, rz, rx, s, ip);\n\t\t\t\tbreak;\n\t\t\tcase YXZ :\n\t\t\t\tgetTransforms().addAll(t, p, rz, rx, ry, s, ip);\n\t\t\t\tbreak;\n\t\t\tcase YZX :\n\t\t\t\tgetTransforms().addAll(t, p, rx, rz, ry, s, ip); // For Camera\n\t\t\t\tbreak;\n\t\t\tcase ZXY :\n\t\t\t\tgetTransforms().addAll(t, p, ry, rx, rz, s, ip);\n\t\t\t\tbreak;\n\t\t\tcase ZYX :\n\t\t\t\tgetTransforms().addAll(t, p, rx, ry, rz, s, ip);\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t/**\n\t * Sets the translate.\n\t *\n\t * @param x\n\t *            the x\n\t * @param y\n\t *            the y\n\t * @param z\n\t *            the z\n\t */\n\tpublic void setTranslate(double x, double y, double z) {\n\t\tt.setX(x);\n\t\tt.setY(y);\n\t\tt.setZ(z);\n\t}\n\n\t/**\n\t * Sets the translate.\n\t *\n\t * @param x\n\t *            the x\n\t * @param y\n\t *            the y\n\t */\n\tpublic void setTranslate(double x, double y) {\n\t\tt.setX(x);\n\t\tt.setY(y);\n\t}\n\n\t// Cannot override these methods as they are final:\n\t// public void setTranslateX(double x) { t.setX(x); }\n\t// public void setTranslateY(double y) { t.setY(y); }\n\t// public void setTranslateZ(double z) { t.setZ(z); }\n\t/**\n\t * Sets the tx.\n\t *\n\t * @param x\n\t *            the new tx\n\t */\n\t// Use these methods instead:\n\tpublic void setTx(double x) {\n\t\tt.setX(x);\n\t}\n\n\t/**\n\t * Sets the ty.\n\t *\n\t * @param y\n\t *            the new ty\n\t */\n\tpublic void setTy(double y) {\n\t\tt.setY(y);\n\t}\n\n\t/**\n\t * Sets the tz.\n\t *\n\t * @param z\n\t *            the new tz\n\t */\n\tpublic void setTz(double z) {\n\t\tt.setZ(z);\n\t}\n\n\t/**\n\t * Sets the rotate.\n\t *\n\t * @param x\n\t *            the x\n\t * @param y\n\t *            the y\n\t * @param z\n\t *            the z\n\t */\n\tpublic void setRotate(double x, double y, double z) {\n\t\trx.setAngle(x);\n\t\try.setAngle(y);\n\t\trz.setAngle(z);\n\t}\n\n\t/**\n\t * Sets the rotate x.\n\t *\n\t * @param x\n\t *            the new rotate x\n\t */\n\tpublic void setRotateX(double x) {\n\t\trx.setAngle(x);\n\t}\n\n\t/**\n\t * Sets the rotate y.\n\t *\n\t * @param y\n\t *            the new rotate y\n\t */\n\tpublic void setRotateY(double y) {\n\t\try.setAngle(y);\n\t}\n\n\t/**\n\t * Sets the rotate z.\n\t *\n\t * @param z\n\t *            the new rotate z\n\t */\n\tpublic void setRotateZ(double z) {\n\t\trz.setAngle(z);\n\t}\n\n\t/**\n\t * Sets the rx.\n\t *\n\t * @param x\n\t *            the new rx\n\t */\n\tpublic void setRx(double x) {\n\t\trx.setAngle(x);\n\t}\n\n\t/**\n\t * Sets the ry.\n\t *\n\t * @param y\n\t *            the new ry\n\t */\n\tpublic void setRy(double y) {\n\t\try.setAngle(y);\n\t}\n\n\t/**\n\t * Sets the rz.\n\t *\n\t * @param z\n\t *            the new rz\n\t */\n\tpublic void setRz(double z) {\n\t\trz.setAngle(z);\n\t}\n\n\t/**\n\t * Sets the scale.\n\t *\n\t * @param scaleFactor\n\t *            the new scale\n\t */\n\tpublic void setScale(double scaleFactor) {\n\t\ts.setX(scaleFactor);\n\t\ts.setY(scaleFactor);\n\t\ts.setZ(scaleFactor);\n\t}\n\n\t/**\n\t * Sets the scale.\n\t *\n\t * @param x\n\t *            the x\n\t * @param y\n\t *            the y\n\t * @param z\n\t *            the z\n\t */\n\tpublic void setScale(double x, double y, double z) {\n\t\ts.setX(x);\n\t\ts.setY(y);\n\t\ts.setZ(z);\n\t}\n\n\t// Cannot override these methods as they are final:\n\t// public void setScaleX(double x) { s.setX(x); }\n\t// public void setScaleY(double y) { s.setY(y); }\n\t// public void setScaleZ(double z) { s.setZ(z); }\n\t/**\n\t * Sets the sx.\n\t *\n\t * @param x\n\t *            the new sx\n\t */\n\t// Use these methods instead:\n\tpublic void setSx(double x) {\n\t\ts.setX(x);\n\t}\n\n\t/**\n\t * Sets the sy.\n\t *\n\t * @param y\n\t *            the new sy\n\t */\n\tpublic void setSy(double y) {\n\t\ts.setY(y);\n\t}\n\n\t/**\n\t * Sets the sz.\n\t *\n\t * @param z\n\t *            the new sz\n\t */\n\tpublic void setSz(double z) {\n\t\ts.setZ(z);\n\t}\n\n\t/**\n\t * Sets the pivot.\n\t *\n\t * @param x\n\t *            the x\n\t * @param y\n\t *            the y\n\t * @param z\n\t *            the z\n\t */\n\tpublic void setPivot(double x, double y, double z) {\n\t\tp.setX(x);\n\t\tp.setY(y);\n\t\tp.setZ(z);\n\t\tip.setX(-x);\n\t\tip.setY(-y);\n\t\tip.setZ(-z);\n\t}\n\n\t/**\n\t * Reset.\n\t */\n\tpublic void reset() {\n\t\tt.setX(0.0);\n\t\tt.setY(0.0);\n\t\tt.setZ(0.0);\n\t\trx.setAngle(0.0);\n\t\try.setAngle(0.0);\n\t\trz.setAngle(0.0);\n\t\ts.setX(1.0);\n\t\ts.setY(1.0);\n\t\ts.setZ(1.0);\n\t\tp.setX(0.0);\n\t\tp.setY(0.0);\n\t\tp.setZ(0.0);\n\t\tip.setX(0.0);\n\t\tip.setY(0.0);\n\t\tip.setZ(0.0);\n\t}\n\n\t/**\n\t * Reset tsp.\n\t */\n\tpublic void resetTSP() {\n\t\tt.setX(0.0);\n\t\tt.setY(0.0);\n\t\tt.setZ(0.0);\n\t\ts.setX(1.0);\n\t\ts.setY(1.0);\n\t\ts.setZ(1.0);\n\t\tp.setX(0.0);\n\t\tp.setY(0.0);\n\t\tp.setZ(0.0);\n\t\tip.setX(0.0);\n\t\tip.setY(0.0);\n\t\tip.setZ(0.0);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/utils/BowlerConnectionMenu.java",
    "content": "package com.neuronrobotics.bowlerstudio.utils;\n\nimport com.neuronrobotics.bowlerstudio.BowlerStudio;\nimport com.neuronrobotics.bowlerstudio.BowlerStudioController;\nimport com.neuronrobotics.bowlerstudio.assets.AssetFactory;\nimport com.neuronrobotics.bowlerstudio.assets.FontSizeManager;\nimport com.neuronrobotics.sdk.common.BowlerDatagram;\nimport com.neuronrobotics.sdk.common.DeviceManager;\nimport com.neuronrobotics.sdk.common.Log;\nimport com.neuronrobotics.sdk.network.BowlerTCPClient;\nimport com.neuronrobotics.sdk.network.UDPBowlerConnection;\nimport com.neuronrobotics.sdk.serial.SerialConnection;\n\nimport javafx.application.Application;\nimport javafx.fxml.FXML;\nimport javafx.fxml.FXMLLoader;\nimport javafx.scene.Parent;\nimport javafx.scene.Scene;\nimport javafx.scene.control.*;\nimport javafx.stage.Modality;\nimport javafx.stage.Stage;\n\nimport java.net.InetAddress;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.ResourceBundle;\n\n/**\n * Sample Skeleton for \"BowlerConnectionMenue.fxml\" Controller Class You can\n * copy and paste this code into your favorite IDE\n **/\n\npublic class BowlerConnectionMenu extends Application {\n\n\t@FXML // ResourceBundle that was given to the FXMLLoader\n\tprivate ResourceBundle resources;\n\n\t@FXML // URL location of the FXML file that was given to the FXMLLoader\n\tprivate URL location;\n\n\t@FXML // fx:id=\"baudrate\"\n\tprivate TextField baudrate; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"connectNetwork\"\n\tprivate Button connectNetwork; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"connectSerial\"\n\tprivate Button connectSerial; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"portOptions\"\n\tprivate ComboBox<String> portOptions; // Value injected by FXMLLoader\n\n\t@FXML\n\tprivate ComboBox<String> ipSelector;\n\n\t@FXML // fx:id=\"portType\"\n\tprivate ToggleGroup portType; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"searchNetwork\"\n\tprivate Button searchNetwork; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"searchSerial\"\n\tprivate Button searchSerial; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"tcpPort\"\n\tprivate TextField tcpPort; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"tcpSelect\"\n\tprivate RadioButton tcpSelect; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"udpPort\"\n\tprivate TextField udpPort; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"udpSelect\"\n\tprivate RadioButton udpSelect; // Value injected by FXMLLoader\n\n\tprivate UDPBowlerConnection clnt;\n\n\tprivate int defaultPortNum = 1865;\n\n\tprivate Stage primaryStage;\n\n\tprivate String port;\n\n\tprivate int baud;\n\n\t@FXML // This method is called by the FXMLLoader when initialization is complete\n\tvoid initialize() {\n\t\tcom.neuronrobotics.sdk.common.Log.error(\"Initializing conection Dialog\");\n\t\tassert baudrate != null\n\t\t\t\t: \"fx:id=\\\"baudrate\\\" was not injected: check your FXML file 'BowlerConnectionMenue.fxml'.\";\n\t\tassert connectNetwork != null\n\t\t\t\t: \"fx:id=\\\"connectNetwork\\\" was not injected: check your FXML file 'BowlerConnectionMenue.fxml'.\";\n\t\tassert connectSerial != null\n\t\t\t\t: \"fx:id=\\\"connectSerial\\\" was not injected: check your FXML file 'BowlerConnectionMenue.fxml'.\";\n\t\tassert portOptions != null\n\t\t\t\t: \"fx:id=\\\"portOptions\\\" was not injected: check your FXML file 'BowlerConnectionMenue.fxml'.\";\n\t\tassert portType != null\n\t\t\t\t: \"fx:id=\\\"portType\\\" was not injected: check your FXML file 'BowlerConnectionMenue.fxml'.\";\n\t\tassert searchNetwork != null\n\t\t\t\t: \"fx:id=\\\"searchNetwork\\\" was not injected: check your FXML file 'BowlerConnectionMenue.fxml'.\";\n\t\tassert searchSerial != null\n\t\t\t\t: \"fx:id=\\\"searchSerial\\\" was not injected: check your FXML file 'BowlerConnectionMenue.fxml'.\";\n\t\tassert tcpPort != null\n\t\t\t\t: \"fx:id=\\\"tcpPort\\\" was not injected: check your FXML file 'BowlerConnectionMenue.fxml'.\";\n\t\tassert tcpSelect != null\n\t\t\t\t: \"fx:id=\\\"tcpSelect\\\" was not injected: check your FXML file 'BowlerConnectionMenue.fxml'.\";\n\t\tassert udpPort != null\n\t\t\t\t: \"fx:id=\\\"udpPort\\\" was not injected: check your FXML file 'BowlerConnectionMenue.fxml'.\";\n\t\tassert udpSelect != null\n\t\t\t\t: \"fx:id=\\\"udpSelect\\\" was not injected: check your FXML file 'BowlerConnectionMenue.fxml'.\";\n\t\tassert ipSelector != null\n\t\t\t\t: \"fx:id=\\\"ipSelector\\\" was not injected: check your FXML file 'BowlerConnectionMenu.fxml'.\";\n\n\t\trunsearchSerial();\n\t\trunsearchNetwork();\n\n\t\tsearchNetwork.setOnAction(event -> {\n\t\t\trunsearchNetwork();\n\t\t});\n\n\t\tsearchSerial.setOnAction(event -> {\n\t\t\trunsearchSerial();\n\t\t});\n\n\t\tconnectNetwork.setOnAction(event -> {\n\t\t\trunconnectNetwork();\n\t\t\tprimaryStage.hide();\n\t\t});\n\n\t\tconnectSerial.setOnAction(event -> {\n\t\t\trunconnectSerial();\n\t\t\tprimaryStage.hide();\n\t\t});\n\n\t}\n\n\tprivate void runconnectSerial() {\n\n\t\tnew Thread(() -> {\n\t\t\tfor (int i = 0; i < 3; i++) {\n\t\t\t\tSerialConnection ser = null;\n\t\t\t\ttry {\n\t\t\t\t\tBowlerDatagram.setUseBowlerV4(true);\n\t\t\t\t\tbaud = Integer.parseInt(baudrate.getText());\n\t\t\t\t\tif (baud < 0)\n\t\t\t\t\t\tthrow new NumberFormatException();\n\n\t\t\t\t\tport = portOptions.getSelectionModel().getSelectedItem().toString();\n\t\t\t\t\tint level = Log.getMinimumPrintLevel();\n\t\t\t\t\t// Log.enableInfoPrint();\n\t\t\t\t\tser = new SerialConnection(port, baud);\n\n\t\t\t\t\tDeviceManager.addConnection(ser);\n\t\t\t\t\treturn;\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tcom.neuronrobotics.sdk.common.Log\n\t\t\t\t\t\t\t.error(\"false start \" + port + \" at baud \" + baud + \" is not responding\");\n\t\t\t\t\tBowlerStudioController.highlightException(null, e);\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t\tif (ser != null)\n\t\t\t\t\t\tser.disconnect();\n\t\t\t\t}\n\t\t\t}\n\t\t\tcom.neuronrobotics.sdk.common.Log\n\t\t\t\t\t.error(\"Connection failed! \" + port + \" at baud \" + baud + \" is not responding\");\n\t\t}).start();\n\n\t}\n\n\tprivate void runconnectNetwork() {\n\n\t\tnew Thread(() -> {\n\t\t\tint port;\n\t\t\tString ip = ipSelector.getSelectionModel().getSelectedItem().toString();\n\n\t\t\tif (udpSelect.isSelected()) {\n\t\t\t\tport = Integer.parseInt(udpPort.getText());\n\t\t\t\ttry {\n\t\t\t\t\tclnt = new UDPBowlerConnection(InetAddress.getByName(ip), port);\n\t\t\t\t\tDeviceManager.addConnection(clnt);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tcom.neuronrobotics.sdk.common.Log\n\t\t\t\t\t\t\t.error(\"Connection failed! \" + ip + \" at port \" + ip + \" is not responding\");\n\t\t\t\t\tBowlerStudioController.highlightException(null, e);\n\t\t\t\t\tif (clnt != null)\n\t\t\t\t\t\tclnt.disconnect();\n\t\t\t\t}\n\n\t\t\t} else {\n\t\t\t\tport = Integer.parseInt(tcpPort.getText());\n\t\t\t\tBowlerTCPClient tcp = null;\n\t\t\t\ttry {\n\t\t\t\t\ttcp = new BowlerTCPClient(ip, port);\n\t\t\t\t\tDeviceManager.addConnection(tcp);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tcom.neuronrobotics.sdk.common.Log\n\t\t\t\t\t\t\t.error(\"Connection failed! \" + ip + \" at port \" + ip + \" is not responding\");\n\t\t\t\t\tBowlerStudioController.highlightException(null, e);\n\t\t\t\t\tif (tcp != null)\n\t\t\t\t\t\ttcp.disconnect();\n\t\t\t\t}\n\t\t\t}\n\n\t\t}).start();\n\t}\n\n\tprivate void runsearchSerial() {\n\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tportOptions.getItems().clear();\n\t\t\tnew Thread(() -> {\n\n\t\t\t\tfor (String s : SerialConnection.getAvailableSerialPorts())\n\t\t\t\t\tBowlerStudio.runLater(() -> portOptions.getItems().add(s));\n\n\t\t\t}).start();\n\t\t});\n\n\t}\n\n\tprivate void runsearchNetwork() {\n\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tipSelector.getItems().clear();\n\t\t\tBowlerStudio.runLater(() -> ipSelector.getItems().add(\"127.0.0.1\"));\n\t\t\tnew Thread(() -> {\n\t\t\t\tint prt;\n\t\t\t\ttry {\n\t\t\t\t\tprt = Integer.parseInt(udpPort.getText());\n\t\t\t\t} catch (NumberFormatException e) {\n\t\t\t\t\tprt = defaultPortNum;\n\t\t\t\t\tBowlerStudio.runLater(() -> udpPort.setText(String.valueOf(defaultPortNum)));\n\t\t\t\t}\n\t\t\t\tclnt = new UDPBowlerConnection(prt);\n\t\t\t\tArrayList<InetAddress> addrs = clnt.getAllAddresses();\n\n\t\t\t\tfor (InetAddress i : addrs)\n\t\t\t\t\tBowlerStudio.runLater(() -> ipSelector.getItems().add(i.getHostAddress()));\n\n\t\t\t}).start();\n\t\t});\n\n\t}\n\n\t@SuppressWarnings(\"restriction\")\n\t@Override\n\tpublic void start(Stage primaryStage) throws Exception {\n\t\tthis.primaryStage = primaryStage;\n\t\tFXMLLoader loader = AssetFactory.loadLayout(\"layout/BowlerConnectionMenu.fxml\", true);\n\t\tParent root;\n\t\tloader.setController(this);\n\t\t// This is needed when loading on MAC\n\t\tloader.setClassLoader(getClass().getClassLoader());\n\t\troot = loader.load();\n\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\tint tmp = fontNum - 10;\n\t\t\tif (tmp < 12)\n\t\t\t\ttmp = 12;\n\t\t\troot.setStyle(\"-fx-font-size: \" + tmp + \"pt\");\n\t\t});\n\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tprimaryStage.setTitle(\"Bowler Device Connection\");\n\n\t\t\tScene scene = new Scene(root);\n\t\t\tprimaryStage.setScene(scene);\n\t\t\tprimaryStage.initModality(Modality.WINDOW_MODAL);\n\t\t\tprimaryStage.setResizable(true);\n\t\t\tprimaryStage.show();\n\n\t\t});\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/utils/FindTextWidget.java",
    "content": "package com.neuronrobotics.bowlerstudio.utils;\n/**\n * Sample Skeleton for 'findWidget.fxml' Controller Class\n */\n\nimport com.neuronrobotics.bowlerstudio.BowlerStudio;\nimport com.neuronrobotics.bowlerstudio.assets.AssetFactory;\nimport com.neuronrobotics.bowlerstudio.assets.FontSizeManager;\n\nimport javafx.application.Application;\nimport javafx.event.ActionEvent;\nimport javafx.fxml.FXML;\nimport javafx.fxml.FXMLLoader;\nimport javafx.scene.Parent;\nimport javafx.scene.Scene;\nimport javafx.scene.control.CheckBox;\nimport javafx.scene.control.TextField;\nimport javafx.stage.Stage;\n\nimport org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;\n\nimport javax.swing.*;\nimport javax.swing.text.BadLocationException;\nimport javax.swing.text.Document;\n\nimport java.awt.*;\nimport java.awt.geom.Rectangle2D;\n\nimport java.net.URL;\nimport java.util.ResourceBundle;\n\npublic class FindTextWidget extends Application {\n\n\t@FXML // ResourceBundle that was given to the FXMLLoader\n\tprivate ResourceBundle resources;\n\n\t@FXML // URL location of the FXML file that was given to the FXMLLoader\n\tprivate URL location;\n\n\t@FXML // fx:id=\"matchCase\"\n\tprivate CheckBox matchCase; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"findBox\"\n\tprivate TextField findBox; // Value injected by FXMLLoader\n\n\t@FXML // fx:id=\"replaceBox\"\n\tprivate TextField replaceBox; // Value injected by FXMLLoader\n\n\tprivate Stage primaryStage;\n\n\tprivate RSyntaxTextArea textArea;\n\tprivate int pos = 0;\n\n\tprivate int find(double direction) {\n\t\t// BowlerStudio.invokeLater(() -> {\n\t\ttry {\n\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"Got ctrl f \" +\n\t\t\t// textArea.getSelectedText());\n\t\t\t// Get the text to find...convert it to\n\t\t\t// lower case for eaiser comparision\n\n\t\t\tString find = findBox.getText();\n\t\t\tif (!matchCase.isSelected())\n\t\t\t\tfind = find.toLowerCase();\n\n\t\t\t// Focus the text area, otherwise the\n\t\t\t// highlighting won't show up\n\t\t\ttextArea.requestFocusInWindow();\n\n\t\t\t// Make sure we have a valid search term\n\t\t\tif ((find != null) && (find.length() > 0)) {\n\t\t\t\tDocument document = textArea.getDocument();\n\t\t\t\tint findLength = find.length();\n\t\t\t\ttry {\n\t\t\t\t\tboolean found = false;\n\t\t\t\t\t// Rest the search position if we're\n\t\t\t\t\t// at the end of the document\n\t\t\t\t\tif (pos + findLength > document.getLength())\n\t\t\t\t\t\tpos = 0;\n\n\t\t\t\t\tif (pos < 0)\n\t\t\t\t\t\tpos = document.getLength() - findLength;\n\n\t\t\t\t\t// While we haven't reached the end...\n\t\t\t\t\t// \"<=\" Correction\n\t\t\t\t\twhile ((pos + findLength <= document.getLength()) && (pos >= 0)) {\n\t\t\t\t\t\t// Extract the text from the document\n\t\t\t\t\t\tString match = document.getText(pos, findLength);\n\t\t\t\t\t\tif (!matchCase.isSelected()) {\n\t\t\t\t\t\t\tmatch = match.toLowerCase();\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Check to see if it matches or request\n\t\t\t\t\t\tif (match.equals(find)) {\n\t\t\t\t\t\t\tfound = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tpos += 1 * direction;\n\t\t\t\t\t}\n\n\t\t\t\t\tint baseOfFind = pos;\n\t\t\t\t\t// Did we find something...\n\t\t\t\t\tif (found) {\n\t\t\t\t\t\tBowlerStudio.invokeLater(() -> {\n\t\t\t\t\t\t\t// Get the rectangle of the area where\n\t\t\t\t\t\t\t// the text would be visible...\n\t\t\t\t\t\t\tRectangle2D viewRect;\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tviewRect = textArea.modelToView2D(pos);\n\t\t\t\t\t\t\t\t// Scroll to make the rectangle visible\n\t\t\t\t\t\t\t\ttextArea.scrollRectToVisible(viewRect.getBounds());\n\t\t\t\t\t\t\t\t// Highlight the text\n\t\t\t\t\t\t\t\ttextArea.setCaretPosition(pos);\n\t\t\t\t\t\t\t\ttextArea.moveCaretPosition(pos + findLength);\n\t\t\t\t\t\t\t\t// Move the search position beyond\n\t\t\t\t\t\t\t\t// the current match\n\t\t\t\t\t\t\t\tpos += findLength * direction;\n\t\t\t\t\t\t\t} catch (BadLocationException e) {\n\t\t\t\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\t\t\t\te.printStackTrace();\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\treturn baseOfFind;\n\n\t\t\t\t} catch (Exception exp) {\n\t\t\t\t\texp.printStackTrace();\n\t\t\t\t}\n\n\t\t\t}\n\t\t} catch (Exception ex) {\n\t\t\tex.printStackTrace();\n\t\t}\n\t\t// });\n\t\treturn pos;\n\t}\n\n\tprivate void replace(double direction) {\n\t\tnew Thread(() -> {\n\t\t\tString find = findBox.getText();\n\t\t\tString replace = replaceBox.getText();\n\t\t\tif (replace == null)\n\t\t\t\treplace = \"\";\n\n\t\t\tString current = textArea.getText();\n\t\t\tint intLengthOfRemove = find.length();\n\n\t\t\tif (pos >= intLengthOfRemove) {\n\t\t\t\tString firstHalf = current.substring(0, pos - intLengthOfRemove);\n\t\t\t\tString secondtHalf = current.substring(pos);\n\t\t\t\tif (direction > 0) {\n\t\t\t\t\tif (secondtHalf.length() <= intLengthOfRemove)\n\t\t\t\t\t\treturn; // bail\n\t\t\t\t} else {\n\t\t\t\t\tif (firstHalf.length() <= intLengthOfRemove)\n\t\t\t\t\t\treturn; // bail\n\t\t\t\t}\n\t\t\t\tString newContent = firstHalf + replace + secondtHalf;\n\t\t\t\tBowlerStudio.invokeLater(() -> {\n\t\t\t\t\ttextArea.setText(newContent);\n\t\t\t\t\tfind(direction);\n\t\t\t\t});\n\t\t\t} else\n\t\t\t\tfind(direction);\n\n\t\t}).start();\n\t}\n\n\t@FXML\n\tvoid findPrevious(ActionEvent event) {\n\t\tfind(-1);\n\t}\n\n\t@FXML\n\tvoid findNext(ActionEvent event) {\n\t\tfind(1);\n\t}\n\n\t@FXML\n\tvoid replaceNext(ActionEvent event) {\n\t\treplace(1);\n\t}\n\n\t@FXML\n\tvoid replacePrevious(ActionEvent event) {\n\t\treplace(-1);\n\t}\n\n\t@FXML // This method is called by the FXMLLoader when initialization is\n\t\t\t// complete\n\tvoid initialize() {\n\t\tassert matchCase != null : \"fx:id=\\\"matchCase\\\" was not injected: check your FXML file 'findWidget.fxml'.\";\n\t\tassert findBox != null : \"fx:id=\\\"findBox\\\" was not injected: check your FXML file 'findWidget.fxml'.\";\n\t\tassert replaceBox != null : \"fx:id=\\\"replaceBox\\\" was not injected: check your FXML file 'findWidget.fxml'.\";\n\n\t\tif (textArea.getSelectedText() != null)\n\t\t\tBowlerStudio.runLater(() -> findBox.setText(textArea.getSelectedText()));\n\n\t}\n\n\t@SuppressWarnings(\"restriction\")\n\t@Override\n\tpublic void start(Stage primaryStage) throws Exception {\n\t\tthis.primaryStage = primaryStage;\n\t\tFXMLLoader loader = AssetFactory.loadLayout(\"layout/findWidget.fxml\", true);\n\t\tParent root;\n\t\tloader.setController(this);\n\t\t// This is needed when loading on MAC\n\t\tloader.setClassLoader(getClass().getClassLoader());\n\t\troot = loader.load();\n\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\tint tmp = fontNum - 10;\n\t\t\tif (tmp < 12)\n\t\t\t\ttmp = 12;\n\n\t\t\troot.setStyle(\"-fx-font-size: \" + tmp + \"pt\");\n\t\t});\n\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tprimaryStage.setTitle(\"Find/Replace\");\n\t\t\tScene scene = new Scene(root);\n\t\t\tprimaryStage.setScene(scene);\n\t\t\t// primaryStage.initModality(Modality.WINDOW_MODAL);\n\t\t\tprimaryStage.setResizable(true);\n\t\t\tprimaryStage.show();\n\t\t});\n\t}\n\n\tpublic void setTextArea(RSyntaxTextArea textArea) {\n\t\tthis.textArea = textArea;\n\t\tpos = 0;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/utils/ImageTracer.java",
    "content": "/*\n\tImageTracer.java\n\t(Desktop version with javax.imageio. See ImageTracerAndroid.java for the Android version.)\n\tSimple raster image tracer and vectorizer written in Java. This is a port of imagetracer.js.\n\tby András Jankovics 2015, 2016\n\tandras@jankovics.net\n\n */\n\n/*\n\nThe Unlicense / PUBLIC DOMAIN\n\nThis is free and unencumbered software released into the public domain.\n\nAnyone is free to copy, modify, publish, use, compile, sell, or\ndistribute this software, either in source code form or as a compiled\nbinary, for any purpose, commercial or non-commercial, and by any\nmeans.\n\nIn jurisdictions that recognize copyright laws, the author or authors\nof this software dedicate any and all copyright interest in the\nsoftware to the public domain. We make this dedication for the benefit\nof the public at large and to the detriment of our heirs and\nsuccessors. We intend this dedication to be an overt act of\nrelinquishment in perpetuity of all present and future rights to this\nsoftware under copyright law.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR\nOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\nARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE.\n\nFor more information, please refer to http://unlicense.org/\n\n */\npackage com.neuronrobotics.bowlerstudio.utils;\n\nimport java.awt.image.BufferedImage;\nimport java.io.BufferedWriter;\nimport java.io.File;\nimport java.io.FileWriter;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.Map.Entry;\nimport java.util.TreeMap;\n\nimport javax.imageio.ImageIO;\n\npublic class ImageTracer {\n\n\tpublic static String versionnumber = \"1.1.1\";\n\n\tpublic ImageTracer() {\n\t}\n\n\tpublic static void main(String[] args) {\n\t\ttry {\n\n\t\t\tif (args.length < 1) {\n\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\n\t\t\t\t\t\t\"ERROR: there's no input filename. Basic usage: \\r\\n\\r\\njava -jar ImageTracer.jar <filename>\"\n\t\t\t\t\t\t\t\t+ \"\\r\\n\\r\\nor\\r\\n\\r\\njava -jar ImageTracer.jar help\");\n\t\t\t} else if (arraycontains(args, \"help\") > -1) {\n\t\t\t\tcom.neuronrobotics.sdk.common.Log\n\t\t\t\t\t\t.error(\"Example usage:\\r\\n\\r\\njava -jar ImageTracer.jar <filename> outfilename test.svg \"\n\t\t\t\t\t\t\t\t+ \"ltres 1 qtres 1 pathomit 8 colorsampling 1 numberofcolors 16 mincolorratio 0.02 colorquantcycles 3 \"\n\t\t\t\t\t\t\t\t+ \"scale 1 simplifytolerance 0 roundcoords 1 lcpr 0 qcpr 0 desc 1 viewbox 0 blurradius 0 blurdelta 20 \\r\\n\"\n\t\t\t\t\t\t\t\t+ \"\\r\\nOnly <filename> is mandatory, if some of the other optional parameters are missing, they will be set to these defaults. \"\n\t\t\t\t\t\t\t\t+ \"\\r\\nWarning: if outfilename is not specified, then <filename>.svg will be overwritten.\"\n\t\t\t\t\t\t\t\t+ \"\\r\\nSee https://github.com/jankovicsandras/imagetracerjava for details. \\r\\nThis is version \"\n\t\t\t\t\t\t\t\t+ versionnumber);\n\t\t\t} else {\n\n\t\t\t\t// Parameter parsing\n\t\t\t\tString outfilename = args[0] + \".svg\";\n\t\t\t\tHashMap<String, Float> options = new HashMap<String, Float>();\n\t\t\t\tString[] parameternames = {\"ltres\", \"qtres\", \"pathomit\", \"colorsampling\", \"numberofcolors\",\n\t\t\t\t\t\t\"mincolorratio\", \"colorquantcycles\", \"scale\", \"simplifytolerance\", \"roundcoords\", \"lcpr\",\n\t\t\t\t\t\t\"qcpr\", \"desc\", \"viewbox\", \"blurradius\", \"blurdelta\", \"outfilename\"};\n\t\t\t\tint j = -1;\n\t\t\t\tfloat f = -1;\n\t\t\t\tfor (String parametername : parameternames) {\n\t\t\t\t\tj = arraycontains(args, parametername);\n\t\t\t\t\tif (j > -1) {\n\t\t\t\t\t\tif (parametername == \"outfilename\") {\n\t\t\t\t\t\t\tif (j < (args.length - 1)) {\n\t\t\t\t\t\t\t\toutfilename = args[j + 1];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tf = parsenext(args, j);\n\t\t\t\t\t\t\tif (f > -1) {\n\t\t\t\t\t\t\t\toptions.put(parametername, f);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} // End of parameternames loop\n\n\t\t\t\t// Loading image, tracing, rendering SVG, saving SVG file\n\t\t\t\tsaveString(outfilename, imageToSVG(args[0], options, null));\n\n\t\t\t} // End of parameter parsing and processing\n\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t}// End of main()\n\n\tpublic static int arraycontains(String[] arr, String str) {\n\t\tfor (int j = 0; j < arr.length; j++) {\n\t\t\tif (arr[j].toLowerCase().equals(str)) {\n\t\t\t\treturn j;\n\t\t\t}\n\t\t}\n\t\treturn -1;\n\t}\n\n\tpublic static float parsenext(String[] arr, int i) {\n\t\tif (i < (arr.length - 1)) {\n\t\t\ttry {\n\t\t\t\treturn Float.parseFloat(arr[i + 1]);\n\t\t\t} catch (Exception e) {\n\t\t\t}\n\t\t}\n\t\treturn -1;\n\t}\n\n\t// Container for the color-indexed image before and tracedata after vectorizing\n\tpublic static class IndexedImage {\n\t\tpublic int width, height;\n\t\tpublic int[][] array; // array[x][y] of palette colors\n\t\tpublic byte[][] palette;// array[palettelength][4] RGBA color palette\n\t\tpublic ArrayList<ArrayList<ArrayList<Double[]>>> layers;// tracedata\n\n\t\tpublic IndexedImage(int[][] marray, byte[][] mpalette) {\n\t\t\tarray = marray;\n\t\t\tpalette = mpalette;\n\t\t\twidth = marray[0].length - 2;\n\t\t\theight = marray.length - 2;// Color quantization adds +2 to the original width and height\n\t\t}\n\t}\n\n\t// https://developer.mozilla.org/en-US/docs/Web/API/ImageData\n\tpublic static class ImageData {\n\t\tpublic int width, height;\n\t\tpublic byte[] data; // raw byte data: R G B A R G B A ...\n\t\tpublic ImageData(int mwidth, int mheight, byte[] mdata) {\n\t\t\twidth = mwidth;\n\t\t\theight = mheight;\n\t\t\tdata = mdata;\n\t\t}\n\t}\n\n\t// Saving a String as a file\n\tpublic static void saveString(String filename, String str) throws Exception {\n\t\tFile file = new File(filename);\n\t\t// if file doesnt exists, then create it\n\t\tif (!file.exists()) {\n\t\t\tfile.createNewFile();\n\t\t}\n\t\tFileWriter fw = new FileWriter(file.getAbsoluteFile());\n\t\tBufferedWriter bw = new BufferedWriter(fw);\n\t\tbw.write(str);\n\t\tbw.close();\n\t}\n\n\t// Loading a file to ImageData, ARGB byte order\n\tpublic static ImageData loadImageData(String filename) throws Exception {\n\t\tBufferedImage image = ImageIO.read(new File(filename));\n\t\treturn loadImageData(image);\n\t}\n\n\tpublic static ImageData loadImageData(BufferedImage image) throws Exception {\n\t\tint width = image.getWidth();\n\t\tint height = image.getHeight();\n\t\tint[] rawdata = image.getRGB(0, 0, width, height, null, 0, width);\n\t\tbyte[] data = new byte[rawdata.length * 4];\n\t\tfor (int i = 0; i < rawdata.length; i++) {\n\t\t\tdata[(i * 4) + 3] = bytetrans((byte) (rawdata[i] >>> 24));\n\t\t\tdata[i * 4] = bytetrans((byte) (rawdata[i] >>> 16));\n\t\t\tdata[(i * 4) + 1] = bytetrans((byte) (rawdata[i] >>> 8));\n\t\t\tdata[(i * 4) + 2] = bytetrans((byte) (rawdata[i]));\n\t\t}\n\t\treturn new ImageData(width, height, data);\n\t}\n\n\t// The bitshift method in loadImageData creates signed bytes where -1 -> 255\n\t// unsigned ; -128 -> 128 unsigned ;\n\t// 127 -> 127 unsigned ; 0 -> 0 unsigned ; These will be converted to -128\n\t// (representing 0 unsigned) ...\n\t// 127 (representing 255 unsigned) and tosvgcolorstr will add +128 to create RGB\n\t// values 0..255\n\tpublic static byte bytetrans(byte b) {\n\t\tif (b < 0) {\n\t\t\treturn (byte) (b + 128);\n\t\t} else {\n\t\t\treturn (byte) (b - 128);\n\t\t}\n\t}\n\n\t////////////////////////////////////////////////////////////\n\t//\n\t// User friendly functions\n\t//\n\t////////////////////////////////////////////////////////////\n\n\t// Loading an image from a file, tracing when loaded, then returning the SVG\n\t// String\n\tpublic static String imageToSVG(String filename, HashMap<String, Float> options, byte[][] palette)\n\t\t\tthrows Exception {\n\t\toptions = checkoptions(options);\n\t\tImageData imgd = loadImageData(filename);\n\t\treturn imagedataToSVG(imgd, options, palette);\n\t}// End of imageToSVG()\n\n\tpublic static String imageToSVG(BufferedImage image, HashMap<String, Float> options, byte[][] palette)\n\t\t\tthrows Exception {\n\t\toptions = checkoptions(options);\n\t\tImageData imgd = loadImageData(image);\n\t\treturn imagedataToSVG(imgd, options, palette);\n\t}// End of imageToSVG()\n\n\t// Tracing ImageData, then returning the SVG String\n\tpublic static String imagedataToSVG(ImageData imgd, HashMap<String, Float> options, byte[][] palette) {\n\t\toptions = checkoptions(options);\n\t\tIndexedImage ii = imagedataToTracedata(imgd, options, palette);\n\t\treturn getsvgstring(ii, options);\n\t}// End of imagedataToSVG()\n\n\t// Loading an image from a file, tracing when loaded, then returning\n\t// IndexedImage with tracedata in layers\n\tpublic IndexedImage imageToTracedata(String filename, HashMap<String, Float> options, byte[][] palette)\n\t\t\tthrows Exception {\n\t\toptions = checkoptions(options);\n\t\tImageData imgd = loadImageData(filename);\n\t\treturn imagedataToTracedata(imgd, options, palette);\n\t}// End of imageToTracedata()\n\n\tpublic IndexedImage imageToTracedata(BufferedImage image, HashMap<String, Float> options, byte[][] palette)\n\t\t\tthrows Exception {\n\t\toptions = checkoptions(options);\n\t\tImageData imgd = loadImageData(image);\n\t\treturn imagedataToTracedata(imgd, options, palette);\n\t}// End of imageToTracedata()\n\n\t// Tracing ImageData, then returning IndexedImage with tracedata in layers\n\tpublic static IndexedImage imagedataToTracedata(ImageData imgd, HashMap<String, Float> options, byte[][] palette) {\n\t\t// 1. Color quantization\n\t\tIndexedImage ii = colorquantization(imgd, palette, options);\n\t\t// 2. Layer separation and edge detection\n\t\tint[][][] rawlayers = layering(ii);\n\t\t// 3. Batch pathscan\n\t\tArrayList<ArrayList<ArrayList<Integer[]>>> bps = batchpathscan(rawlayers,\n\t\t\t\t(int) (Math.floor(options.get(\"pathomit\"))));\n\t\t// 4. Batch interpollation\n\t\tArrayList<ArrayList<ArrayList<Double[]>>> bis = batchinternodes(bps);\n\t\t// 5. Batch tracing\n\t\tii.layers = batchtracelayers(bis, options.get(\"ltres\"), options.get(\"qtres\"));\n\t\treturn ii;\n\t}// End of imagedataToTracedata()\n\n\t// creating options object, setting defaults for missing values\n\tpublic static HashMap<String, Float> checkoptions(HashMap<String, Float> options) {\n\t\tif (options == null) {\n\t\t\toptions = new HashMap<String, Float>();\n\t\t}\n\t\t// Tracing\n\t\tif (!options.containsKey(\"ltres\")) {\n\t\t\toptions.put(\"ltres\", 1f);\n\t\t}\n\t\tif (!options.containsKey(\"qtres\")) {\n\t\t\toptions.put(\"qtres\", 1f);\n\t\t}\n\t\tif (!options.containsKey(\"pathomit\")) {\n\t\t\toptions.put(\"pathomit\", 8f);\n\t\t}\n\t\t// Color quantization\n\t\tif (!options.containsKey(\"colorsampling\")) {\n\t\t\toptions.put(\"colorsampling\", 1f);\n\t\t}\n\t\tif (!options.containsKey(\"numberofcolors\")) {\n\t\t\toptions.put(\"numberofcolors\", 16f);\n\t\t}\n\t\tif (!options.containsKey(\"mincolorratio\")) {\n\t\t\toptions.put(\"mincolorratio\", 0.02f);\n\t\t}\n\t\tif (!options.containsKey(\"colorquantcycles\")) {\n\t\t\toptions.put(\"colorquantcycles\", 3f);\n\t\t}\n\t\t// SVG rendering\n\t\tif (!options.containsKey(\"scale\")) {\n\t\t\toptions.put(\"scale\", 1f);\n\t\t}\n\t\tif (!options.containsKey(\"simplifytolerance\")) {\n\t\t\toptions.put(\"simplifytolerance\", 0f);\n\t\t}\n\t\tif (!options.containsKey(\"roundcoords\")) {\n\t\t\toptions.put(\"roundcoords\", 1f);\n\t\t}\n\t\tif (!options.containsKey(\"lcpr\")) {\n\t\t\toptions.put(\"lcpr\", 0f);\n\t\t}\n\t\tif (!options.containsKey(\"qcpr\")) {\n\t\t\toptions.put(\"qcpr\", 0f);\n\t\t}\n\t\tif (!options.containsKey(\"desc\")) {\n\t\t\toptions.put(\"desc\", 1f);\n\t\t}\n\t\tif (!options.containsKey(\"viewbox\")) {\n\t\t\toptions.put(\"viewbox\", 0f);\n\t\t}\n\t\t// Blur\n\t\tif (!options.containsKey(\"blurradius\")) {\n\t\t\toptions.put(\"blurradius\", 0f);\n\t\t}\n\t\tif (!options.containsKey(\"blurdelta\")) {\n\t\t\toptions.put(\"blurdelta\", 20f);\n\t\t}\n\n\t\treturn options;\n\t}// End of checkoptions()\n\n\t////////////////////////////////////////////////////////////\n\t//\n\t// Vectorizing functions\n\t//\n\t////////////////////////////////////////////////////////////\n\n\t// 1. Color quantization repeated \"cycles\" times, based on K-means clustering\n\t// https://en.wikipedia.org/wiki/Color_quantization\n\t// https://en.wikipedia.org/wiki/K-means_clustering\n\tpublic static IndexedImage colorquantization(ImageData imgd, byte[][] palette, HashMap<String, Float> options) {\n\t\tint numberofcolors = (int) Math.floor(options.get(\"numberofcolors\"));\n\t\tfloat minratio = options.get(\"mincolorratio\");\n\t\tint cycles = (int) Math.floor(options.get(\"colorquantcycles\"));\n\t\t// Creating indexed color array arr which has a boundary filled with -1 in every\n\t\t// direction\n\t\tint[][] arr = new int[imgd.height + 2][imgd.width + 2];\n\t\tfor (int j = 0; j < (imgd.height + 2); j++) {\n\t\t\tarr[j][0] = -1;\n\t\t\tarr[j][imgd.width + 1] = -1;\n\t\t}\n\t\tfor (int i = 0; i < (imgd.width + 2); i++) {\n\t\t\tarr[0][i] = -1;\n\t\t\tarr[imgd.height + 1][i] = -1;\n\t\t}\n\n\t\tint idx = 0, cd, cdl, ci, c1, c2, c3, c4;\n\n\t\t// Use custom palette if pal is defined or sample or generate custom length\n\t\t// palette\n\t\tif (palette == null) {\n\t\t\tif (options.get(\"colorsampling\") != 0) {\n\t\t\t\tpalette = samplepalette(numberofcolors, imgd);\n\t\t\t} else {\n\t\t\t\tpalette = generatepalette(numberofcolors);\n\t\t\t}\n\t\t}\n\n\t\t// Selective Gaussian blur preprocessing\n\t\tif (options.get(\"blurradius\") > 0) {\n\t\t\timgd = blur(imgd, options.get(\"blurradius\"), options.get(\"blurdelta\"));\n\t\t}\n\n\t\tlong[][] paletteacc = new long[palette.length][5];\n\n\t\t// Repeat clustering step \"cycles\" times\n\t\tfor (int cnt = 0; cnt < cycles; cnt++) {\n\n\t\t\t// Average colors from the second iteration\n\t\t\tif (cnt > 0) {\n\t\t\t\t// averaging paletteacc for palette\n\t\t\t\tfloat ratio;\n\t\t\t\tfor (int k = 0; k < palette.length; k++) {\n\t\t\t\t\t// averaging\n\t\t\t\t\tif (paletteacc[k][3] > 0) {\n\t\t\t\t\t\tpalette[k][0] = (byte) (-128 + Math.floor(paletteacc[k][0] / paletteacc[k][4]));\n\t\t\t\t\t\tpalette[k][1] = (byte) (-128 + Math.floor(paletteacc[k][1] / paletteacc[k][4]));\n\t\t\t\t\t\tpalette[k][2] = (byte) (-128 + Math.floor(paletteacc[k][2] / paletteacc[k][4]));\n\t\t\t\t\t\tpalette[k][3] = (byte) (-128 + Math.floor(paletteacc[k][3] / paletteacc[k][4]));\n\t\t\t\t\t}\n\t\t\t\t\tratio = paletteacc[k][4] / (imgd.width * imgd.height);\n\n\t\t\t\t\t// Randomizing a color, if there are too few pixels and there will be a new\n\t\t\t\t\t// cycle\n\t\t\t\t\tif ((ratio < minratio) && (cnt < (cycles - 1))) {\n\t\t\t\t\t\tpalette[k][0] = (byte) (-128 + Math.floor(Math.random() * 255));\n\t\t\t\t\t\tpalette[k][1] = (byte) (-128 + Math.floor(Math.random() * 255));\n\t\t\t\t\t\tpalette[k][2] = (byte) (-128 + Math.floor(Math.random() * 255));\n\t\t\t\t\t\tpalette[k][3] = (byte) (-128 + Math.floor(Math.random() * 255));\n\t\t\t\t\t}\n\n\t\t\t\t} // End of palette loop\n\t\t\t} // End of Average colors from the second iteration\n\n\t\t\t// Reseting palette accumulator for averaging\n\t\t\tfor (int i = 0; i < palette.length; i++) {\n\t\t\t\tpaletteacc[i][0] = 0;\n\t\t\t\tpaletteacc[i][1] = 0;\n\t\t\t\tpaletteacc[i][2] = 0;\n\t\t\t\tpaletteacc[i][3] = 0;\n\t\t\t\tpaletteacc[i][4] = 0;\n\t\t\t}\n\n\t\t\t// loop through all pixels\n\t\t\tfor (int j = 0; j < imgd.height; j++) {\n\t\t\t\tfor (int i = 0; i < imgd.width; i++) {\n\n\t\t\t\t\tidx = ((j * imgd.width) + i) * 4;\n\n\t\t\t\t\t// find closest color from palette by measuring (rectilinear) color distance\n\t\t\t\t\t// between this pixel and all palette colors\n\t\t\t\t\tcdl = 256 + 256 + 256 + 256;\n\t\t\t\t\tci = 0;\n\t\t\t\t\tfor (int k = 0; k < palette.length; k++) {\n\n\t\t\t\t\t\t// In my experience, https://en.wikipedia.org/wiki/Rectilinear_distance works\n\t\t\t\t\t\t// better than https://en.wikipedia.org/wiki/Euclidean_distance\n\t\t\t\t\t\tc1 = Math.abs(palette[k][0] - imgd.data[idx]);\n\t\t\t\t\t\tc2 = Math.abs(palette[k][1] - imgd.data[idx + 1]);\n\t\t\t\t\t\tc3 = Math.abs(palette[k][2] - imgd.data[idx + 2]);\n\t\t\t\t\t\tc4 = Math.abs(palette[k][3] - imgd.data[idx + 3]);\n\t\t\t\t\t\tcd = c1 + c2 + c3 + (c4 * 4); // weighted alpha seems to help images with transparency\n\n\t\t\t\t\t\t// Remember this color if this is the closest yet\n\t\t\t\t\t\tif (cd < cdl) {\n\t\t\t\t\t\t\tcdl = cd;\n\t\t\t\t\t\t\tci = k;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} // End of palette loop\n\n\t\t\t\t\t// add to palettacc\n\t\t\t\t\tpaletteacc[ci][0] += 128 + imgd.data[idx];\n\t\t\t\t\tpaletteacc[ci][1] += 128 + imgd.data[idx + 1];\n\t\t\t\t\tpaletteacc[ci][2] += 128 + imgd.data[idx + 2];\n\t\t\t\t\tpaletteacc[ci][3] += 128 + imgd.data[idx + 3];\n\t\t\t\t\tpaletteacc[ci][4]++;\n\n\t\t\t\t\tarr[j + 1][i + 1] = ci;\n\t\t\t\t} // End of i loop\n\t\t\t} // End of j loop\n\n\t\t} // End of Repeat clustering step \"cycles\" times\n\n\t\treturn new IndexedImage(arr, palette);\n\t}// End of colorquantization\n\n\t// Generating a palette with numberofcolors, array[numberofcolors][4] where\n\t// [i][0] = R ; [i][1] = G ; [i][2] = B ; [i][3] = A\n\tpublic static byte[][] generatepalette(int numberofcolors) {\n\t\tbyte[][] palette = new byte[numberofcolors][4];\n\t\tif (numberofcolors < 8) {\n\n\t\t\t// Grayscale\n\t\t\tbyte graystep = (byte) Math.floor(255 / (numberofcolors - 1));\n\t\t\tfor (byte ccnt = 0; ccnt < numberofcolors; ccnt++) {\n\t\t\t\tpalette[ccnt][0] = (byte) (-128 + (ccnt * graystep));\n\t\t\t\tpalette[ccnt][1] = (byte) (-128 + (ccnt * graystep));\n\t\t\t\tpalette[ccnt][2] = (byte) (-128 + (ccnt * graystep));\n\t\t\t\tpalette[ccnt][3] = (byte) 255;\n\t\t\t}\n\n\t\t} else {\n\n\t\t\t// RGB color cube\n\t\t\tint colorqnum = (int) Math.floor(Math.pow(numberofcolors, 1.0 / 3.0)); // Number of points on each edge on\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// the RGB color cube\n\t\t\tint colorstep = (int) Math.floor(255 / (colorqnum - 1)); // distance between points\n\t\t\tint ccnt = 0;\n\t\t\tfor (int rcnt = 0; rcnt < colorqnum; rcnt++) {\n\t\t\t\tfor (int gcnt = 0; gcnt < colorqnum; gcnt++) {\n\t\t\t\t\tfor (int bcnt = 0; bcnt < colorqnum; bcnt++) {\n\t\t\t\t\t\tpalette[ccnt][0] = (byte) (-128 + (rcnt * colorstep));\n\t\t\t\t\t\tpalette[ccnt][1] = (byte) (-128 + (gcnt * colorstep));\n\t\t\t\t\t\tpalette[ccnt][2] = (byte) (-128 + (bcnt * colorstep));\n\t\t\t\t\t\tpalette[ccnt][3] = (byte) 127;\n\t\t\t\t\t\tccnt++;\n\t\t\t\t\t} // End of blue loop\n\t\t\t\t} // End of green loop\n\t\t\t} // End of red loop\n\n\t\t\t// Rest is random\n\t\t\tfor (int rcnt = ccnt; rcnt < numberofcolors; rcnt++) {\n\t\t\t\tpalette[ccnt][0] = (byte) (-128 + Math.floor(Math.random() * 255));\n\t\t\t\tpalette[ccnt][1] = (byte) (-128 + Math.floor(Math.random() * 255));\n\t\t\t\tpalette[ccnt][2] = (byte) (-128 + Math.floor(Math.random() * 255));\n\t\t\t\tpalette[ccnt][3] = (byte) (-128 + Math.floor(Math.random() * 255));\n\t\t\t}\n\n\t\t} // End of numberofcolors check\n\n\t\treturn palette;\n\t};// End of generatepalette()\n\n\tpublic static byte[][] samplepalette(int numberofcolors, ImageData imgd) {\n\t\tint idx = 0;\n\t\tbyte[][] palette = new byte[numberofcolors][4];\n\t\tfor (int i = 0; i < numberofcolors; i++) {\n\t\t\tidx = (int) (Math.floor((Math.random() * imgd.data.length) / 4) * 4);\n\t\t\tpalette[i][0] = imgd.data[idx];\n\t\t\tpalette[i][1] = imgd.data[idx + 1];\n\t\t\tpalette[i][2] = imgd.data[idx + 2];\n\t\t\tpalette[i][3] = imgd.data[idx + 3];\n\t\t}\n\t\treturn palette;\n\t}// End of samplepalette()\n\n\t// 2. Layer separation and edge detection\n\t// Edge node types ( ▓:light or 1; ░:dark or 0 )\n\t// 12 ░░ ▓░ ░▓ ▓▓ ░░ ▓░ ░▓ ▓▓ ░░ ▓░ ░▓ ▓▓ ░░ ▓░ ░▓ ▓▓\n\t// 48 ░░ ░░ ░░ ░░ ░▓ ░▓ ░▓ ░▓ ▓░ ▓░ ▓░ ▓░ ▓▓ ▓▓ ▓▓ ▓▓\n\t// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15\n\t//\n\tpublic static int[][][] layering(IndexedImage ii) {\n\t\t// Creating layers for each indexed color in arr\n\t\tint val = 0, aw = ii.array[0].length, ah = ii.array.length, n1, n2, n3, n4, n5, n6, n7, n8;\n\t\tint[][][] layers = new int[ii.palette.length][ah][aw];\n\n\t\t// Looping through all pixels and calculating edge node type\n\t\tfor (int j = 1; j < (ah - 1); j++) {\n\t\t\tfor (int i = 1; i < (aw - 1); i++) {\n\n\t\t\t\t// This pixel's indexed color\n\t\t\t\tval = ii.array[j][i];\n\n\t\t\t\t// Are neighbor pixel colors the same?\n\t\t\t\tif ((j > 0) && (i > 0)) {\n\t\t\t\t\tn1 = ii.array[j - 1][i - 1] == val ? 1 : 0;\n\t\t\t\t} else {\n\t\t\t\t\tn1 = 0;\n\t\t\t\t}\n\t\t\t\tif (j > 0) {\n\t\t\t\t\tn2 = ii.array[j - 1][i] == val ? 1 : 0;\n\t\t\t\t} else {\n\t\t\t\t\tn2 = 0;\n\t\t\t\t}\n\t\t\t\tif ((j > 0) && (i < (aw - 1))) {\n\t\t\t\t\tn3 = ii.array[j - 1][i + 1] == val ? 1 : 0;\n\t\t\t\t} else {\n\t\t\t\t\tn3 = 0;\n\t\t\t\t}\n\t\t\t\tif (i > 0) {\n\t\t\t\t\tn4 = ii.array[j][i - 1] == val ? 1 : 0;\n\t\t\t\t} else {\n\t\t\t\t\tn4 = 0;\n\t\t\t\t}\n\t\t\t\tif (i < (aw - 1)) {\n\t\t\t\t\tn5 = ii.array[j][i + 1] == val ? 1 : 0;\n\t\t\t\t} else {\n\t\t\t\t\tn5 = 0;\n\t\t\t\t}\n\t\t\t\tif ((j < (ah - 1)) && (i > 0)) {\n\t\t\t\t\tn6 = ii.array[j + 1][i - 1] == val ? 1 : 0;\n\t\t\t\t} else {\n\t\t\t\t\tn6 = 0;\n\t\t\t\t}\n\t\t\t\tif (j < (ah - 1)) {\n\t\t\t\t\tn7 = ii.array[j + 1][i] == val ? 1 : 0;\n\t\t\t\t} else {\n\t\t\t\t\tn7 = 0;\n\t\t\t\t}\n\t\t\t\tif ((j < (ah - 1)) && (i < (aw - 1))) {\n\t\t\t\t\tn8 = ii.array[j + 1][i + 1] == val ? 1 : 0;\n\t\t\t\t} else {\n\t\t\t\t\tn8 = 0;\n\t\t\t\t}\n\n\t\t\t\t// this pixel\"s type and looking back on previous pixels\n\t\t\t\tlayers[val][j + 1][i + 1] = 1 + (n5 * 2) + (n8 * 4) + (n7 * 8);\n\t\t\t\tif (n4 == 0) {\n\t\t\t\t\tlayers[val][j + 1][i] = 0 + 2 + (n7 * 4) + (n6 * 8);\n\t\t\t\t}\n\t\t\t\tif (n2 == 0) {\n\t\t\t\t\tlayers[val][j][i + 1] = 0 + (n3 * 2) + (n5 * 4) + 8;\n\t\t\t\t}\n\t\t\t\tif (n1 == 0) {\n\t\t\t\t\tlayers[val][j][i] = 0 + (n2 * 2) + 4 + (n4 * 8);\n\t\t\t\t}\n\n\t\t\t} // End of i loop\n\t\t} // End of j loop\n\n\t\treturn layers;\n\t}// End of layering()\n\n\t// 3. Walking through an edge node array, discarding edge node types 0 and 15\n\t// and creating paths from the rest.\n\t// Walk directions (dir): 0 > ; 1 ^ ; 2 < ; 3 v\n\t// Edge node types ( ▓:light or 1; ░:dark or 0 )\n\t// ░░ ▓░ ░▓ ▓▓ ░░ ▓░ ░▓ ▓▓ ░░ ▓░ ░▓ ▓▓ ░░ ▓░ ░▓ ▓▓\n\t// ░░ ░░ ░░ ░░ ░▓ ░▓ ░▓ ░▓ ▓░ ▓░ ▓░ ▓░ ▓▓ ▓▓ ▓▓ ▓▓\n\t// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15\n\t//\n\tpublic static ArrayList<ArrayList<Integer[]>> pathscan(int[][] arr, float pathomit) {\n\t\tArrayList<ArrayList<Integer[]>> paths = new ArrayList<ArrayList<Integer[]>>();\n\t\tArrayList<Integer[]> thispath;\n\t\tint px = 0, py = 0, w = arr[0].length, h = arr.length, dir = 0;\n\t\tboolean pathfinished = true, holepath = false;\n\n\t\tfor (int j = 0; j < h; j++) {\n\t\t\tfor (int i = 0; i < w; i++) {\n\t\t\t\tif ((arr[j][i] != 0) && (arr[j][i] != 15)) {\n\t\t\t\t\t// Init\n\t\t\t\t\tpx = i;\n\t\t\t\t\tpy = j;\n\t\t\t\t\tpaths.add(new ArrayList<Integer[]>());\n\t\t\t\t\tthispath = paths.get(paths.size() - 1);\n\t\t\t\t\tpathfinished = false;\n\t\t\t\t\t// fill paths will be drawn, but hole paths are also required to remove\n\t\t\t\t\t// unnecessary edge nodes\n\t\t\t\t\tif (arr[py][px] == 1) {\n\t\t\t\t\t\tdir = 0;\n\t\t\t\t\t}\n\t\t\t\t\tif (arr[py][px] == 2) {\n\t\t\t\t\t\tdir = 3;\n\t\t\t\t\t}\n\t\t\t\t\tif (arr[py][px] == 3) {\n\t\t\t\t\t\tdir = 0;\n\t\t\t\t\t}\n\t\t\t\t\tif (arr[py][px] == 4) {\n\t\t\t\t\t\tdir = 1;\n\t\t\t\t\t\tholepath = false;\n\t\t\t\t\t}\n\t\t\t\t\tif (arr[py][px] == 5) {\n\t\t\t\t\t\tdir = 0;\n\t\t\t\t\t}\n\t\t\t\t\tif (arr[py][px] == 6) {\n\t\t\t\t\t\tdir = 3;\n\t\t\t\t\t}\n\t\t\t\t\tif (arr[py][px] == 7) {\n\t\t\t\t\t\tdir = 0;\n\t\t\t\t\t\tholepath = true;\n\t\t\t\t\t}\n\t\t\t\t\tif (arr[py][px] == 8) {\n\t\t\t\t\t\tdir = 0;\n\t\t\t\t\t}\n\t\t\t\t\tif (arr[py][px] == 9) {\n\t\t\t\t\t\tdir = 3;\n\t\t\t\t\t}\n\t\t\t\t\tif (arr[py][px] == 10) {\n\t\t\t\t\t\tdir = 3;\n\t\t\t\t\t}\n\t\t\t\t\tif (arr[py][px] == 11) {\n\t\t\t\t\t\tdir = 1;\n\t\t\t\t\t\tholepath = true;\n\t\t\t\t\t}\n\t\t\t\t\tif (arr[py][px] == 12) {\n\t\t\t\t\t\tdir = 0;\n\t\t\t\t\t}\n\t\t\t\t\tif (arr[py][px] == 13) {\n\t\t\t\t\t\tdir = 3;\n\t\t\t\t\t\tholepath = true;\n\t\t\t\t\t}\n\t\t\t\t\tif (arr[py][px] == 14) {\n\t\t\t\t\t\tdir = 0;\n\t\t\t\t\t\tholepath = true;\n\t\t\t\t\t}\n\t\t\t\t\t// Path points loop\n\t\t\t\t\twhile (!pathfinished) {\n\n\t\t\t\t\t\t// New path point\n\t\t\t\t\t\tthispath.add(new Integer[3]);\n\t\t\t\t\t\tthispath.get(thispath.size() - 1)[0] = px - 1;\n\t\t\t\t\t\tthispath.get(thispath.size() - 1)[1] = py - 1;\n\t\t\t\t\t\tthispath.get(thispath.size() - 1)[2] = arr[py][px];\n\n\t\t\t\t\t\t// Node types\n\t\t\t\t\t\tif (arr[py][px] == 1) {\n\t\t\t\t\t\t\tarr[py][px] = 0;\n\t\t\t\t\t\t\tif (dir == 0) {\n\t\t\t\t\t\t\t\tpy--;\n\t\t\t\t\t\t\t\tdir = 1;\n\t\t\t\t\t\t\t} else if (dir == 3) {\n\t\t\t\t\t\t\t\tpx--;\n\t\t\t\t\t\t\t\tdir = 2;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tpathfinished = true;\n\t\t\t\t\t\t\t\tpaths.remove(thispath);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\telse if (arr[py][px] == 2) {\n\t\t\t\t\t\t\tarr[py][px] = 0;\n\t\t\t\t\t\t\tif (dir == 3) {\n\t\t\t\t\t\t\t\tpx++;\n\t\t\t\t\t\t\t\tdir = 0;\n\t\t\t\t\t\t\t} else if (dir == 2) {\n\t\t\t\t\t\t\t\tpy--;\n\t\t\t\t\t\t\t\tdir = 1;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tpathfinished = true;\n\t\t\t\t\t\t\t\tpaths.remove(thispath);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\telse if (arr[py][px] == 3) {\n\t\t\t\t\t\t\tarr[py][px] = 0;\n\t\t\t\t\t\t\tif (dir == 0) {\n\t\t\t\t\t\t\t\tpx++;\n\t\t\t\t\t\t\t} else if (dir == 2) {\n\t\t\t\t\t\t\t\tpx--;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tpathfinished = true;\n\t\t\t\t\t\t\t\tpaths.remove(thispath);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\telse if (arr[py][px] == 4) {\n\t\t\t\t\t\t\tarr[py][px] = 0;\n\t\t\t\t\t\t\tif (dir == 1) {\n\t\t\t\t\t\t\t\tpx++;\n\t\t\t\t\t\t\t\tdir = 0;\n\t\t\t\t\t\t\t} else if (dir == 2) {\n\t\t\t\t\t\t\t\tpy++;\n\t\t\t\t\t\t\t\tdir = 3;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tpathfinished = true;\n\t\t\t\t\t\t\t\tpaths.remove(thispath);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\telse if (arr[py][px] == 5) {\n\t\t\t\t\t\t\tif (dir == 0) {\n\t\t\t\t\t\t\t\tarr[py][px] = 13;\n\t\t\t\t\t\t\t\tpy++;\n\t\t\t\t\t\t\t\tdir = 3;\n\t\t\t\t\t\t\t} else if (dir == 1) {\n\t\t\t\t\t\t\t\tarr[py][px] = 13;\n\t\t\t\t\t\t\t\tpx--;\n\t\t\t\t\t\t\t\tdir = 2;\n\t\t\t\t\t\t\t} else if (dir == 2) {\n\t\t\t\t\t\t\t\tarr[py][px] = 7;\n\t\t\t\t\t\t\t\tpy--;\n\t\t\t\t\t\t\t\tdir = 1;\n\t\t\t\t\t\t\t} else if (dir == 3) {\n\t\t\t\t\t\t\t\tarr[py][px] = 7;\n\t\t\t\t\t\t\t\tpx++;\n\t\t\t\t\t\t\t\tdir = 0;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\telse if (arr[py][px] == 6) {\n\t\t\t\t\t\t\tarr[py][px] = 0;\n\t\t\t\t\t\t\tif (dir == 1) {\n\t\t\t\t\t\t\t\tpy--;\n\t\t\t\t\t\t\t} else if (dir == 3) {\n\t\t\t\t\t\t\t\tpy++;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tpathfinished = true;\n\t\t\t\t\t\t\t\tpaths.remove(thispath);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\telse if (arr[py][px] == 7) {\n\t\t\t\t\t\t\tarr[py][px] = 0;\n\t\t\t\t\t\t\tif (dir == 0) {\n\t\t\t\t\t\t\t\tpy++;\n\t\t\t\t\t\t\t\tdir = 3;\n\t\t\t\t\t\t\t} else if (dir == 1) {\n\t\t\t\t\t\t\t\tpx--;\n\t\t\t\t\t\t\t\tdir = 2;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tpathfinished = true;\n\t\t\t\t\t\t\t\tpaths.remove(thispath);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\telse if (arr[py][px] == 8) {\n\t\t\t\t\t\t\tarr[py][px] = 0;\n\t\t\t\t\t\t\tif (dir == 0) {\n\t\t\t\t\t\t\t\tpy++;\n\t\t\t\t\t\t\t\tdir = 3;\n\t\t\t\t\t\t\t} else if (dir == 1) {\n\t\t\t\t\t\t\t\tpx--;\n\t\t\t\t\t\t\t\tdir = 2;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tpathfinished = true;\n\t\t\t\t\t\t\t\tpaths.remove(thispath);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\telse if (arr[py][px] == 9) {\n\t\t\t\t\t\t\tarr[py][px] = 0;\n\t\t\t\t\t\t\tif (dir == 1) {\n\t\t\t\t\t\t\t\tpy--;\n\t\t\t\t\t\t\t} else if (dir == 3) {\n\t\t\t\t\t\t\t\tpy++;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tpathfinished = true;\n\t\t\t\t\t\t\t\tpaths.remove(thispath);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\telse if (arr[py][px] == 10) {\n\t\t\t\t\t\t\tif (dir == 0) {\n\t\t\t\t\t\t\t\tarr[py][px] = 11;\n\t\t\t\t\t\t\t\tpy--;\n\t\t\t\t\t\t\t\tdir = 1;\n\t\t\t\t\t\t\t} else if (dir == 1) {\n\t\t\t\t\t\t\t\tarr[py][px] = 14;\n\t\t\t\t\t\t\t\tpx++;\n\t\t\t\t\t\t\t\tdir = 0;\n\t\t\t\t\t\t\t} else if (dir == 2) {\n\t\t\t\t\t\t\t\tarr[py][px] = 14;\n\t\t\t\t\t\t\t\tpy++;\n\t\t\t\t\t\t\t\tdir = 3;\n\t\t\t\t\t\t\t} else if (dir == 3) {\n\t\t\t\t\t\t\t\tarr[py][px] = 11;\n\t\t\t\t\t\t\t\tpx--;\n\t\t\t\t\t\t\t\tdir = 2;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\telse if (arr[py][px] == 11) {\n\t\t\t\t\t\t\tarr[py][px] = 0;\n\t\t\t\t\t\t\tif (dir == 1) {\n\t\t\t\t\t\t\t\tpx++;\n\t\t\t\t\t\t\t\tdir = 0;\n\t\t\t\t\t\t\t} else if (dir == 2) {\n\t\t\t\t\t\t\t\tpy++;\n\t\t\t\t\t\t\t\tdir = 3;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tpathfinished = true;\n\t\t\t\t\t\t\t\tpaths.remove(thispath);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\telse if (arr[py][px] == 12) {\n\t\t\t\t\t\t\tarr[py][px] = 0;\n\t\t\t\t\t\t\tif (dir == 0) {\n\t\t\t\t\t\t\t\tpx++;\n\t\t\t\t\t\t\t} else if (dir == 2) {\n\t\t\t\t\t\t\t\tpx--;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tpathfinished = true;\n\t\t\t\t\t\t\t\tpaths.remove(thispath);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\telse if (arr[py][px] == 13) {\n\t\t\t\t\t\t\tarr[py][px] = 0;\n\t\t\t\t\t\t\tif (dir == 2) {\n\t\t\t\t\t\t\t\tpy--;\n\t\t\t\t\t\t\t\tdir = 1;\n\t\t\t\t\t\t\t} else if (dir == 3) {\n\t\t\t\t\t\t\t\tpx++;\n\t\t\t\t\t\t\t\tdir = 0;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tpathfinished = true;\n\t\t\t\t\t\t\t\tpaths.remove(thispath);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\telse if (arr[py][px] == 14) {\n\t\t\t\t\t\t\tarr[py][px] = 0;\n\t\t\t\t\t\t\tif (dir == 0) {\n\t\t\t\t\t\t\t\tpy--;\n\t\t\t\t\t\t\t\tdir = 1;\n\t\t\t\t\t\t\t} else if (dir == 3) {\n\t\t\t\t\t\t\t\tpx--;\n\t\t\t\t\t\t\t\tdir = 2;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tpathfinished = true;\n\t\t\t\t\t\t\t\tpaths.remove(thispath);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Close path\n\t\t\t\t\t\tif (((px - 1) == thispath.get(0)[0]) && ((py - 1) == thispath.get(0)[1])) {\n\t\t\t\t\t\t\tpathfinished = true;\n\t\t\t\t\t\t\t// Discarding 'hole' type paths and paths shorter than pathomit\n\t\t\t\t\t\t\tif ((holepath) || (thispath.size() < pathomit)) {\n\t\t\t\t\t\t\t\tpaths.remove(thispath);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} // End of Path points loop\n\n\t\t\t\t} // End of Follow path\n\n\t\t\t} // End of i loop\n\t\t} // End of j loop\n\n\t\treturn paths;\n\t}// End of pathscan()\n\n\t// 3. Batch pathscan\n\tpublic static ArrayList<ArrayList<ArrayList<Integer[]>>> batchpathscan(int[][][] layers, float pathomit) {\n\t\tArrayList<ArrayList<ArrayList<Integer[]>>> bpaths = new ArrayList<ArrayList<ArrayList<Integer[]>>>();\n\t\tfor (int[][] layer : layers) {\n\t\t\tbpaths.add(pathscan(layer, pathomit));\n\t\t}\n\t\treturn bpaths;\n\t}\n\n\t// 4. interpolating between path points for nodes with 8 directions ( East,\n\t// SouthEast, S, SW, W, NW, N, NE )\n\tpublic static ArrayList<ArrayList<Double[]>> internodes(ArrayList<ArrayList<Integer[]>> paths) {\n\t\tArrayList<ArrayList<Double[]>> ins = new ArrayList<ArrayList<Double[]>>();\n\t\tArrayList<Double[]> thisinp;\n\t\tDouble[] thispoint, nextpoint = new Double[2];\n\t\tInteger[] pp1, pp2, pp3;\n\n\t\tint palen = 0, nextidx = 0, nextidx2 = 0;\n\t\t// paths loop\n\t\tfor (int pacnt = 0; pacnt < paths.size(); pacnt++) {\n\t\t\tins.add(new ArrayList<Double[]>());\n\t\t\tthisinp = ins.get(ins.size() - 1);\n\t\t\tpalen = paths.get(pacnt).size();\n\t\t\t// pathpoints loop\n\t\t\tfor (int pcnt = 0; pcnt < palen; pcnt++) {\n\n\t\t\t\t// interpolate between two path points\n\t\t\t\tnextidx = (pcnt + 1) % palen;\n\t\t\t\tnextidx2 = (pcnt + 2) % palen;\n\t\t\t\tthisinp.add(new Double[3]);\n\t\t\t\tthispoint = thisinp.get(thisinp.size() - 1);\n\t\t\t\tpp1 = paths.get(pacnt).get(pcnt);\n\t\t\t\tpp2 = paths.get(pacnt).get(nextidx);\n\t\t\t\tpp3 = paths.get(pacnt).get(nextidx2);\n\t\t\t\tthispoint[0] = (pp1[0] + pp2[0]) / 2.0;\n\t\t\t\tthispoint[1] = (pp1[1] + pp2[1]) / 2.0;\n\t\t\t\tnextpoint[0] = (pp2[0] + pp3[0]) / 2.0;\n\t\t\t\tnextpoint[1] = (pp2[1] + pp3[1]) / 2.0;\n\n\t\t\t\t// line segment direction to the next point\n\t\t\t\tif (thispoint[0] < nextpoint[0]) {\n\t\t\t\t\tif (thispoint[1] < nextpoint[1]) {\n\t\t\t\t\t\tthispoint[2] = 1.0;\n\t\t\t\t\t} // SouthEast\n\t\t\t\t\telse if (thispoint[1] > nextpoint[1]) {\n\t\t\t\t\t\tthispoint[2] = 7.0;\n\t\t\t\t\t} // NE\n\t\t\t\t\telse {\n\t\t\t\t\t\tthispoint[2] = 0.0;\n\t\t\t\t\t} // E\n\t\t\t\t} else if (thispoint[0] > nextpoint[0]) {\n\t\t\t\t\tif (thispoint[1] < nextpoint[1]) {\n\t\t\t\t\t\tthispoint[2] = 3.0;\n\t\t\t\t\t} // SW\n\t\t\t\t\telse if (thispoint[1] > nextpoint[1]) {\n\t\t\t\t\t\tthispoint[2] = 5.0;\n\t\t\t\t\t} // NW\n\t\t\t\t\telse {\n\t\t\t\t\t\tthispoint[2] = 4.0;\n\t\t\t\t\t} // N\n\t\t\t\t} else {\n\t\t\t\t\tif (thispoint[1] < nextpoint[1]) {\n\t\t\t\t\t\tthispoint[2] = 2.0;\n\t\t\t\t\t} // S\n\t\t\t\t\telse if (thispoint[1] > nextpoint[1]) {\n\t\t\t\t\t\tthispoint[2] = 6.0;\n\t\t\t\t\t} // N\n\t\t\t\t\telse {\n\t\t\t\t\t\tthispoint[2] = 8.0;\n\t\t\t\t\t} // center, this should not happen\n\t\t\t\t}\n\n\t\t\t} // End of pathpoints loop\n\n\t\t} // End of paths loop\n\n\t\treturn ins;\n\t}// End of internodes()\n\n\t// 4. Batch interpollation\n\tstatic ArrayList<ArrayList<ArrayList<Double[]>>> batchinternodes(\n\t\t\tArrayList<ArrayList<ArrayList<Integer[]>>> bpaths) {\n\t\tArrayList<ArrayList<ArrayList<Double[]>>> binternodes = new ArrayList<ArrayList<ArrayList<Double[]>>>();\n\t\tfor (int k = 0; k < bpaths.size(); k++) {\n\t\t\tbinternodes.add(internodes(bpaths.get(k)));\n\t\t}\n\t\treturn binternodes;\n\t}\n\n\t// 5. tracepath() : recursively trying to fit straight and quadratic spline\n\t// segments on the 8 direction internode path\n\n\t// 5.1. Find sequences of points with only 2 segment types\n\t// 5.2. Fit a straight line on the sequence\n\t// 5.3. If the straight line fails (an error>ltreshold), find the point with the\n\t// biggest error\n\t// 5.4. Fit a quadratic spline through errorpoint (project this to get\n\t// controlpoint), then measure errors on every point in the sequence\n\t// 5.5. If the spline fails (an error>qtreshold), find the point with the\n\t// biggest error, set splitpoint = (fitting point + errorpoint)/2\n\t// 5.6. Split sequence and recursively apply 5.2. - 5.7. to\n\t// startpoint-splitpoint and splitpoint-endpoint sequences\n\t// 5.7. TODO? If splitpoint-endpoint is a spline, try to add new points from the\n\t// next sequence\n\n\t// This returns an SVG Path segment as a double[7] where\n\t// segment[0] ==1.0 linear ==2.0 quadratic interpolation\n\t// segment[1] , segment[2] : x1 , y1\n\t// segment[3] , segment[4] : x2 , y2 ; middle point of Q curve, endpoint of L\n\t// line\n\t// segment[5] , segment[6] : x3 , y3 for Q curve, should be 0.0 , 0.0 for L line\n\t//\n\t// path type is discarded, no check for path.size < 3 , which should not happen\n\n\tpublic static ArrayList<Double[]> tracepath(ArrayList<Double[]> path, float ltreshold, float qtreshold) {\n\t\tint pcnt = 0, seqend = 0;\n\t\tdouble segtype1, segtype2;\n\t\tArrayList<Double[]> smp = new ArrayList<Double[]>();\n\t\t// Double [] thissegment;\n\t\tint pathlength = path.size();\n\n\t\twhile (pcnt < pathlength) {\n\t\t\t// 5.1. Find sequences of points with only 2 segment types\n\t\t\tsegtype1 = path.get(pcnt)[2];\n\t\t\tsegtype2 = -1;\n\t\t\tseqend = pcnt + 1;\n\t\t\twhile (((path.get(seqend)[2] == segtype1) || (path.get(seqend)[2] == segtype2) || (segtype2 == -1))\n\t\t\t\t\t&& (seqend < (pathlength - 1))) {\n\t\t\t\tif ((path.get(seqend)[2] != segtype1) && (segtype2 == -1)) {\n\t\t\t\t\tsegtype2 = path.get(seqend)[2];\n\t\t\t\t}\n\t\t\t\tseqend++;\n\t\t\t}\n\t\t\tif (seqend == (pathlength - 1)) {\n\t\t\t\tseqend = 0;\n\t\t\t}\n\n\t\t\t// 5.2. - 5.6. Split sequence and recursively apply 5.2. - 5.6. to\n\t\t\t// startpoint-splitpoint and splitpoint-endpoint sequences\n\t\t\tsmp.addAll(fitseq(path, ltreshold, qtreshold, pcnt, seqend));\n\t\t\t// 5.7. TODO? If splitpoint-endpoint is a spline, try to add new points from the\n\t\t\t// next sequence\n\n\t\t\t// forward pcnt;\n\t\t\tif (seqend > 0) {\n\t\t\t\tpcnt = seqend;\n\t\t\t} else {\n\t\t\t\tpcnt = pathlength;\n\t\t\t}\n\n\t\t} // End of pcnt loop\n\n\t\treturn smp;\n\n\t}// End of tracepath()\n\n\t// 5.2. - 5.6. recursively fitting a straight or quadratic line segment on this\n\t// sequence of path nodes,\n\t// called from tracepath()\n\tpublic static ArrayList<Double[]> fitseq(ArrayList<Double[]> path, float ltreshold, float qtreshold, int seqstart,\n\t\t\tint seqend) {\n\t\tArrayList<Double[]> segment = new ArrayList<Double[]>();\n\t\tDouble[] thissegment;\n\t\tint pathlength = path.size();\n\n\t\t// return if invalid seqend\n\t\tif ((seqend > pathlength) || (seqend < 0)) {\n\t\t\treturn segment;\n\t\t}\n\n\t\tint errorpoint = seqstart;\n\t\tboolean curvepass = true;\n\t\tdouble px, py, dist2, errorval = 0;\n\t\tdouble tl = (seqend - seqstart);\n\t\tif (tl < 0) {\n\t\t\ttl += pathlength;\n\t\t}\n\t\tdouble vx = (path.get(seqend)[0] - path.get(seqstart)[0]) / tl,\n\t\t\t\tvy = (path.get(seqend)[1] - path.get(seqstart)[1]) / tl;\n\n\t\t// 5.2. Fit a straight line on the sequence\n\t\tint pcnt = (seqstart + 1) % pathlength;\n\t\tdouble pl;\n\t\twhile (pcnt != seqend) {\n\t\t\tpl = pcnt - seqstart;\n\t\t\tif (pl < 0) {\n\t\t\t\tpl += pathlength;\n\t\t\t}\n\t\t\tpx = path.get(seqstart)[0] + (vx * pl);\n\t\t\tpy = path.get(seqstart)[1] + (vy * pl);\n\t\t\tdist2 = ((path.get(pcnt)[0] - px) * (path.get(pcnt)[0] - px))\n\t\t\t\t\t+ ((path.get(pcnt)[1] - py) * (path.get(pcnt)[1] - py));\n\t\t\tif (dist2 > ltreshold) {\n\t\t\t\tcurvepass = false;\n\t\t\t}\n\t\t\tif (dist2 > errorval) {\n\t\t\t\terrorpoint = pcnt;\n\t\t\t\terrorval = dist2;\n\t\t\t}\n\t\t\tpcnt = (pcnt + 1) % pathlength;\n\t\t}\n\n\t\t// return straight line if fits\n\t\tif (curvepass) {\n\t\t\tsegment.add(new Double[7]);\n\t\t\tthissegment = segment.get(segment.size() - 1);\n\t\t\tthissegment[0] = 1.0;\n\t\t\tthissegment[1] = path.get(seqstart)[0];\n\t\t\tthissegment[2] = path.get(seqstart)[1];\n\t\t\tthissegment[3] = path.get(seqend)[0];\n\t\t\tthissegment[4] = path.get(seqend)[1];\n\t\t\tthissegment[5] = 0.0;\n\t\t\tthissegment[6] = 0.0;\n\t\t\treturn segment;\n\t\t}\n\n\t\t// 5.3. If the straight line fails (an error>ltreshold), find the point with the\n\t\t// biggest error\n\t\tint fitpoint = errorpoint;\n\t\tcurvepass = true;\n\t\terrorval = 0;\n\n\t\t// 5.4. Fit a quadratic spline through this point, measure errors on every point\n\t\t// in the sequence\n\t\t// helpers and projecting to get control point\n\t\tdouble t = (fitpoint - seqstart) / tl, t1 = (1.0 - t) * (1.0 - t), t2 = 2.0 * (1.0 - t) * t, t3 = t * t;\n\t\tdouble cpx = (((t1 * path.get(seqstart)[0]) + (t3 * path.get(seqend)[0])) - path.get(fitpoint)[0]) / -t2,\n\t\t\t\tcpy = (((t1 * path.get(seqstart)[1]) + (t3 * path.get(seqend)[1])) - path.get(fitpoint)[1]) / -t2;\n\n\t\t// Check every point\n\t\tpcnt = seqstart + 1;\n\t\twhile (pcnt != seqend) {\n\n\t\t\tt = (pcnt - seqstart) / tl;\n\t\t\tt1 = (1.0 - t) * (1.0 - t);\n\t\t\tt2 = 2.0 * (1.0 - t) * t;\n\t\t\tt3 = t * t;\n\t\t\tpx = (t1 * path.get(seqstart)[0]) + (t2 * cpx) + (t3 * path.get(seqend)[0]);\n\t\t\tpy = (t1 * path.get(seqstart)[1]) + (t2 * cpy) + (t3 * path.get(seqend)[1]);\n\n\t\t\tdist2 = ((path.get(pcnt)[0] - px) * (path.get(pcnt)[0] - px))\n\t\t\t\t\t+ ((path.get(pcnt)[1] - py) * (path.get(pcnt)[1] - py));\n\n\t\t\tif (dist2 > qtreshold) {\n\t\t\t\tcurvepass = false;\n\t\t\t}\n\t\t\tif (dist2 > errorval) {\n\t\t\t\terrorpoint = pcnt;\n\t\t\t\terrorval = dist2;\n\t\t\t}\n\t\t\tpcnt = (pcnt + 1) % pathlength;\n\t\t}\n\n\t\t// return spline if fits\n\t\tif (curvepass) {\n\t\t\tsegment.add(new Double[7]);\n\t\t\tthissegment = segment.get(segment.size() - 1);\n\t\t\tthissegment[0] = 2.0;\n\t\t\tthissegment[1] = path.get(seqstart)[0];\n\t\t\tthissegment[2] = path.get(seqstart)[1];\n\t\t\tthissegment[3] = cpx;\n\t\t\tthissegment[4] = cpy;\n\t\t\tthissegment[5] = path.get(seqend)[0];\n\t\t\tthissegment[6] = path.get(seqend)[1];\n\t\t\treturn segment;\n\t\t}\n\n\t\t// 5.5. If the spline fails (an error>qtreshold), find the point with the\n\t\t// biggest error,\n\t\t// set splitpoint = (fitting point + errorpoint)/2\n\t\tint splitpoint = (fitpoint + errorpoint) / 2;\n\n\t\t// 5.6. Split sequence and recursively apply 5.2. - 5.6. to\n\t\t// startpoint-splitpoint and splitpoint-endpoint sequences\n\t\tsegment = fitseq(path, ltreshold, qtreshold, seqstart, splitpoint);\n\t\tsegment.addAll(fitseq(path, ltreshold, qtreshold, splitpoint, seqend));\n\t\treturn segment;\n\n\t}// End of fitseq()\n\n\t// 5. Batch tracing paths\n\tpublic static ArrayList<ArrayList<Double[]>> batchtracepaths(ArrayList<ArrayList<Double[]>> internodepaths,\n\t\t\tfloat ltres, float qtres) {\n\t\tArrayList<ArrayList<Double[]>> btracedpaths = new ArrayList<ArrayList<Double[]>>();\n\t\tfor (int k = 0; k < internodepaths.size(); k++) {\n\t\t\tbtracedpaths.add(tracepath(internodepaths.get(k), ltres, qtres));\n\t\t}\n\t\treturn btracedpaths;\n\t}\n\n\t// 5. Batch tracing layers\n\tpublic static ArrayList<ArrayList<ArrayList<Double[]>>> batchtracelayers(\n\t\t\tArrayList<ArrayList<ArrayList<Double[]>>> binternodes, float ltres, float qtres) {\n\t\tArrayList<ArrayList<ArrayList<Double[]>>> btbis = new ArrayList<ArrayList<ArrayList<Double[]>>>();\n\t\tfor (int k = 0; k < binternodes.size(); k++) {\n\t\t\tbtbis.add(batchtracepaths(binternodes.get(k), ltres, qtres));\n\t\t}\n\t\treturn btbis;\n\t}\n\n\t////////////////////////////////////////////////////////////\n\t//\n\t// SVG Drawing functions\n\t//\n\t////////////////////////////////////////////////////////////\n\n\tpublic static float roundtodec(float val, float places) {\n\t\treturn (float) (Math.round(val * Math.pow(10, places)) / Math.pow(10, places));\n\t}\n\n\t// Getting SVG path element string from a traced path\n\tpublic static void svgpathstring(StringBuilder sb, String desc, ArrayList<Double[]> segments, String colorstr,\n\t\t\tHashMap<String, Float> options) {\n\t\tfloat scale = options.get(\"scale\");\n\t\tfloat lcpr = options.get(\"lcpr\");\n\t\tfloat qcpr = options.get(\"qcpr\");\n\t\tfloat roundcoords = (float) Math.floor(options.get(\"roundcoords\"));\n\t\t// Path\n\t\tsb.append(\"<path \").append(desc).append(colorstr).append(\"d=\\\"\").append(\"M \").append(segments.get(0)[1] * scale)\n\t\t\t\t.append(\" \").append(segments.get(0)[2] * scale).append(\" \");\n\n\t\tif (roundcoords == -1) {\n\t\t\tfor (int pcnt = 0; pcnt < segments.size(); pcnt++) {\n\t\t\t\tif (segments.get(pcnt)[0] == 1.0) {\n\t\t\t\t\tsb.append(\"L \").append(segments.get(pcnt)[3] * scale).append(\" \")\n\t\t\t\t\t\t\t.append(segments.get(pcnt)[4] * scale).append(\" \");\n\t\t\t\t} else {\n\t\t\t\t\tsb.append(\"Q \").append(segments.get(pcnt)[3] * scale).append(\" \")\n\t\t\t\t\t\t\t.append(segments.get(pcnt)[4] * scale).append(\" \").append(segments.get(pcnt)[5] * scale)\n\t\t\t\t\t\t\t.append(\" \").append(segments.get(pcnt)[6] * scale).append(\" \");\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tfor (int pcnt = 0; pcnt < segments.size(); pcnt++) {\n\t\t\t\tif (segments.get(pcnt)[0] == 1.0) {\n\t\t\t\t\tsb.append(\"L \").append(roundtodec((float) (segments.get(pcnt)[3] * scale), roundcoords)).append(\" \")\n\t\t\t\t\t\t\t.append(roundtodec((float) (segments.get(pcnt)[4] * scale), roundcoords)).append(\" \");\n\t\t\t\t} else {\n\t\t\t\t\tsb.append(\"Q \").append(roundtodec((float) (segments.get(pcnt)[3] * scale), roundcoords)).append(\" \")\n\t\t\t\t\t\t\t.append(roundtodec((float) (segments.get(pcnt)[4] * scale), roundcoords)).append(\" \")\n\t\t\t\t\t\t\t.append(roundtodec((float) (segments.get(pcnt)[5] * scale), roundcoords)).append(\" \")\n\t\t\t\t\t\t\t.append(roundtodec((float) (segments.get(pcnt)[6] * scale), roundcoords)).append(\" \");\n\t\t\t\t}\n\t\t\t}\n\t\t} // End of roundcoords check\n\n\t\tsb.append(\"Z\\\" />\");\n\n\t\t// Rendering control points\n\t\tfor (int pcnt = 0; pcnt < segments.size(); pcnt++) {\n\t\t\tif ((lcpr > 0) && (segments.get(pcnt)[0] == 1.0)) {\n\t\t\t\tsb.append(\"<circle cx=\\\"\").append(segments.get(pcnt)[3] * scale).append(\"\\\" cy=\\\"\")\n\t\t\t\t\t\t.append(segments.get(pcnt)[4] * scale).append(\"\\\" r=\\\"\").append(lcpr)\n\t\t\t\t\t\t.append(\"\\\" fill=\\\"white\\\" stroke-width=\\\"\").append(lcpr * 0.2)\n\t\t\t\t\t\t.append(\"\\\" stroke=\\\"black\\\" />\");\n\t\t\t}\n\t\t\tif ((qcpr > 0) && (segments.get(pcnt)[0] == 2.0)) {\n\t\t\t\tsb.append(\"<circle cx=\\\"\").append(segments.get(pcnt)[3] * scale).append(\"\\\" cy=\\\"\")\n\t\t\t\t\t\t.append(segments.get(pcnt)[4] * scale).append(\"\\\" r=\\\"\").append(qcpr)\n\t\t\t\t\t\t.append(\"\\\" fill=\\\"cyan\\\" stroke-width=\\\"\").append(qcpr * 0.2).append(\"\\\" stroke=\\\"black\\\" />\");\n\t\t\t\tsb.append(\"<circle cx=\\\"\").append(segments.get(pcnt)[5] * scale).append(\"\\\" cy=\\\"\")\n\t\t\t\t\t\t.append(segments.get(pcnt)[6] * scale).append(\"\\\" r=\\\"\").append(qcpr)\n\t\t\t\t\t\t.append(\"\\\" fill=\\\"white\\\" stroke-width=\\\"\").append(qcpr * 0.2)\n\t\t\t\t\t\t.append(\"\\\" stroke=\\\"black\\\" />\");\n\t\t\t\tsb.append(\"<line x1=\\\"\").append(segments.get(pcnt)[1] * scale).append(\"\\\" y1=\\\"\")\n\t\t\t\t\t\t.append(segments.get(pcnt)[2] * scale).append(\"\\\" x2=\\\"\").append(segments.get(pcnt)[3] * scale)\n\t\t\t\t\t\t.append(\"\\\" y2=\\\"\").append(segments.get(pcnt)[4] * scale).append(\"\\\" stroke-width=\\\"\")\n\t\t\t\t\t\t.append(qcpr * 0.2).append(\"\\\" stroke=\\\"cyan\\\" />\");\n\t\t\t\tsb.append(\"<line x1=\\\"\").append(segments.get(pcnt)[3] * scale).append(\"\\\" y1=\\\"\")\n\t\t\t\t\t\t.append(segments.get(pcnt)[4] * scale).append(\"\\\" x2=\\\"\").append(segments.get(pcnt)[5] * scale)\n\t\t\t\t\t\t.append(\"\\\" y2=\\\"\").append(segments.get(pcnt)[6] * scale).append(\"\\\" stroke-width=\\\"\")\n\t\t\t\t\t\t.append(qcpr * 0.2).append(\"\\\" stroke=\\\"cyan\\\" />\");\n\t\t\t} // End of quadratic control points\n\t\t}\n\n\t}// End of svgpathstring()\n\n\t// Converting tracedata to an SVG string, paths are drawn according to a Z-index\n\t// the optional lcpr and qcpr are linear and quadratic control point radiuses\n\tpublic static String getsvgstring(IndexedImage ii, HashMap<String, Float> options) {\n\t\toptions = checkoptions(options);\n\t\t// SVG start\n\t\tint w = (int) (ii.width * options.get(\"scale\")), h = (int) (ii.height * options.get(\"scale\"));\n\t\tString viewboxorviewport = options.get(\"viewbox\") != 0\n\t\t\t\t? \"viewBox=\\\"0 0 \" + w + \" \" + h + \"\\\" \"\n\t\t\t\t: \"width=\\\"\" + w + \"\\\" height=\\\"\" + h + \"\\\" \";\n\t\tStringBuilder svgstr = new StringBuilder(\n\t\t\t\t\"<svg \" + viewboxorviewport + \"version=\\\"1.1\\\" xmlns=\\\"http://www.w3.org/2000/svg\\\" \");\n\t\tif (options.get(\"desc\") != 0) {\n\t\t\tsvgstr.append(\"desc=\\\"Created with ImageTracer.java version \" + ImageTracer.versionnumber + \"\\\" \");\n\t\t}\n\t\tsvgstr.append(\">\");\n\n\t\t// creating Z-index\n\t\tTreeMap<Double, Integer[]> zindex = new TreeMap<Double, Integer[]>();\n\t\tdouble label;\n\t\t// Layer loop\n\t\tfor (int k = 0; k < ii.layers.size(); k++) {\n\n\t\t\t// Path loop\n\t\t\tfor (int pcnt = 0; pcnt < ii.layers.get(k).size(); pcnt++) {\n\n\t\t\t\t// Label (Z-index key) is the startpoint of the path, linearized\n\t\t\t\tlabel = (ii.layers.get(k).get(pcnt).get(0)[2] * w) + ii.layers.get(k).get(pcnt).get(0)[1];\n\t\t\t\t// Creating new list if required\n\t\t\t\tif (!zindex.containsKey(label)) {\n\t\t\t\t\tzindex.put(label, new Integer[2]);\n\t\t\t\t}\n\t\t\t\t// Adding layer and path number to list\n\t\t\t\tzindex.get(label)[0] = k;\n\t\t\t\tzindex.get(label)[1] = pcnt;\n\t\t\t} // End of path loop\n\n\t\t} // End of layer loop\n\n\t\t// Sorting Z-index is not required, TreeMap is sorted automatically\n\n\t\t// Drawing\n\t\t// Z-index loop\n\t\tString thisdesc = \"\";\n\t\tfor (Entry<Double, Integer[]> entry : zindex.entrySet()) {\n\t\t\tif (options.get(\"desc\") != 0) {\n\t\t\t\tthisdesc = \"desc=\\\"l \" + entry.getValue()[0] + \" p \" + entry.getValue()[1] + \"\\\" \";\n\t\t\t} else {\n\t\t\t\tthisdesc = \"\";\n\t\t\t}\n\t\t\tsvgpathstring(svgstr, thisdesc, ii.layers.get(entry.getValue()[0]).get(entry.getValue()[1]),\n\t\t\t\t\ttosvgcolorstr(ii.palette[entry.getValue()[0]]), options);\n\t\t}\n\n\t\t// SVG End\n\t\tsvgstr.append(\"</svg>\");\n\n\t\treturn svgstr.toString();\n\n\t}// End of getsvgstring()\n\n\tstatic String tosvgcolorstr(byte[] c) {\n\t\t// hack this to export cuttable files\n\t\t// return \"fill=\\\"rgb(\"+(c[0]+128)+\",\"+(c[1]+128)+\",\"+(c[2]+128)+\")\\\"\n\t\t// stroke=\\\"rgb(\"+(c[0]+128)+\",\"+(c[1]+128)+\",\"+(c[2]+128)+\")\\\"\n\t\t// stroke-width=\\\"1\\\" opacity=\\\"\"+((c[3]+128)/255.0)+\"\\\" \";\n\n\t\treturn \"fill=\\\"none\\\" stroke=\\\"rgb(\" + (0) + \",\" + (0) + \",\" + (0) + \")\\\" stroke-width=\\\"0.354\\\" opacity=\\\"\" + 1\n\t\t\t\t+ \"\\\" \";\n\t}\n\n\t// Gaussian kernels for blur\n\tstatic double[][] gks = {{0.27901, 0.44198, 0.27901}, {0.135336, 0.228569, 0.272192, 0.228569, 0.135336},\n\t\t\t{0.086776, 0.136394, 0.178908, 0.195843, 0.178908, 0.136394, 0.086776},\n\t\t\t{0.063327, 0.093095, 0.122589, 0.144599, 0.152781, 0.144599, 0.122589, 0.093095, 0.063327},\n\t\t\t{0.049692, 0.069304, 0.089767, 0.107988, 0.120651, 0.125194, 0.120651, 0.107988, 0.089767, 0.069304,\n\t\t\t\t\t0.049692}};\n\n\t// Selective Gaussian blur for preprocessing\n\tstatic ImageData blur(ImageData imgd, float rad, float del) {\n\t\tint i, j, k, d, idx;\n\t\tdouble racc, gacc, bacc, aacc, wacc;\n\t\tImageData imgd2 = new ImageData(imgd.width, imgd.height, new byte[imgd.width * imgd.height * 4]);\n\n\t\t// radius and delta limits, this kernel\n\t\tint radius = (int) Math.floor(rad);\n\t\tif (radius < 1) {\n\t\t\treturn imgd;\n\t\t}\n\t\tif (radius > 5) {\n\t\t\tradius = 5;\n\t\t}\n\t\tint delta = (int) Math.abs(del);\n\t\tif (delta > 1024) {\n\t\t\tdelta = 1024;\n\t\t}\n\t\tdouble[] thisgk = gks[radius - 1];\n\n\t\t// loop through all pixels, horizontal blur\n\t\tfor (j = 0; j < imgd.height; j++) {\n\t\t\tfor (i = 0; i < imgd.width; i++) {\n\n\t\t\t\tracc = 0;\n\t\t\t\tgacc = 0;\n\t\t\t\tbacc = 0;\n\t\t\t\taacc = 0;\n\t\t\t\twacc = 0;\n\t\t\t\t// gauss kernel loop\n\t\t\t\tfor (k = -radius; k < (radius + 1); k++) {\n\t\t\t\t\t// add weighted color values\n\t\t\t\t\tif (((i + k) > 0) && ((i + k) < imgd.width)) {\n\t\t\t\t\t\tidx = ((j * imgd.width) + i + k) * 4;\n\t\t\t\t\t\tracc += imgd.data[idx] * thisgk[k + radius];\n\t\t\t\t\t\tgacc += imgd.data[idx + 1] * thisgk[k + radius];\n\t\t\t\t\t\tbacc += imgd.data[idx + 2] * thisgk[k + radius];\n\t\t\t\t\t\taacc += imgd.data[idx + 3] * thisgk[k + radius];\n\t\t\t\t\t\twacc += thisgk[k + radius];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// The new pixel\n\t\t\t\tidx = ((j * imgd.width) + i) * 4;\n\t\t\t\timgd2.data[idx] = (byte) Math.floor(racc / wacc);\n\t\t\t\timgd2.data[idx + 1] = (byte) Math.floor(gacc / wacc);\n\t\t\t\timgd2.data[idx + 2] = (byte) Math.floor(bacc / wacc);\n\t\t\t\timgd2.data[idx + 3] = (byte) Math.floor(aacc / wacc);\n\n\t\t\t} // End of width loop\n\t\t} // End of horizontal blur\n\n\t\t// copying the half blurred imgd2\n\t\tbyte[] himgd = imgd2.data.clone();\n\n\t\t// loop through all pixels, vertical blur\n\t\tfor (j = 0; j < imgd.height; j++) {\n\t\t\tfor (i = 0; i < imgd.width; i++) {\n\n\t\t\t\tracc = 0;\n\t\t\t\tgacc = 0;\n\t\t\t\tbacc = 0;\n\t\t\t\taacc = 0;\n\t\t\t\twacc = 0;\n\t\t\t\t// gauss kernel loop\n\t\t\t\tfor (k = -radius; k < (radius + 1); k++) {\n\t\t\t\t\t// add weighted color values\n\t\t\t\t\tif (((j + k) > 0) && ((j + k) < imgd.height)) {\n\t\t\t\t\t\tidx = (((j + k) * imgd.width) + i) * 4;\n\t\t\t\t\t\tracc += himgd[idx] * thisgk[k + radius];\n\t\t\t\t\t\tgacc += himgd[idx + 1] * thisgk[k + radius];\n\t\t\t\t\t\tbacc += himgd[idx + 2] * thisgk[k + radius];\n\t\t\t\t\t\taacc += himgd[idx + 3] * thisgk[k + radius];\n\t\t\t\t\t\twacc += thisgk[k + radius];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// The new pixel\n\t\t\t\tidx = ((j * imgd.width) + i) * 4;\n\t\t\t\timgd2.data[idx] = (byte) Math.floor(racc / wacc);\n\t\t\t\timgd2.data[idx + 1] = (byte) Math.floor(gacc / wacc);\n\t\t\t\timgd2.data[idx + 2] = (byte) Math.floor(bacc / wacc);\n\t\t\t\timgd2.data[idx + 3] = (byte) Math.floor(aacc / wacc);\n\n\t\t\t} // End of width loop\n\t\t} // End of vertical blur\n\n\t\t// Selective blur: loop through all pixels\n\t\tfor (j = 0; j < imgd.height; j++) {\n\t\t\tfor (i = 0; i < imgd.width; i++) {\n\n\t\t\t\tidx = ((j * imgd.width) + i) * 4;\n\t\t\t\t// d is the difference between the blurred and the original pixel\n\t\t\t\td = Math.abs(imgd2.data[idx] - imgd.data[idx]) + Math.abs(imgd2.data[idx + 1] - imgd.data[idx + 1])\n\t\t\t\t\t\t+ Math.abs(imgd2.data[idx + 2] - imgd.data[idx + 2])\n\t\t\t\t\t\t+ Math.abs(imgd2.data[idx + 3] - imgd.data[idx + 3]);\n\t\t\t\t// selective blur: if d>delta, put the original pixel back\n\t\t\t\tif (d > delta) {\n\t\t\t\t\timgd2.data[idx] = imgd.data[idx];\n\t\t\t\t\timgd2.data[idx + 1] = imgd.data[idx + 1];\n\t\t\t\t\timgd2.data[idx + 2] = imgd.data[idx + 2];\n\t\t\t\t\timgd2.data[idx + 3] = imgd.data[idx + 3];\n\t\t\t\t}\n\t\t\t}\n\t\t} // End of Selective blur\n\n\t\treturn imgd2;\n\n\t}// End of blur()\n\n}// End of ImageTracer class\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/bowlerstudio/utils/SVGFactory.java",
    "content": "package com.neuronrobotics.bowlerstudio.utils;\n\nimport java.io.File;\n\nimport com.neuronrobotics.nrconsole.util.FileSelectionFactory;\nimport eu.mihosoft.vrl.v3d.CSG;\nimport eu.mihosoft.vrl.v3d.svg.SVGExporter;\n\n@SuppressWarnings(\"restriction\")\npublic class SVGFactory {\n\n\tpublic static File exportSVG(CSG currentCsg, File defaultDir) {\n\t\tcom.neuronrobotics.sdk.common.Log.error(\"Starting SVG ...\");\n\n\t\tFile baseDirForFiles = FileSelectionFactory.GetFile(defaultDir, true);\n\n\t\tif (!baseDirForFiles.getAbsolutePath().toLowerCase().endsWith(\".svg\"))\n\t\t\tbaseDirForFiles = new File(baseDirForFiles.getAbsolutePath() + \".svg\");\n\t\ttry {\n\t\t\tSVGExporter.export(currentCsg, baseDirForFiles);\n\t\t} catch (Exception e) {\n\t\t\t// Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t}\n\t\tcom.neuronrobotics.sdk.common.Log.error(\"SVG at \" + baseDirForFiles);\n\t\treturn baseDirForFiles.getParentFile();\n\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/graphing/CSVWriter.java",
    "content": "package com.neuronrobotics.graphing;\n\nimport java.io.BufferedWriter;\nimport java.io.File;\nimport java.io.FileWriter;\nimport java.io.IOException;\nimport java.util.ArrayList;\n\nimport javax.swing.JOptionPane;\n\npublic class CSVWriter {\n\tprivate CSVWriter() {\n\t}\n\n\tpublic static void WriteToCSV(ArrayList<GraphDataElement> dataTable, String filename) {\n\t\tString out = \"\";\n\t\tsynchronized (dataTable) {\n\t\t\tfor (int j = 0; j < dataTable.size(); j++) {\n\t\t\t\tout += dataTable.get(j).getTimestamp();\n\t\t\t\tfor (int i = 0; i < dataTable.get(j).getData().length; i++)\n\t\t\t\t\tout += \",\" + dataTable.get(j).getData()[i];\n\n\t\t\t\tout += \"\\r\\n\";\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\t// Create file\n\t\t\tFileWriter fstream = new FileWriter(filename);\n\t\t\tBufferedWriter outPut = new BufferedWriter(fstream);\n\t\t\toutPut.write(out);\n\t\t\t// Close the output stream\n\t\t\toutPut.close();\n\t\t} catch (Exception e) {// Catch exception if any\n\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Error: \" + e.getMessage());\n\t\t}\n\t\tFile dir1 = new File(\".\");\n\n\t\ttry {\n\t\t\tString dir;\n\t\t\tif (System.getProperty(\"os.name\").toLowerCase().startsWith(\"windows\"))\n\t\t\t\tdir = dir1.getCanonicalPath() + \"\\\\\";\n\t\t\telse\n\t\t\t\tdir = dir1.getCanonicalPath() + \"/\";\n\n\t\t\tJOptionPane.showMessageDialog(null, \"Saved data to file: \" + dir + filename, \"PID Save\",\n\t\t\t\t\tJOptionPane.INFORMATION_MESSAGE);\n\t\t} catch (IOException e) {\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/graphing/DataChannel.java",
    "content": "package com.neuronrobotics.graphing;\n\nimport org.jfree.data.xy.XYSeries;\n\npublic class DataChannel {\n\tprivate String title;\n\tprivate XYSeries series;\n\tprivate static long startTime = System.currentTimeMillis();\n\tprivate long lastTime = System.currentTimeMillis();\n\n\tpublic DataChannel(String title) {\n\t\tthis.title = title;\n\t\tseries = new XYSeries(toString());\n\t}\n\n\tpublic String toString() {\n\t\treturn title;\n\t}\n\n\tpublic void graphValue(double value) {\n\t\tif ((lastTime + 100) > System.currentTimeMillis())\n\t\t\treturn;\n\t\tlastTime = System.currentTimeMillis();\n\t\t// try{\n\t\t// Platform.runLater(()-> {\n\t\t// long time = System.currentTimeMillis() - startTime ;\n\t\t// if(series != null)\n\t\t// series.add((double) time/1000, value);\n\t\t// while(series.getItemCount()>3000){\n\t\t// Platform.runLater(()-> {\n\t\t// series.remove(0);\n\t\t// });\n\t\t// }\n\t\t// });\n\t\t// }catch(IllegalStateException ex){\n\t\t// //application not yet loaded\n\t\t// }\n\t}\n\n\tpublic XYSeries getSeries() {\n\t\treturn series;\n\t}\n\n\tpublic static void restart() {\n\t\tstartTime = System.currentTimeMillis();\n\t}\n\n\tpublic void clear() {\n\t\tseries.clear();\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/graphing/DataWriter.java",
    "content": "package com.neuronrobotics.graphing;\n\nimport java.io.File;\n\npublic interface DataWriter {\n\tpublic void setFile(File f);\n\n\tpublic void addData(DataChannel c);\n\n\tpublic void cleanup();\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/graphing/ExcelWriter.java",
    "content": "package com.neuronrobotics.graphing;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.Locale;\n\nimport jxl.Workbook;\nimport jxl.WorkbookSettings;\nimport jxl.write.Label;\nimport jxl.write.Number;\nimport jxl.write.WritableSheet;\nimport jxl.write.WritableWorkbook;\nimport jxl.write.WriteException;\nimport jxl.write.biff.RowsExceededException;\n\nimport org.jfree.data.xy.XYDataItem;\n\npublic class ExcelWriter implements DataWriter {\n\n\tprivate WorkbookSettings wbSettings = new WorkbookSettings();\n\tprivate WritableWorkbook workbook;\n\tprivate WritableSheet excelSheet;\n\tprivate int lineOffset = 0;\n\tpublic ExcelWriter() {\n\t\twbSettings.setLocale(Locale.of(\"en\", \"EN\"));\n\t}\n\n\tprivate void addNumber(int column, int row, double d) throws WriteException, RowsExceededException {\n\t\tNumber number;\n\t\tnumber = new Number(column, row, d);\n\t\texcelSheet.addCell(number);\n\t}\n\n\tprivate void addLabel(int column, int row, String s) throws WriteException, RowsExceededException {\n\t\tLabel label;\n\t\tlabel = new Label(column, row, s);\n\t\texcelSheet.addCell(label);\n\t}\n\n\tpublic void setFile(File f) {\n\t\ttry {\n\t\t\tworkbook = Workbook.createWorkbook(f, wbSettings);\n\t\t\tworkbook.createSheet(\"Data\", 0);\n\t\t\texcelSheet = workbook.getSheet(0);\n\t\t} catch (IOException e) {\n\t\t\t// Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t}\n\t}\n\n\tpublic void addData(DataChannel c) {\n\t\ttry {\n\t\t\tint col = 1;\n\t\t\taddLabel(lineOffset, 0, c.toString() + \" Time (ms)\");\n\t\t\taddLabel(lineOffset + 1, 0, c.toString() + \" Value\");\n\n\t\t\tfor (Object o : c.getSeries().getItems()) {\n\t\t\t\tXYDataItem i = (XYDataItem) o;\n\t\t\t\taddNumber(lineOffset, col, i.getXValue());\n\t\t\t\taddNumber(lineOffset + 1, col, i.getYValue());\n\t\t\t\tcol++;\n\t\t\t}\n\t\t} catch (RowsExceededException e) {\n\t\t\t// Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t} catch (WriteException e) {\n\t\t\t// Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t}\n\t\tlineOffset += 2;\n\t}\n\n\tpublic void cleanup() {\n\t\ttry {\n\t\t\tworkbook.write();\n\t\t\tworkbook.close();\n\t\t} catch (Exception e) {\n\t\t\t// Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/graphing/GraphDataElement.java",
    "content": "package com.neuronrobotics.graphing;\n\npublic class GraphDataElement {\n\tprivate long ms;\n\tprivate double[] data;\n\tpublic GraphDataElement(long currentTimeMillis, double[] data) {\n\t\tsetTimestamp(currentTimeMillis);\n\t\tthis.setData(data);\n\t}\n\n\tpublic void setData(double[] data) {\n\t\tthis.data = data;\n\t}\n\n\tpublic double[] getData() {\n\t\treturn data;\n\t}\n\n\tpublic void setTimestamp(long ms) {\n\t\tthis.ms = ms;\n\t}\n\n\tpublic long getTimestamp() {\n\t\treturn ms;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/graphing/GraphingOptionsDialog.java",
    "content": "package com.neuronrobotics.graphing;\n\nimport javax.swing.JFrame;\nimport javax.swing.JLabel;\n\nimport net.miginfocom.swing.MigLayout;\n\npublic class GraphingOptionsDialog extends JFrame {\n\t/**\n\t *\n\t */\n\tprivate static final long serialVersionUID = 1L;\n\t@SuppressWarnings(\"unused\")\n\tprivate GraphingWindow window;\n\n\tpublic GraphingOptionsDialog(GraphingWindow window) {\n\t\tthis.window = window;\n\t\tsetLayout(new MigLayout());\n\t\tsetTitle(\"Graphing Options\");\n\n\t\tadd(new JLabel(\"Graphing Options\"));\n\n\t\tpack();\n\t\tsetLocationRelativeTo(null);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/graphing/GraphingWindow.java",
    "content": "package com.neuronrobotics.graphing;\n\nimport java.awt.BorderLayout;\nimport java.awt.Dimension;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.awt.event.KeyEvent;\nimport java.awt.event.KeyListener;\nimport java.util.ArrayList;\n\nimport javax.swing.JButton;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\nimport javax.swing.JSlider;\nimport javax.swing.JTextField;\nimport javax.swing.event.ChangeEvent;\nimport javax.swing.event.ChangeListener;\n\nimport net.miginfocom.swing.MigLayout;\n\nimport org.jfree.chart.ChartFactory;\nimport org.jfree.chart.ChartPanel;\nimport org.jfree.chart.JFreeChart;\nimport org.jfree.chart.axis.ValueAxis;\nimport org.jfree.chart.plot.PlotOrientation;\nimport org.jfree.chart.plot.XYPlot;\nimport org.jfree.data.Range;\nimport org.jfree.data.xy.XYSeriesCollection;\n\npublic class GraphingWindow extends JPanel {\n\tprivate XYSeriesCollection xyDataset;\n\tprivate ChartPanel chartPanel;\n\tprivate ValueAxis axis;\n\tprivate JTextField length = new JTextField(5);\n\tprivate JSlider window = new JSlider(1, 100);\n\tprivate JSlider scale = new JSlider(1, 100);\n\tprivate ArrayList<DataChannel> dataChannels = new ArrayList<>();\n\n\t/**\n\t * long\n\t */\n\tprivate static final long serialVersionUID = 2171583604829088880L;\n\tpublic GraphingWindow() {\n\t\tsetName(\"DyIO Graph\");\n\t\txyDataset = new XYSeriesCollection();\n\n\t\tJFreeChart chart = ChartFactory.createXYLineChart(\"Live Data\", \"Time\", \"Value\", xyDataset,\n\t\t\t\tPlotOrientation.VERTICAL, true, false, false);\n\n\t\tchartPanel = new ChartPanel(chart);\n\n\t\tXYPlot plot = (XYPlot) chart.getPlot();\n\t\taxis = plot.getDomainAxis();\n\t\tscale.setValue(100);\n\t\tsetDefaultWindow();\n\n\t\tscale.addChangeListener(new ChangeListener() {\n\t\t\tpublic void stateChanged(ChangeEvent e) {\n\t\t\t\tif (window.getValue() == 100) {\n\t\t\t\t\tsetDefaultWindow();\n\t\t\t\t} else {\n\t\t\t\t\tsetMovedWindow(window.getValue());\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\twindow.setValue(100);\n\t\twindow.addChangeListener(new ChangeListener() {\n\t\t\tpublic void stateChanged(ChangeEvent e) {\n\t\t\t\tif (window.getValue() == 100) {\n\t\t\t\t\tsetDefaultWindow();\n\t\t\t\t} else {\n\t\t\t\t\tsetMovedWindow(window.getValue());\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tlength.addKeyListener(new KeyListener() {\n\n\t\t\tpublic void keyTyped(KeyEvent e) {\n\t\t\t\tif (e.getKeyChar() != '\\n' && (e.getKeyChar() < '0' || e.getKeyChar() > '9')) {\n\t\t\t\t\te.consume();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpublic void keyReleased(KeyEvent e) {\n\n\t\t\t}\n\n\t\t\tpublic void keyPressed(KeyEvent e) {\n\n\t\t\t}\n\t\t});\n\t\tlength.addActionListener(new ActionListener() {\n\n\t\t\tpublic void actionPerformed(ActionEvent arg0) {\n\t\t\t\tint value;\n\t\t\t\ttry {\n\t\t\t\t\tvalue = Integer.parseInt(length.getText());\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tvalue = scale.getMaximum();\n\t\t\t\t}\n\n\t\t\t\taxis.setFixedAutoRange(value);\n\t\t\t\tlength.setText(String.valueOf(value));\n\t\t\t\tscale.setValue(value);\n\n\t\t\t\tinvalidate();\n\t\t\t\trepaint();\n\t\t\t}\n\t\t});\n\n\t\tJButton clearBtn = new JButton(\"Clear Data\");\n\t\tclearBtn.addActionListener(new ActionListener() {\n\n\t\t\tpublic void actionPerformed(ActionEvent arg0) {\n\t\t\t\tfor (DataChannel dc : dataChannels) {\n\t\t\t\t\tdc.clear();\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tJPanel options = new JPanel(new MigLayout());\n\t\toptions.add(new JLabel(\"Range Size:\"));\n\t\toptions.add(scale);\n\t\toptions.add(length);\n\t\toptions.add(new JLabel(\"seconds\"));\n\t\toptions.add(clearBtn, \"east\");\n\n\t\tsetLayout(new BorderLayout());\n\t\tsetSize(new Dimension(500, 400));\n\t\tadd(chartPanel, BorderLayout.CENTER);\n\t\tadd(options, BorderLayout.SOUTH);\n\n\t\tJPanel opt = new JPanel(new MigLayout());\n\t\topt.add(new JLabel(\"View Window\"));\n\t\topt.add(window);\n\n\t\tJPanel slidingWindow = new JPanel(new MigLayout());\n\t\tslidingWindow.add(options, \"wrap\");\n\t\tslidingWindow.add(opt, \"wrap\");\n\t\tadd(slidingWindow, BorderLayout.SOUTH);\n\t}\n\n\tprivate void setDefaultWindow() {\n\n\t\taxis.setAutoRange(true);\n\t\taxis.setFixedAutoRange(scale.getValue());\n\t\tlength.setText(String.valueOf(scale.getValue()));\n\t\t// invalidate();\n\t\trepaint();\n\t}\n\n\tprivate void setMovedWindow(double percent) {\n\n\t\taxis.setAutoRange(false);\n\t\tRange total = xyDataset.getDomainBounds(true);\n\t\tdouble lower = total.getLowerBound();\n\t\tdouble upper = total.getUpperBound();\n\t\tdouble loc = (upper - lower) * percent / 100;\n\n\t\tdouble sLower = loc - (scale.getValue() / 2);\n\t\tdouble sUpper = loc + (scale.getValue() / 2);\n\t\taxis.setRange(sLower, sUpper);\n\t\tlength.setText(String.valueOf(scale.getValue()));\n\t\t// invalidate();\n\t\t// repaint();\n\t}\n\n\tpublic void addDataset(DataChannel data) {\n\t\tif (!dataChannels.contains(data)) {\n\t\t\tdataChannels.add(data);\n\t\t}\n\n\t\tif (!(xyDataset.indexOf(data.getSeries()) > -1)) {\n\t\t\tshowDataChannel(data);\n\t\t}\n\t}\n\n\tpublic void removeDataset(DataChannel data) {\n\t\tif (!dataChannels.contains(data)) {\n\t\t\tdataChannels.add(data);\n\t\t}\n\n\t\tif (xyDataset.indexOf(data.getSeries()) > -1) {\n\t\t\thideDataChannel(data);\n\t\t}\n\t}\n\n\tpublic void hideDataChannel(DataChannel data) {\n\t\txyDataset.removeSeries(data.getSeries());\n\t}\n\n\tpublic void showDataChannel(DataChannel data) {\n\t\txyDataset.addSeries(data.getSeries());\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/nrconsole/plugin/BowlerCam/BowlerCamController.java",
    "content": "package com.neuronrobotics.nrconsole.plugin.BowlerCam;\n\nimport javafx.embed.swing.SwingNode;\nimport javafx.scene.control.ScrollPane;\n\nimport com.neuronrobotics.bowlerstudio.tabs.AbstractBowlerStudioTab;\nimport com.neuronrobotics.sdk.bowlercam.device.BowlerCamDevice;\nimport com.neuronrobotics.sdk.common.BowlerAbstractDevice;\n\npublic class BowlerCamController extends AbstractBowlerStudioTab {\n\n\tprivate BowlerCamPanel bcp = new BowlerCamPanel();\n\n\t@Override\n\tpublic void onTabClosing() {\n\t\t// Auto-generated method stub\n\n\t}\n\n\t@Override\n\tpublic String[] getMyNameSpaces() {\n\t\t// Auto-generated method stub\n\t\treturn new String[]{\"neuronrobotics.bowlercam.*\"};\n\t}\n\n\t@Override\n\tpublic void initializeUI(BowlerAbstractDevice pm) {\n\n\t\tbcp.setConnection((BowlerCamDevice) pm);\n\n\t\tSwingNode sn = new SwingNode();\n\t\tsn.setContent(bcp);\n\t\tScrollPane s1 = new ScrollPane();\n\n\t\ts1.setContent(sn);\n\t\tsetContent(s1);\n\t\tsetText(\"BowlerCam Control\");\n\t\tonTabReOpening();\n\t}\n\n\t@Override\n\tpublic void onTabReOpening() {\n\t\t// Auto-generated method stub\n\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/nrconsole/plugin/BowlerCam/BowlerCamPanel.java",
    "content": "package com.neuronrobotics.nrconsole.plugin.BowlerCam;\n\nimport java.awt.Color;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.awt.event.MouseEvent;\nimport java.awt.event.MouseListener;\nimport java.awt.image.BufferedImage;\nimport java.text.NumberFormat;\n\nimport javax.swing.ImageIcon;\nimport javax.swing.JButton;\nimport javax.swing.JCheckBox;\nimport javax.swing.JFormattedTextField;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\nimport javax.swing.JSlider;\nimport javax.swing.SwingConstants;\n\nimport net.miginfocom.swing.MigLayout;\n\nimport com.neuronrobotics.sdk.bowlercam.device.BowlerCamDevice;\nimport com.neuronrobotics.sdk.bowlercam.device.IWebcamImageListener;\nimport com.neuronrobotics.sdk.util.ThreadUtil;\n\npublic class BowlerCamPanel extends JPanel implements IWebcamImageListener {\n\t/**\n\t * long\n\t */\n\tprivate static final long serialVersionUID = 8710018539364791015L;\n\tprivate BowlerCamDevice cam = new BowlerCamDevice();\n\tprivate JPanel directPanel = new JPanel(new MigLayout());\n\tprivate JPanel bcPanel = new JPanel(new MigLayout());\n\tprivate JPanel images = new JPanel(new MigLayout());\n\tprivate JPanel controls = new JPanel(new MigLayout());\n\tprivate JPanel sliders = new JPanel(new MigLayout());\n\tprivate JLabel fps = new JLabel(\"FPS: \");\n\tprivate JLabel thr = new JLabel(\"\");\n\n\tprivate RGBSlider target = new RGBSlider(\"Target Color\");\n\t// private RGBSlider vector = new RGBSlider(\"Vector Color\");\n\tprivate JSlider threshhold = new JSlider(SwingConstants.HORIZONTAL, 0, 255, 104);\n\tprivate JFormattedTextField min = new JFormattedTextField(NumberFormat.getNumberInstance());\n\tprivate JFormattedTextField max = new JFormattedTextField(NumberFormat.getNumberInstance());\n\tprivate JFormattedTextField scale = new JFormattedTextField(NumberFormat.getNumberInstance());\n\tprivate JCheckBox within = new JCheckBox(\"Within Threshhold\");\n\tprivate JButton update = new JButton(\"Update Processor\");\n\tprivate BufferedImage unaltered = null;\n\tprivate BufferedImage processedIm = null;\n\tprivate long time;\n\tdouble scaleSet = 1;\n\n\tpublic BowlerCamPanel() {\n\t\tsetName(\"Bowler Camera\");\n\t\timages.add(directPanel);\n\t\timages.add(bcPanel);\n\t\tbcPanel.addMouseListener(new MouseListener() {\n\n\t\t\tpublic void mouseReleased(MouseEvent arg0) {\n\t\t\t}\n\n\t\t\tpublic void mousePressed(MouseEvent arg0) {\n\t\t\t}\n\n\t\t\tpublic void mouseExited(MouseEvent arg0) {\n\t\t\t}\n\n\t\t\tpublic void mouseEntered(MouseEvent arg0) {\n\t\t\t}\n\n\t\t\tpublic void mouseClicked(MouseEvent arg0) {\n\t\t\t\tColor cl = new Color(unaltered.getRGB(arg0.getX(), arg0.getY()));\n\t\t\t\tgetTargetColor().setColor(cl);\n\t\t\t}\n\t\t});\n\n\t\tsliders.add(fps, \"wrap\");\n\t\ttarget.setColor(33, 240, 246);\n\t\tsliders.add(target);\n\t\tcontrols.add(new JLabel(\"Threshhold\"), \"wrap\");\n\t\tcontrols.add(threshhold);\n\t\tcontrols.add(thr, \"wrap\");\n\t\tthr.setText(Integer.toString(threshhold.getValue()));\n\t\tcontrols.add(within, \"wrap\");\n\t\twithin.setSelected(true);\n\n\t\tcontrols.add(new JLabel(\"Image Scale\"));\n\t\tcontrols.add(scale, \"wrap\");\n\t\tscale.setText(Double.toString(scaleSet));\n\n\t\tmin.setText(\"5\");\n\t\tmax.setText(\"100000\");\n\t\tcontrols.add(new JLabel(\"Minimum pixles per blob\"));\n\t\tcontrols.add(min, \"wrap\");\n\t\tcontrols.add(new JLabel(\"Maximum pixles per blob\"));\n\t\tcontrols.add(max, \"wrap\");\n\t\tupdate.addActionListener(new ActionListener() {\n\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\tint minimum = Integer.parseInt(min.getText());\n\t\t\t\tint maximum = Integer.parseInt(max.getText());\n\t\t\t\tcam.updateFilter(target.getColor(), threshhold.getValue(), within.isSelected(), minimum, maximum);\n\t\t\t}\n\t\t});\n\t\tcontrols.add(update);\n\n\t\tJPanel tmp2 = new JPanel(new MigLayout());\n\t\ttmp2.add(sliders);\n\t\ttmp2.add(controls);\n\t\tadd(tmp2, \"wrap\");\n\t\tadd(images, \"wrap\");\n\t}\n\n\tpublic boolean setConnection(BowlerCamDevice connection) {\n\t\ttry {\n\t\t\tcam = connection;\n\t\t\tcam.addWebcamImageListener(this);\n\t\t\tcam.startHighSpeedAutoCapture(0, scaleSet, 5);\n\t\t\tcam.startHighSpeedAutoCapture(1, scaleSet, 5);\n\t\t\tnew displayThread().start();\n\t\t} catch (Exception ex) {\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate void displayImage() {\n\t\tthr.setText(Integer.toString(threshhold.getValue()));\n\t\ttarget.getColor();\n\t\tif (scaleSet != Double.parseDouble(scale.getText())) {\n\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"Resetting scale :\n\t\t\t// \"+scale.getText());\n\t\t\tscaleSet = Double.parseDouble(scale.getText());\n\t\t\tcam.startHighSpeedAutoCapture(0, scaleSet, 0);\n\t\t}\n\t\tupdateImage(unaltered, bcPanel);\n\t\tupdateImage(processedIm, directPanel);\n\t}\n\n\tprotected RGBSlider getTargetColor() {\n\t\treturn target;\n\t}\n\n\tprivate void updateImage(BufferedImage imageUpdate, JPanel p) {\n\t\tif (imageUpdate == null)\n\t\t\treturn;\n\t\tp.removeAll();\n\t\tJLabel l = new JLabel();\n\t\tl.setIcon(new ImageIcon(imageUpdate));\n\t\tp.add(l);\n\t\tp.invalidate();\n\t}\n\n\tpublic void onNewImage(int camera, BufferedImage image) {\n\t\t// com.neuronrobotics.sdk.common.Log.error(\"Got image: \"+camera+\" at\n\t\t// \"+System.currentTimeMillis());\n\t\tif (camera == 0) {\n\t\t\tdouble s = ((double) (System.currentTimeMillis() - time)) / 1000.0;\n\t\t\tfps.setText(\"FPS: \" + (int) (1 / (s)));\n\t\t\ttime = System.currentTimeMillis();\n\t\t\tunaltered = image;\n\t\t\t// process();\n\t\t\t// displayImage();\n\t\t}\n\t\tif (camera == 1) {\n\t\t\tprocessedIm = image;\n\t\t}\n\n\t}\n\tprivate class displayThread extends Thread {\n\t\tpublic void run() {\n\t\t\twhile (cam.isAvailable()) {\n\t\t\t\tThreadUtil.wait(200);\n\t\t\t\tdisplayImage();\n\t\t\t}\n\t\t\tcam.disconnect();\n\t\t\tcam.stopAutoCapture(0);\n\t\t\tcam.stopAutoCapture(1);\n\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"Bowler cam exiting\");\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/nrconsole/plugin/BowlerCam/RGBSlider.java",
    "content": "package com.neuronrobotics.nrconsole.plugin.BowlerCam;\n\nimport java.awt.Color;\nimport java.awt.Graphics;\n\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\nimport javax.swing.JSlider;\nimport javax.swing.SwingConstants;\n\nimport net.miginfocom.swing.MigLayout;\n\npublic class RGBSlider extends JPanel {\n\tJSlider r = new JSlider(SwingConstants.VERTICAL, 0, 255, 128);\n\tJSlider g = new JSlider(SwingConstants.VERTICAL, 0, 255, 128);\n\tJSlider b = new JSlider(SwingConstants.VERTICAL, 0, 255, 128);\n\n\tJLabel rl = new JLabel(\"128\");\n\tJLabel gl = new JLabel(\"128\");\n\tJLabel bl = new JLabel(\"128\");\n\tColorBox c = new ColorBox(Color.gray);\n\t/**\n\t *\n\t */\n\tprivate static final long serialVersionUID = 1L;\n\n\tpublic RGBSlider(String name) {\n\t\tsetLayout(new MigLayout());\n\t\tr.setMajorTickSpacing(15);\n\t\tg.setMajorTickSpacing(15);\n\t\tb.setMajorTickSpacing(15);\n\n\t\tr.setPaintTicks(true);\n\t\tg.setPaintTicks(true);\n\t\tb.setPaintTicks(true);\n\n\t\tJPanel rc = new JPanel(new MigLayout());\n\t\tJPanel gc = new JPanel(new MigLayout());\n\t\tJPanel bc = new JPanel(new MigLayout());\n\n\t\trc.add(new JLabel(\"R\"));\n\t\trc.add(rl, \"wrap\");\n\t\trc.add(r);\n\n\t\tgc.add(new JLabel(\"G\"));\n\t\tgc.add(gl, \"wrap\");\n\t\tgc.add(g);\n\n\t\tbc.add(new JLabel(\"B\"));\n\t\tbc.add(bl, \"wrap\");\n\t\tbc.add(b);\n\n\t\tJPanel slides = new JPanel(new MigLayout());\n\t\tslides.add(new JLabel(name), \"wrap\");\n\t\tslides.add(rc);\n\t\tslides.add(gc);\n\t\tslides.add(bc);\n\n\t\tadd(slides, \"wrap\");\n\t\tadd(c, \"wrap\");\n\n\t}\n\n\tpublic void setColor(int r, int g, int b) {\n\t\tthis.r.setValue(r);\n\t\tthis.g.setValue(g);\n\t\tthis.b.setValue(b);\n\t\tgetColor();\n\t}\n\n\tpublic void setColor(Color c) {\n\t\tthis.r.setValue(c.getRed());\n\t\tthis.g.setValue(c.getGreen());\n\t\tthis.b.setValue(c.getBlue());\n\t\tgetColor();\n\t}\n\n\tpublic Color getColor() {\n\t\trl.setText(Integer.toString(r.getValue()));\n\t\tgl.setText(Integer.toString(g.getValue()));\n\t\tbl.setText(Integer.toString(b.getValue()));\n\t\tColor now = new Color(r.getValue(), g.getValue(), b.getValue());\n\t\tc.setColor(now);\n\t\tsetBackground(now);\n\t\treturn now;\n\t}\n\tprivate class ColorBox extends JPanel {\n\t\t/**\n\t\t * long\n\t\t */\n\t\tprivate static final long serialVersionUID = 1L;\n\t\tpublic ColorBox(Color backColor) {\n\t\t\tsetSize(400, 400);\n\t\t\tsetColor(backColor);\n\t\t}\n\n\t\tpublic void setColor(Color c) {\n\t\t\tsetBackground(c);\n\t\t}\n\n\t\tpublic void paintComponent(Graphics g) {\n\t\t\tsuper.paintComponent(g);\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/nrconsole/plugin/DyIO/Secheduler/AnamationSequencer.java",
    "content": "package com.neuronrobotics.nrconsole.plugin.DyIO.Secheduler;\n\nimport javafx.embed.swing.SwingNode;\nimport javafx.scene.control.ScrollPane;\n\nimport com.neuronrobotics.bowlerstudio.tabs.AbstractBowlerStudioTab;\nimport com.neuronrobotics.sdk.common.BowlerAbstractDevice;\n\npublic class AnamationSequencer extends AbstractBowlerStudioTab {\n\tpublic static final String[] myNames = {\"neuronrobotics.dyio.*\"};\n\tprivate SchedulerGui gui = new SchedulerGui();;\n\n\t@Override\n\tpublic void onTabClosing() {\n\t\t// Auto-generated method stub\n\n\t}\n\n\t@Override\n\tpublic String[] getMyNameSpaces() {\n\t\treturn new String[]{\"neuronrobotics.dyio.*\"};\n\t}\n\n\t@Override\n\tpublic void initializeUI(BowlerAbstractDevice pm) {\n\t\t// Auto-generated method stub\n\t\tgui.setConnection(pm);\n\t\tSwingNode sn = new SwingNode();\n\t\tsn.setContent(gui);\n\t\tScrollPane s1 = new ScrollPane();\n\n\t\ts1.setContent(sn);\n\t\tsetContent(s1);\n\t\tsetText(\"Anamation Sequencer\");\n\t\tonTabReOpening();\n\t}\n\n\t@Override\n\tpublic void onTabReOpening() {\n\t\t// Auto-generated method stub\n\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/nrconsole/plugin/DyIO/Secheduler/SchedulerControlBar.java",
    "content": "package com.neuronrobotics.nrconsole.plugin.DyIO.Secheduler;\n\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.io.File;\nimport java.text.DecimalFormat;\nimport java.util.ArrayList;\n\nimport javafx.stage.FileChooser.ExtensionFilter;\n\nimport javax.swing.BorderFactory;\nimport javax.swing.JButton;\nimport javax.swing.JCheckBox;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\nimport javax.swing.JSlider;\nimport javax.swing.JTextField;\nimport javax.swing.event.ChangeEvent;\nimport javax.swing.event.ChangeListener;\n\nimport net.miginfocom.swing.MigLayout;\n\nimport com.neuronrobotics.bowlerstudio.scripting.ScriptingEngine;\nimport com.neuronrobotics.nrconsole.util.FileSelectionFactory;\nimport com.neuronrobotics.sdk.dyio.sequencer.CoreScheduler;\nimport com.neuronrobotics.sdk.dyio.sequencer.ISchedulerListener;\n\npublic class SchedulerControlBar extends JPanel implements ISchedulerListener {\n\n\tprivate JSlider slider = new JSlider();\n\tprivate JButton play = new JButton(\"Play \");\n\tprivate JButton step = new JButton(\"Step \");\n\tprivate JCheckBox loop = new JCheckBox(\"Loop\");\n\tprivate JLabel time = new JLabel(\"Seconds\");\n\n\tprivate JTextField length = new JTextField(4);\n\tprivate JButton selectSong = new JButton(\"Select Audio Track\");\n\tprivate JLabel trackName = new JLabel(\"none\");\n\tprivate CoreScheduler cs;\n\tprivate File mp3File = null;\n\tprivate ChangeListener sliderListener;\n\tprivate ArrayList<ActionListener> pauseListeners = new ArrayList<>();\n\tprivate ArrayList<ActionListener> playListeners = new ArrayList<>();\n\t/**\n\t * long\n\t */\n\tprivate static final long serialVersionUID = -5636481366169943501L;\n\tpublic SchedulerControlBar(CoreScheduler core) {\n\t\tcore.addISchedulerListener(this);\n\t\tsetLayout(new MigLayout());\n\t\tsetBorder(BorderFactory.createLoweredBevelBorder());\n\n\t\tslider.setMajorTickSpacing(1000);\n\t\tslider.setPaintTicks(true);\n\t\tsetTrackLegnth(60000);\n\t\tsetCurrentTime(0);\n\t\tcs = core;\n\t\tsliderListener = new ChangeListener() {\n\t\t\tprivate boolean wasAdjusting = false;\n\t\t\t@Override\n\t\t\tpublic void stateChanged(ChangeEvent e) {\n\t\t\t\tslider.removeChangeListener(sliderListener);\n\t\t\t\tif (slider.getValueIsAdjusting()) {\n\t\t\t\t\tif (cs.isPlaying()) {\n\t\t\t\t\t\twasAdjusting = true;\n\t\t\t\t\t\tpause();\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tsetCurrentTime(slider.getValue());\n\t\t\t\t\tif (wasAdjusting) {\n\t\t\t\t\t\twasAdjusting = false;\n\t\t\t\t\t\tplay();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tslider.addChangeListener(sliderListener);\n\t\t\t}\n\t\t};\n\t\tslider.addChangeListener(sliderListener);\n\n\t\tplay.addActionListener(new ActionListener() {\n\n\t\t\tpublic void actionPerformed(ActionEvent arg0) {\n\t\t\t\tif (!cs.isPlaying()) {\n\t\t\t\t\tplay();\n\t\t\t\t} else {\n\n\t\t\t\t\tpause();\n\t\t\t\t}\n\t\t\t}\n\n\t\t});\n\n\t\tstep.addActionListener(new ActionListener() {\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\tsetSequenceParams();\n\t\t\t\tcs.playStep();\n\t\t\t}\n\t\t});\n\n\t\tselectSong.addActionListener(new ActionListener() {\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\tgetFile();\n\t\t\t}\n\t\t});\n\n\t\tloop.setSelected(false);\n\t\tloop.addActionListener(new ActionListener() {\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\tcs.setLooping(loop.isSelected());\n\t\t\t}\n\t\t});\n\n\t\tJPanel mp3Bar = new JPanel(new MigLayout());\n\t\tmp3Bar.add(selectSong);\n\t\tmp3Bar.add(new JLabel(\"Current Track:\"));\n\t\tmp3Bar.add(trackName);\n\n\t\tJPanel trackBar = new JPanel(new MigLayout());\n\t\ttrackBar.add(length);\n\t\ttrackBar.add(time);\n\t\ttrackBar.add(slider);\n\t\ttrackBar.add(step);\n\t\ttrackBar.add(play);\n\t\ttrackBar.add(loop);\n\n\t\tsetBorder(BorderFactory.createRaisedBevelBorder());\n\t\tadd(mp3Bar, \"wrap\");\n\t\tadd(trackBar, \"wrap\");\n\t}\n\n\tprivate void setSequenceParams() {\n\t\tint start = slider.getValue();\n\n\t\tint setpoint;\n\t\ttry {\n\t\t\tsetpoint = (int) (1000 * Double.parseDouble(length.getText()));\n\t\t} catch (NumberFormatException n) {\n\t\t\tsetpoint = 1000;\n\t\t}\n\t\tsetTrackLegnth(setpoint);\n\t\tcs.setSequenceParams(setpoint, start);\n\t}\n\n\tprivate void play() {\n\t\tsetSequenceParams();\n\t\tcs.play();\n\t\tplay.setText(\"Pause\");\n\t\tstep.setEnabled(false);\n\t\tfor (ActionListener a : playListeners)\n\t\t\ta.actionPerformed(null);\n\t}\n\n\tprivate void pause() {\n\t\tif (cs != null)\n\t\t\tcs.pause();\n\t\tplay.setText(\"Play \");\n\t\tstep.setEnabled(true);\n\t\tfor (ActionListener a : pauseListeners)\n\t\t\ta.actionPerformed(null);\n\n\t}\n\n\tprivate void setTrackLegnth(int ms) {\n\t\tlength.setText(Double.toString(((double) ms) / 1000.0));\n\t\tsetBounds(ms);\n\t}\n\n\tprivate void setCurrentTime(long val) {\n\t\t// com.neuronrobotics.sdk.common.Log.error(\"Setting current time=\"+val);\n\t\ttry {\n\t\t\tslider.setValue((int) (val));\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t\tdouble cTime = ((double) val) / 1000;\n\t\ttime.setText(\"Seconds: \" + new DecimalFormat(\"000.00\").format(cTime));\n\n\t\t// com.neuronrobotics.sdk.common.Log.error(\"Setting current time=\"+val+\"\n\t\t// slider=\"+slider.getValue());\n\t}\n\n\tprivate void setBounds(double top) {\n\t\tslider.setMaximum(0);\n\t\tslider.setMaximum((int) (top));\n\t}\n\n\tprivate void getFile() {\n\t\tsetAudioFile(FileSelectionFactory.GetFile(mp3File == null ? ScriptingEngine.getWorkspace() : mp3File,\n\t\t\t\tnew ExtensionFilter(\"WAV file\", \"*.wav\", \"*.WAV\")));\n\t}\n\n\tpublic void setAudioFile(File f) {\n\t\tcs.setAudioFile(f);\n\t\tsetTrackLegnth(cs.getTrackLength());\n\t\ttrackName.setText(f.getName());\n\t\tlength.setEditable(false);\n\t\tsetCurrentTime(0);\n\t}\n\n\t@Override\n\tpublic void onTimeUpdate(double ms) {\n\t\tsetCurrentTime((long) ms);\n\t}\n\n\t@Override\n\tpublic void onReset() {\n\t\tplay.setText(\"Play\");\n\t}\n\n\t@Override\n\tpublic void setIntervalTime(int msInterval, int totalTime) {\n\t\t// Auto-generated method stub\n\n\t}\n\n\tpublic void addPauseListener(ActionListener actionListener) {\n\t\tif (!pauseListeners.contains(actionListener))\n\t\t\tpauseListeners.add(actionListener);\n\t}\n\n\tpublic void addPlayListener(ActionListener actionListener) {\n\t\tif (!playListeners.contains(actionListener))\n\t\t\tplayListeners.add(actionListener);\n\t}\n\n\t@Override\n\tpublic void onPlay() {\n\t\t// Auto-generated method stub\n\n\t}\n\n\t@Override\n\tpublic void onPause() {\n\t\t// Auto-generated method stub\n\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/nrconsole/plugin/DyIO/Secheduler/SchedulerGui.java",
    "content": "package com.neuronrobotics.nrconsole.plugin.DyIO.Secheduler;\n\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.io.BufferedWriter;\nimport java.io.File;\nimport java.io.FileWriter;\nimport java.util.ArrayList;\n\nimport javafx.stage.FileChooser.ExtensionFilter;\n\nimport javax.swing.BorderFactory;\nimport javax.swing.JButton;\nimport javax.swing.JOptionPane;\nimport javax.swing.JPanel;\n\nimport net.miginfocom.swing.MigLayout;\n\nimport com.neuronrobotics.bowlerstudio.scripting.ScriptingEngine;\nimport com.neuronrobotics.nrconsole.util.FileSelectionFactory;\nimport com.neuronrobotics.nrconsole.util.IntegerComboBox;\nimport com.neuronrobotics.sdk.common.BowlerAbstractDevice;\nimport com.neuronrobotics.sdk.dyio.DyIO;\nimport com.neuronrobotics.sdk.dyio.sequencer.CoreScheduler;\nimport com.neuronrobotics.sdk.dyio.sequencer.ServoOutputScheduleChannel;\n\npublic class SchedulerGui extends JPanel {\n\n\t/**\n\t *\n\t */\n\t// private DyIO d = new DyIO();\n\tprivate static final long serialVersionUID = -2532174391435417313L;\n\tJPanel channelBar = new JPanel(new MigLayout());\n\tprivate IntegerComboBox availableChans = new IntegerComboBox();\n\tprivate IntegerComboBox usedChans = new IntegerComboBox();\n\tprivate ArrayList<ServoOutputScheduleChannelUI> outputs = new ArrayList<>();\n\tprivate File configFile = null;\n\tCoreScheduler cs;\n\tSchedulerControlBar cb;\n\tprivate int loopTime = 50;\n\tprivate DyIO dyio;\n\tpublic SchedulerGui() {\n\n\t}\n\n\tprivate void rmAllChannels() {\n\t\tint[] chans = new int[outputs.size()];\n\t\tfor (int i = 0; i < chans.length; i++) {\n\t\t\tchans[i] = outputs.get(i).getChannelNumber();\n\t\t}\n\t\tfor (int i = 0; i < chans.length; i++) {\n\t\t\trmChannel(chans[i]);\n\t\t}\n\t}\n\n\tprivate void rmChannel(int num) {\n\n\t\tServoOutputScheduleChannelUI s = null;\n\n\t\tfor (ServoOutputScheduleChannelUI so : outputs) {\n\t\t\tif (so.getChannelNumber() == num)\n\t\t\t\ts = so;\n\t\t}\n\t\tif (s == null)\n\t\t\treturn;\n\t\tcs.removeServoOutputScheduleChannel(s.getChannel());\n\t\tcs.removeISchedulerListener(s);\n\t\toutputs.remove(s);\n\t\tchannelBar.remove(s);\n\t\tusedChans.removeInteger(num);\n\t\tavailableChans.addInteger(num);\n\t}\n\n\tprivate void addServoChannel(ServoOutputScheduleChannel chan) {\n\t\tint selected = chan.getChannelNumber();\n\t\tServoOutputScheduleChannelUI sosc = new ServoOutputScheduleChannelUI(chan, cs);\n\t\tcs.addISchedulerListener(sosc);\n\t\toutputs.add(sosc);\n\t\tchannelBar.add(sosc, \"wrap\");\n\t\tavailableChans.removeInteger(selected);\n\t\tusedChans.addInteger(selected);\n\t}\n\n\tprotected void importfromFile() {\n\t\tif (configFile == null)\n\t\t\treturn;\n\t\tcs.loadFromFile(configFile);\n\t\tcb.setAudioFile(cs.getAudioFile());\n\t\tArrayList<ServoOutputScheduleChannel> outs = cs.getOutputs();\n\t\tfor (ServoOutputScheduleChannel so : outs) {\n\t\t\taddServoChannel(so);\n\t\t}\n\t\tfor (int i = 0; i < get().getChannels().size(); i++) {\n\t\t\tget().getValue(i);\n\t\t}\n\t}\n\n\tprotected void exportToFile() {\n\t\tString s = cs.getXml();\n\t\ttry {\n\t\t\t// Create file\n\t\t\tFileWriter fstream = new FileWriter(configFile.getAbsolutePath());\n\t\t\tBufferedWriter out = new BufferedWriter(fstream);\n\t\t\tout.write(s);\n\t\t\t// Close the output stream\n\t\t\tout.close();\n\t\t} catch (Exception e) {// Catch exception if any\n\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Error: \" + e.getMessage());\n\t\t}\n\n\t}\n\n\tprivate void getFile() {\n\t\tif (configFile == null)\n\t\t\tconfigFile = ScriptingEngine.getWorkspace();\n\t\tconfigFile = FileSelectionFactory.GetFile(configFile, new ExtensionFilter(\"Sequence XML\", \"*.xml\", \"*.XML\"));\n\t}\n\n\tprivate DyIO get() {\n\t\treturn dyio;\n\t}\n\n\tpublic boolean setConnection(BowlerAbstractDevice connection) {\n\t\tdyio = (DyIO) connection;\n\t\tsetLayout(new MigLayout());\n\t\tsetBorder(BorderFactory.createLoweredBevelBorder());\n\t\tcs = new CoreScheduler(get(), loopTime, 6000);\n\t\tcb = new SchedulerControlBar(cs);\n\n\t\tJPanel addBar = new JPanel(new MigLayout());\n\t\tJButton addChannel = new JButton(\"Add new channel\");\n\t\taddChannel.addActionListener(new ActionListener() {\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\ttry {\n\t\t\t\t\tint selected = availableChans.getSelectedInteger();\n\t\t\t\t\taddServoChannel(cs.addServoChannel(selected));\n\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\tJOptionPane.showMessageDialog(null, \"Failed to select channel, \" + ex.getMessage(), \"Bowler ERROR\",\n\t\t\t\t\t\t\tJOptionPane.ERROR_MESSAGE);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tfor (int i = 0; i < 24; i++) {\n\t\t\tavailableChans.addInteger(i);\n\t\t}\n\t\taddBar.add(addChannel);\n\t\taddBar.add(availableChans);\n\n\t\tJButton removeChannel = new JButton(\"Remove channel\");\n\t\tremoveChannel.addActionListener(new ActionListener() {\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\n\t\t\t\ttry {\n\t\t\t\t\tint selected = usedChans.getSelectedInteger();\n\t\t\t\t\tfor (int i = 0; i < outputs.size(); i++) {\n\t\t\t\t\t\tServoOutputScheduleChannelUI s = outputs.get(i);\n\t\t\t\t\t\tif (s.getChannelNumber() == selected) {\n\t\t\t\t\t\t\trmChannel(selected);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\tJOptionPane.showMessageDialog(null, \"Failed to select channel, \" + ex.getMessage(), \"Bowler ERROR\",\n\t\t\t\t\t\t\tJOptionPane.ERROR_MESSAGE);\n\t\t\t\t}\n\n\t\t\t}\n\t\t});\n\t\taddBar.add(removeChannel);\n\t\taddBar.add(usedChans);\n\n\t\tJButton saveConfiguration = new JButton(\"Save Configuration\");\n\t\tsaveConfiguration.addActionListener(new ActionListener() {\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent arg0) {\n\t\t\t\tgetFile();\n\t\t\t\texportToFile();\n\t\t\t}\n\t\t});\n\t\tJButton loadConfiguration = new JButton(\"Load Configuration\");\n\t\tloadConfiguration.addActionListener(new ActionListener() {\n\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent arg0) {\n\t\t\t\trmAllChannels();\n\n\t\t\t\tgetFile();\n\t\t\t\timportfromFile();\n\t\t\t}\n\t\t});\n\t\taddBar.add(saveConfiguration);\n\t\taddBar.add(loadConfiguration);\n\t\tchannelBar.setBorder(BorderFactory.createRaisedBevelBorder());\n\n\t\tadd(cb, \"wrap\");\n\t\tadd(addBar, \"wrap\");\n\t\tadd(channelBar, \"wrap\");\n\n\t\treturn get().ping();\n\t}\n\n\t// public static void main(String[] args) {\n\t// JFrame frame = new JFrame();\n\t// SchedulerGui sg =new SchedulerGui();\n\t// //sg.setConnection(new SerialConnection(\"COM48\"));\n\t// sg.setConnection(new SerialConnection(\"/dev/DyIO0\"));\n\t// //sg.setConnection(ConnectionDialog.promptConnection());\n\t// frame .add(sg);\n\t// frame.setSize(new Dimension(1024,768));\n\t// frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);\n\t// frame.setVisible(true);\n\t// }\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/nrconsole/plugin/DyIO/Secheduler/ServoOutputScheduleChannelUI.java",
    "content": "package com.neuronrobotics.nrconsole.plugin.DyIO.Secheduler;\n\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\n\nimport javax.swing.BorderFactory;\nimport javax.swing.JCheckBox;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\nimport javax.swing.JSlider;\nimport javax.swing.JTextField;\nimport javax.swing.event.ChangeEvent;\nimport javax.swing.event.ChangeListener;\n\nimport net.miginfocom.swing.MigLayout;\n\nimport com.neuronrobotics.nrconsole.util.IntegerComboBox;\nimport com.neuronrobotics.sdk.dyio.peripherals.IServoPositionUpdateListener;\nimport com.neuronrobotics.sdk.dyio.peripherals.ServoChannel;\nimport com.neuronrobotics.sdk.dyio.sequencer.CoreScheduler;\nimport com.neuronrobotics.sdk.dyio.sequencer.ISchedulerListener;\nimport com.neuronrobotics.sdk.dyio.sequencer.ServoOutputScheduleChannel;\n\npublic class ServoOutputScheduleChannelUI extends JPanel\n\t\timplements\n\t\t\tIServoPositionUpdateListener,\n\t\t\tActionListener,\n\t\t\tISchedulerListener {\n\n\t/**\n\t *\n\t */\n\tprivate static final long serialVersionUID = -7112414698561768276L;\n\tprivate ServoOutputScheduleChannel channel;\n\t// private JCheckBox record = new JCheckBox(\"Record\");\n\t// private JButton startRecording = new JButton(\"Start Recording\");\n\t// private JButton startTest = new JButton(\"Start Test\");\n\tprivate JSlider position = new JSlider();\n\tprivate JCheckBox useSlider = new JCheckBox(\"Record\");\n\tprivate JPanel recordConfig = new JPanel();\n\tIntegerComboBox inputChannelNumber;\n\tprivate JTextField scale = new JTextField(5);\n\tprivate JTextField zero = new JTextField(5);\n\n\tprivate double currentScale = .25;\n\n\tprivate int currentZero = 512;\n\n\tChangeListener posListener = new ChangeListener() {\n\n\t\t@Override\n\t\tpublic void stateChanged(ChangeEvent e) {\n\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"Pos listener\");\n\n\t\t\tif (useSlider.isSelected()) {\n\t\t\t\tflush();\n\t\t\t\tif (!getCb().isPlaying())\n\t\t\t\t\tchannel.flush();\n\t\t\t} else {\n\t\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"Not flushing\");\n\t\t\t}\n\n\t\t}\n\t};\n\n\tprivate CoreScheduler cb;\n\n\tpublic void flush() {\n\t\tchannel.setCurrentTargetValue(position.getValue());\n\n\t}\n\n\tpublic ServoOutputScheduleChannelUI(ServoOutputScheduleChannel chan, CoreScheduler cb) {\n\n\t\tthis.setCb(cb);\n\t\tchan.addIServoPositionUpdateListener(this);\n\t\tsetChannel(chan);\n\t\tsetLayout(new MigLayout());\n\t\tinputChannelNumber = new IntegerComboBox();\n\t\tfor (int i = 8; i < 16; i++) {\n\t\t\tinputChannelNumber.addInteger(i);\n\t\t}\n\t\tsetBorder(BorderFactory.createLoweredBevelBorder());\n\t\t// record.addActionListener(new ActionListener() {\n\t\t// public void actionPerformed(ActionEvent arg0) {\n\t\t// if(record.isSelected()){\n\t\t// recordConfig.setVisible(true);\n\t\t// }else{\n\t\t// recordConfig.setVisible(false);\n\t\t// pause();\n\t\t// }\n\t\t// }\n\t\t// });\n\t\t//\n\t\t// startRecording.addActionListener(new ActionListener() {\n\t\t// public void actionPerformed(ActionEvent arg0) {\n\t\t// if(!getChannel().isRecording()){\n\t\t// resume();\n\t\t// }\n\t\t// else{\n\t\t// pause();\n\t\t// }\n\t\t// }\n\t\t// });\n\t\t// startTest.addActionListener(new ActionListener() {\n\t\t//\n\t\t// @Override\n\t\t// public void actionPerformed(ActionEvent arg0) {\n\t\t// if(getChannel().isTesting()) {\n\t\t// stopTest();\n\t\t// }else\n\t\t// startTest();\n\t\t// }\n\t\t// });\n\n\t\tuseSlider.addActionListener(new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\tif (useSlider.isSelected()) {\n\t\t\t\t\tposition.setEnabled(true);\n\t\t\t\t\tgetChannel().setRecording(true);\n\t\t\t\t\tflush();\n\t\t\t\t\tchannel.flush();\n\t\t\t\t} else {\n\t\t\t\t\tposition.setEnabled(false);\n\t\t\t\t\tgetChannel().setRecording(false);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\trecordConfig.add(inputChannelNumber);\n\t\t// recordConfig.add(startRecording);\n\t\t// recordConfig.add(startTest);\n\t\trecordConfig.setVisible(false);\n\n\t\t// JPanel config = new JPanel(new MigLayout());\n\t\t// config.add(new JLabel(\"Input Scale:\"));\n\t\t// config.add(scale,\"wrap\");\n\t\t// config.add(new JLabel(\"Output Center:\"));\n\t\t// config.add(zero,\"wrap\");\n\t\tscale.addActionListener(this);\n\t\tzero.addActionListener(this);\n\n\t\t// recordConfig.add(config);\n\n\t\tposition.setEnabled(false);\n\t\tposition.setMaximum(0);\n\t\tposition.setMaximum(255);\n\t\tposition.setMajorTickSpacing(5);\n\t\tposition.setPaintTicks(true);\n\t\tposition.setValue(chan.getCurrentTargetValue());\n\t\tposition.addChangeListener(posListener);\n\n\t\tadd(new JLabel(\"Output Channel: \" + getChannel().getChannelNumber()));\n\t\tadd(position);\n\t\tadd(useSlider);\n\t\t// add(record);\n\t\tadd(recordConfig);\n\n\t\t// record.setSelected(getChannel().isRecording());\n\t\ttry {\n\t\t\tinputChannelNumber.setSelectedInteger(getChannel().getInputChannelNumber());\n\t\t} catch (Exception ex) {\n\t\t\tex.printStackTrace();\n\t\t}\n\t\t// recordConfig.setVisible(record.isSelected());\n\t\tzero.setText(Integer.toString(getChannel().getInputCenter()));\n\t\tscale.setText(Double.toString(getChannel().getInputScale()));\n\t\tsetScaleingInfo();\n\t}\n\n\tprivate void setScaleingInfo() {\n\t\tcurrentZero = Integer.parseInt(zero.getText());\n\t\tcurrentScale = Double.parseDouble(scale.getText());\n\t\tgetChannel().setInputScale(getInputScale());\n\t\tgetChannel().setInputCenter(getInputZero());\n\t\tgetChannel().setAnalogInputChannelNumber(inputChannelNumber.getSelectedInteger());\n\t}\n\n\tprivate int getInputZero() {\n\t\treturn currentZero;\n\t}\n\n\tprivate double getInputScale() {\n\t\treturn currentScale;\n\t}\n\n\tpublic int getChannelNumber() {\n\t\t// Auto-generated method stub\n\t\treturn getChannel().getChannelNumber();\n\t}\n\n\tpublic void setChannel(ServoOutputScheduleChannel channel) {\n\t\tthis.channel = channel;\n\t}\n\n\tpublic ServoOutputScheduleChannel getChannel() {\n\t\treturn channel;\n\t}\n\n\t@Override\n\tpublic void onServoPositionUpdate(ServoChannel srv, int position, double time) {\n\t\tif (useSlider.isSelected()) {\n\t\t\tchannel.removeIServoPositionUpdateListener(this);\n\t\t\t// flush();\n\t\t\tchannel.addIServoPositionUpdateListener(this);\n\t\t\treturn;\n\t\t}\n\t\tthis.position.removeChangeListener(posListener);\n\t\tthis.position.setValue(position);\n\t\tthis.position.addChangeListener(posListener);\n\t}\n\n\t@Override\n\tpublic void actionPerformed(ActionEvent e) {\n\t\tsetScaleingInfo();\n\t}\n\n\t@Override\n\tpublic void onTimeUpdate(double ms) {\n\t\t// Auto-generated method stub\n\n\t}\n\n\t@Override\n\tpublic void setIntervalTime(int msInterval, int totalTime) {\n\t\t// Auto-generated method stub\n\n\t}\n\n\t@Override\n\tpublic void onReset() {\n\n\t}\n\n\t@Override\n\tpublic void onPlay() {\n\n\t\t// getChannel().setRecording(true);\n\t}\n\n\t@Override\n\tpublic void onPause() {\n\t\tuseSlider.setSelected(false);\n\t\tposition.setEnabled(false);\n\t\tgetChannel().setRecording(false);\n\t\t// com.neuronrobotics.sdk.common.Log.error(\"Setting the pause in output UI\");\n\t}\n\n\tpublic CoreScheduler getCb() {\n\t\treturn cb;\n\t}\n\n\tpublic void setCb(CoreScheduler cb) {\n\t\tthis.cb = cb;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/nrconsole/plugin/bootloader/BootloaderPanel.java",
    "content": "package com.neuronrobotics.nrconsole.plugin.bootloader;\n\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.io.File;\n\nimport javafx.embed.swing.SwingNode;\n\nimport javax.swing.JButton;\nimport javax.swing.JFileChooser;\nimport javax.swing.JOptionPane;\nimport javax.swing.JPanel;\nimport javax.swing.JProgressBar;\nimport javax.swing.filechooser.FileFilter;\n\nimport net.miginfocom.swing.MigLayout;\n\nimport com.neuronrobotics.bowlerstudio.tabs.AbstractBowlerStudioTab;\nimport com.neuronrobotics.nrconsole.plugin.bootloader.gui.StatusLabel;\nimport com.neuronrobotics.sdk.bootloader.Hexml;\nimport com.neuronrobotics.sdk.bootloader.NRBoot;\nimport com.neuronrobotics.sdk.common.BowlerAbstractDevice;\nimport com.neuronrobotics.sdk.common.InvalidResponseException;\nimport com.neuronrobotics.sdk.common.NoConnectionAvailableException;\nimport com.neuronrobotics.sdk.config.SDKBuildInfo;\n\npublic class BootloaderPanel extends AbstractBowlerStudioTab implements ActionListener {\n\n\tprivate NRBoot blApp;\n\n\tprivate JButton loadButton;\n\tprivate Hexml hex = null;\n\tprivate JProgressBar progress = new JProgressBar();\n\t// private StatusLabel fileStatus = new StatusLabel();\n\tprivate StatusLabel loadStatus = new StatusLabel();\n\tprivate String revision;\n\tprivate static File file = null;\n\t// set this variable to make this tab auto open when a device is connected\n\t/**\n\t *\n\t */\n\tprivate static final long serialVersionUID = 6467421820450464854L;\n\n\tpublic BootloaderPanel() {\n\t\t//// com.neuronrobotics.sdk.common.Log.error(\"Starting GUI\");\n\t\tsetText(\"NR Bootloader\");\n\t\t// fileButton = new JButton();\n\t\t// fileButton.addActionListener(this);\n\t\t// resetFile();\n\n\t\tloadButton = new JButton();\n\t\tloadButton.addActionListener(this);\n\t\tresetLoad();\n\n\t\tJPanel buttonPanel = new JPanel(new MigLayout());\n\n\t\t// buttonPanel.add(fileStatus);\n\t\t// buttonPanel.add(fileButton, \"wrap\");\n\n\t\tJPanel prog = new JPanel(new MigLayout());\n\t\tprog.add(loadButton, \"wrap\");\n\t\tprog.add(progress, \"wrap\");\n\n\t\tbuttonPanel.add(loadStatus);\n\t\tbuttonPanel.add(prog);\n\n\t\tSwingNode sn = new SwingNode();\n\t\tsn.setContent(buttonPanel);\n\t\tsetContent(sn);\n\n\t}\n\n\tpublic NRBoot getBlApp() {\n\t\treturn blApp;\n\t}\n\n\t// public void resetFile() {\n\t// fileButton.setText(\"Select a NR Firmware File..\");\n\t// }\n\n\tpublic void resetLoad() {\n\t\tloadButton.setEnabled(false);\n\t\tloadButton.setText(\"Load NR Firmware File...\");\n\t\tloadStatus.setStatus(0);\n\t\treloadFile();\n\t}\n\n\tpublic void loadFile() {\n\t\t// com.neuronrobotics.sdk.common.Log.error(\"Loading file:\");\n\t\tJFileChooser fc = new JFileChooser();\n\t\tFile dir2 = null;\n\t\ttry {\n\t\t\tdir2 = new File(System.getProperty(\"user.home\") + \"/git/dyio/FirmwarePublish/Dev/\");\n\t\t\tif (!dir2.exists()) {\n\t\t\t\tdir2 = new File(\"../firmware/\");\n\t\t\t\tif (!dir2.exists()) {\n\t\t\t\t\tdir2 = null;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception ex) {\n\t\t\tex.printStackTrace();\n\t\t}\n\t\tif (dir2 == null) {\n\t\t\tif (SDKBuildInfo.isLinux()) {\n\t\t\t\tdir2 = new File(\"/usr/local/NeuronRobotics/RDK/firmware\");\n\t\t\t} else\n\t\t\t\tdir2 = new File(\".\");\n\t\t}\n\t\tif (file != null) {\n\t\t\tfc.setSelectedFile(file);\n\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"Starting with:\n\t\t\t// \"+file.getAbsolutePath());\n\t\t} else {\n\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"Starting in:\n\t\t\t// \"+dir2.getAbsolutePath());\n\t\t\tfc.setCurrentDirectory(dir2);\n\t\t}\n\t\tfc.setFileFilter(new FileFilter() {\n\n\t\t\tpublic String getDescription() {\n\t\t\t\treturn \"NR Firmware Format (xml)\";\n\t\t\t}\n\n\t\t\tpublic boolean accept(File f) {\n\t\t\t\tif (f.isDirectory()) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\tString path = f.getAbsolutePath().toLowerCase();\n\t\t\t\tif (path.endsWith(\"xml\") && path.charAt(path.length() - 3) == '.') {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\treturn f.getName().matches(\".+\\\\.xml$\");\n\t\t\t}\n\t\t});\n\n\t\tint returnVal = fc.showOpenDialog(null);\n\t\tif (returnVal == JFileChooser.APPROVE_OPTION) {\n\t\t\tfile = fc.getSelectedFile();\n\t\t\tif (!file.getName().matches(\".+\\\\.xml$\")) {\n\t\t\t\tString message = \"Invalid file type. Must be .xml\";\n\t\t\t\tJOptionPane.showMessageDialog(null, message, message, JOptionPane.ERROR_MESSAGE);\n\t\t\t\t// fileStatus.setStatus(StatusLabel.ERROR);\n\t\t\t\t// resetFile();\n\t\t\t\tresetLoad();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\treloadFile();\n\t\t}\n\t\tloadButton.setEnabled(true);\n\t}\n\n\tprivate void reloadFile() {\n\t\tif (file != null) {\n\t\t\ttry {\n\t\t\t\thex = new Hexml(file);\n\t\t\t} catch (Exception e) {\n\t\t\t\te.printStackTrace();\n\t\t\t\tString message = \"Invalid xml file\";\n\t\t\t\tJOptionPane.showMessageDialog(null, message, message, JOptionPane.ERROR_MESSAGE);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// fileButton.setText(\"Using: \"+file.getName());\n\t\t\trevision = hex.getRevision();\n\t\t\t// fileStatus.setStatus(StatusLabel.OK);\n\t\t}\n\t}\n\n\tpublic String getRevision() {\n\t\treturn revision;\n\t}\n\n\tprivate void resetAll() {\n\t\tgetBlApp().getDevice().disconnect();\n\t\tresetLoad();\n\t}\n\tpublic class LoaderChecker extends Thread {\n\t\tpublic void run() {\n\t\t\tsetName(\"Bowler Platform  boot loader checker\");\n\t\t\tprogress.setMaximum(getBlApp().getProgressMax());\n\t\t\tprogress.setMinimum(0);\n\t\t\t// progress.setIndeterminate(true);\n\t\t\twhile (getBlApp().isLoadDone() == false) {\n\t\t\t\ttry {\n\t\t\t\t\tThread.sleep(100);\n\t\t\t\t} catch (InterruptedException e) {\n\t\t\t\t}\n\t\t\t\tprogress.setValue(getBlApp().getProgressValue());\n\t\t\t}\n\t\t\tprogress.setIndeterminate(false);\n\t\t\tString message = \"Success! Your Bowler device is now updated to version: \" + hex.getRevision()\n\t\t\t\t\t+ \" Dont forget to Un-Plug your device!\";\n\t\t\tJOptionPane.showMessageDialog(null, message, message, JOptionPane.INFORMATION_MESSAGE);\n\t\t\tresetAll();\n\t\t\t// System.exit(0);\n\t\t}\n\t}\n\n\tprivate void selectFile() {\n\t\tloadFile();\n\t\ttry {\n\t\t\tif (getBlApp() != null) {\n\t\t\t\tloadStatus.setStatus(StatusLabel.OK);\n\t\t\t\t//// com.neuronrobotics.sdk.common.Log.error(\"Loading firmware\");\n\t\t\t\treloadFile();\n\t\t\t\tgetBlApp().loadCores(hex.getCores());\n\t\t\t\tloadButton.setText(file.getName() + \" Loading....\");\n\t\t\t\tloadButton.setEnabled(false);\n\t\t\t\tLoaderChecker l = new LoaderChecker();\n\t\t\t\tl.start();\n\t\t\t}\n\t\t} catch (InvalidResponseException ex) {\n\t\t\tString message = \"Device is not in bootloader mode!\";\n\t\t\tJOptionPane.showMessageDialog(null, message, message, JOptionPane.ERROR_MESSAGE);\n\t\t} catch (NoConnectionAvailableException ex) {\n\t\t\tString message = \"Device is not no longer connected to bootloader\";\n\t\t\tJOptionPane.showMessageDialog(null, message, message, JOptionPane.ERROR_MESSAGE);\n\t\t\tresetLoad();\n\t\t}\n\t}\n\n\tpublic void actionPerformed(ActionEvent e) {\n\t\t// Handle open button action.\n\t\t// if (e.getSource() == fileButton) {\n\t\t//\n\t\t// }\n\t\tif (e.getSource() == loadButton) {\n\t\t\tselectFile();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onTabClosing() {\n\t\t// Auto-generated method stub\n\n\t}\n\n\t@Override\n\tpublic String[] getMyNameSpaces() {\n\t\treturn new String[0];\n\t}\n\n\t@Override\n\tpublic void initializeUI(BowlerAbstractDevice pm) {\n\t\t// Auto-generated method stub\n\n\t\tblApp = new NRBoot(pm);\n\t\tloadButton.setEnabled(true);\n\t\tselectFile();\n\t}\n\n\t@Override\n\tpublic void onTabReOpening() {\n\t\t// Auto-generated method stub\n\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/nrconsole/plugin/bootloader/gui/BootloaderParams.java",
    "content": "package com.neuronrobotics.nrconsole.plugin.bootloader.gui;\n\nimport com.neuronrobotics.sdk.bootloader.NRBootCoreType;\n\npublic class BootloaderParams {\n\tprivate int core;\n\tprivate String hexFilePath;\n\tprivate NRBootCoreType type;\n\tpublic String toString() {\n\t\tString s;\n\t\ts = \"Core #\" + getCore() + \" of type:\" + getType().getReadableName() + \" Using file:\" + getHexFilePath();\n\t\treturn s;\n\t}\n\n\tpublic void setCore(int core) {\n\t\tthis.core = core;\n\t}\n\n\tpublic int getCore() {\n\t\treturn core;\n\t}\n\n\tpublic void setHexFilePath(String hexFilePath) {\n\t\tthis.hexFilePath = hexFilePath;\n\t}\n\n\tpublic String getHexFilePath() {\n\t\treturn hexFilePath;\n\t}\n\n\tpublic void setType(NRBootCoreType type) {\n\t\tthis.type = type;\n\t}\n\n\tpublic NRBootCoreType getType() {\n\t\treturn type;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/nrconsole/plugin/bootloader/gui/NRBootLoaderApp.java",
    "content": "package com.neuronrobotics.nrconsole.plugin.bootloader.gui;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.util.ArrayList;\n\nimport javax.swing.JOptionPane;\n\nimport com.neuronrobotics.sdk.bootloader.Core;\nimport com.neuronrobotics.sdk.bootloader.Hexml;\nimport com.neuronrobotics.sdk.bootloader.NRBoot;\nimport com.neuronrobotics.sdk.bootloader.NRBootCoreType;\n\npublic class NRBootLoaderApp {\n\tpublic NRBootLoaderApp(String[] args) {\n\t\tString port = null;\n\t\tHexml hex = null;\n\t\tArrayList<Core> cores = new ArrayList<>();\n\t\tfor (int i = 0; i < args.length; i++) {\n\t\t\tif (args[i].contains(\"--core\")) {\n\t\t\t\tBootloaderParams param = new BootloaderParams();\n\t\t\t\ttry {\n\t\t\t\t\tInteger coreNum = Integer.valueOf(args[i + 1]);\n\t\t\t\t\tparam.setCore(coreNum.intValue());\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tfail();\n\t\t\t\t}\n\t\t\t\tString coreType = args[i + 2];\n\t\t\t\tif (coreType.contains(\"AVR\")) {\n\t\t\t\t\tparam.setType(NRBootCoreType.AVRxx4p);\n\t\t\t\t} else if (coreType.contains(\"PIC\")) {\n\t\t\t\t\tparam.setType(NRBootCoreType.PIC32);\n\t\t\t\t} else {\n\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Core Types are:\\nAVR\\nPIC32\");\n\t\t\t\t\tfail();\n\t\t\t\t}\n\t\t\t\tString hexFile = args[i + 3];\n\t\t\t\ttry {\n\t\t\t\t\tnew FileInputStream(hexFile);\n\t\t\t\t\tparam.setHexFilePath(hexFile);\n\t\t\t\t} catch (FileNotFoundException e) {\n\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"File \" + hexFile + \" Does not exist\");\n\t\t\t\t\tfail();\n\t\t\t\t}\n\t\t\t\tcores.add(new Core(param.getCore(), param.getHexFilePath(), param.getType()));\n\t\t\t}\n\t\t\tif (args[i].contains(\"--port\")) {\n\t\t\t\tport = args[i + 1];\n\t\t\t}\n\t\t\tif (args[i].contains(\"--xml\")) {\n\t\t\t\ttry {\n\t\t\t\t\thex = new Hexml(new File(args[i + 1]));\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t\tString message = \"Invalid xml file\";\n\t\t\t\t\tJOptionPane.showMessageDialog(null, message, message, JOptionPane.ERROR_MESSAGE);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (hex != null) {\n\t\t\tcores = hex.getCores();\n\t\t}\n\t\tNRBoot blApp;\n\t\tblApp = new NRBoot(port);\n\t\tfor (Core b : cores) {\n\t\t\tblApp.load(b);\n\t\t}\n\t\tblApp.reset();\n\t\tSystem.exit(0);\n\t}\n\t// public NRBootLoaderApp(){\n\t// NR_Bootloader_GUI gui = new NR_Bootloader_GUI();\n\t// ////com.neuronrobotics.sdk.common.Log.error(\"Waiting for port selection\");\n\t// boolean getAp = false;\n\t// boolean wasSelected = false;\n\t// while (true){\n\t// if(gui.isPortSelect() && !getAp){\n\t// try {\n\t// NRBoot b = new NRBoot(new ConnectionDialog.promptConnection());\n\t// gui.setBlApp(b);\n\t// getAp=true;\n\t// }catch(Exception e) {\n\t// gui.resetPort();\n\t// }\n\t// }\n\t// if(wasSelected && !gui.isPortSelect()){\n\t// getAp = false;\n\t// }\n\t// wasSelected =gui.isPortSelect();\n\t// try {\n\t// Thread.sleep(10);\n\t// } catch (InterruptedException e) {}\n\t// }\n\t//\n\t// }\n\n\tprivate static void fail() {\n\t\tcom.neuronrobotics.sdk.common.Log.error(\n\t\t\t\t\"Paramaters are:\\n(Can be more then one core)\\n--core <num> <type> <path to hex>\\n--xml <path to xml>\");\n\t\tSystem.exit(1);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/nrconsole/plugin/bootloader/gui/NR_Bootloader_GUI.java",
    "content": "package com.neuronrobotics.nrconsole.plugin.bootloader.gui;\n\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.io.File;\n\nimport javax.swing.JButton;\nimport javax.swing.JFileChooser;\nimport javax.swing.JFrame;\nimport javax.swing.JLabel;\nimport javax.swing.JOptionPane;\nimport javax.swing.JPanel;\nimport javax.swing.JProgressBar;\nimport javax.swing.filechooser.FileFilter;\n\nimport net.miginfocom.swing.MigLayout;\n\nimport com.neuronrobotics.sdk.bootloader.Hexml;\nimport com.neuronrobotics.sdk.bootloader.NRBoot;\nimport com.neuronrobotics.sdk.common.InvalidResponseException;\nimport com.neuronrobotics.sdk.common.NoConnectionAvailableException;\n\npublic class NR_Bootloader_GUI implements ActionListener {\n\tprivate NRBoot blApp;\n\n\tprivate JButton fileButton, loadButton;\n\tprivate JFrame frame;\n\tprivate Hexml hex = null;\n\tprivate JProgressBar progress = new JProgressBar();\n\tprivate StatusLabel fileStatus = new StatusLabel();\n\tprivate StatusLabel portStatus = new StatusLabel();\n\tprivate StatusLabel loadStatus = new StatusLabel();\n\n\tprivate static final long serialVersionUID = 1L;\n\tprivate boolean portSelect = false;\n\tprivate String revision;\n\tFile file = null;\n\n\tpublic NR_Bootloader_GUI() {\n\t\t//// com.neuronrobotics.sdk.common.Log.error(\"Starting GUI\");\n\n\t\tfileButton = new JButton();\n\t\tfileButton.addActionListener(this);\n\t\tresetFile();\n\n\t\t// portButton = new JButton();\n\t\t// portButton.addActionListener(this);\n\t\tresetPort();\n\n\t\tloadButton = new JButton();\n\t\tloadButton.addActionListener(this);\n\t\tresetLoad();\n\n\t\tJPanel buttonPanel = new JPanel();\n\t\tbuttonPanel.setLayout(new MigLayout());\n\t\tbuttonPanel.add(fileButton, \"wrap\");\n\t\t// buttonPanel.add(portButton, \"wrap\");\n\t\tbuttonPanel.add(loadButton, \"wrap\");\n\t\tbuttonPanel.add(new JLabel(\"Progress:\"), \"wrap\");\n\t\tbuttonPanel.add(progress, \"wrap\");\n\n\t\tframe = new JFrame(\"NR Bootloader\");\n\t\tframe.add(buttonPanel);\n\t\tframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);\n\t\tframe.pack();\n\t\tframe.setLocationRelativeTo(null);\n\t\tframe.setVisible(true);\n\t}\n\n\tpublic void actionPerformed(ActionEvent e) {\n\t\t// Handle open button action.\n\t\tif (e.getSource() == fileButton) {\n\t\t\tloadFile();\n\t\t}\n\t\t// if (e.getSource() == portButton) {\n\t\t// ////com.neuronrobotics.sdk.common.Log.error(\"Go for port selection\");\n\t\t// setPortSelect(false);\n\t\t// try {\n\t\t// Thread.sleep(50);\n\t\t// } catch (InterruptedException e1) {}\n\t\t// setPortSelect(true);\n\t\t// try{\n\t\t// getBlApp().getDevice().disconnect();\n\t\t// }catch(Exception ex){}\n\t\t// resetLoad();\n\t\t//\n\t\t// }\n\t\tif (e.getSource() == loadButton) {\n\t\t\ttry {\n\t\t\t\tif (getBlApp() != null) {\n\t\t\t\t\t//// com.neuronrobotics.sdk.common.Log.error(\"Loading firmware\");\n\t\t\t\t\treloadFile();\n\t\t\t\t\tgetBlApp().loadCores(hex.getCores());\n\t\t\t\t\tloadButton.setText(\"Loading....\");\n\t\t\t\t\tloadButton.setEnabled(false);\n\t\t\t\t\tLoaderChecker l = new LoaderChecker();\n\t\t\t\t\tl.start();\n\t\t\t\t}\n\t\t\t} catch (InvalidResponseException ex) {\n\t\t\t\tex.printStackTrace();\n\t\t\t\tString message = \"Device is not in bootloader mode!\";\n\t\t\t\tJOptionPane.showMessageDialog(null, message, message, JOptionPane.ERROR_MESSAGE);\n\t\t\t} catch (NoConnectionAvailableException ex) {\n\t\t\t\tex.printStackTrace();\n\t\t\t\tString message = \"Device is not no longer connected to bootloader\";\n\t\t\t\tJOptionPane.showMessageDialog(null, message, message, JOptionPane.ERROR_MESSAGE);\n\t\t\t\tresetPort();\n\t\t\t\tresetLoad();\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void setPortSelect(boolean portSelect) {\n\t\tthis.portSelect = portSelect;\n\t}\n\n\tpublic boolean isPortSelect() {\n\t\treturn portSelect;\n\t}\n\n\tpublic void setBlApp(NRBoot blApp) {\n\t\tthis.blApp = blApp;\n\t\t// portButton.setText(\"Using:\"+blApp.getDevice().getConnection().toString());\n\t\tframe.pack();\n\t\tloadButton.setEnabled(true);\n\t\tportStatus.setStatus(StatusLabel.OK);\n\t}\n\n\tpublic NRBoot getBlApp() {\n\t\treturn blApp;\n\t}\n\n\tpublic void resetFile() {\n\t\tfileButton.setText(\"Select a NR Firmware File..\");\n\t}\n\n\tpublic void resetPort() {\n\t\t// portButton.setEnabled(true);\n\t\t// portButton.setText(\"Select Port\");\n\t\tportStatus.setStatus(0);\n\t\tthis.portSelect = false;\n\t}\n\n\tpublic void resetLoad() {\n\t\tloadButton.setEnabled(false);\n\t\tloadButton.setText(\"Load Firmware\");\n\t\tloadStatus.setStatus(0);\n\t\treloadFile();\n\t}\n\n\tpublic void loadFile() {\n\t\t// com.neuronrobotics.sdk.common.Log.error(\"Loading file:\");\n\t\tJFileChooser fc = new JFileChooser();\n\t\tFile dir1 = new File(\".\");\n\t\tFile dir2 = null;\n\t\ttry {\n\t\t\tdir2 = new File(System.getProperty(\"user.home\") + \"/git/dyio/FirmwarePublish/Dev/\");\n\t\t} catch (Exception ex) {\n\t\t\tex.printStackTrace();\n\t\t}\n\t\tif (file != null) {\n\t\t\tfc.setSelectedFile(file);\n\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Starting with: \" + file.getAbsolutePath());\n\t\t} else {\n\t\t\tif (dir2 == null) {\n\t\t\t\tfc.setCurrentDirectory(dir1);\n\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Starting in: \" + dir1.getAbsolutePath());\n\t\t\t} else {\n\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Starting in: \" + dir2.getAbsolutePath());\n\t\t\t\tfc.setCurrentDirectory(dir2);\n\t\t\t}\n\t\t}\n\t\tfc.setFileFilter(new FileFilter() {\n\n\t\t\t@Override\n\t\t\tpublic String getDescription() {\n\t\t\t\treturn \"NR Firmware Format (xml)\";\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic boolean accept(File f) {\n\t\t\t\tif (f.isDirectory()) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\tString path = f.getAbsolutePath().toLowerCase();\n\t\t\t\tif (path.endsWith(\"xml\") && path.charAt(path.length() - 3) == '.') {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\treturn f.getName().matches(\".+\\\\.xml$\");\n\t\t\t}\n\t\t});\n\n\t\tint returnVal = fc.showOpenDialog(null);\n\t\tif (returnVal == JFileChooser.APPROVE_OPTION) {\n\t\t\tfile = fc.getSelectedFile();\n\t\t\tif (!file.getName().matches(\".+\\\\.xml$\")) {\n\t\t\t\tString message = \"Invalid file type. Must be .xml\";\n\t\t\t\tJOptionPane.showMessageDialog(null, message, message, JOptionPane.ERROR_MESSAGE);\n\t\t\t\tfileStatus.setStatus(StatusLabel.ERROR);\n\t\t\t\tresetFile();\n\t\t\t\tresetLoad();\n\t\t\t\tresetPort();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\treloadFile();\n\t\t}\n\t}\n\n\tprivate void reloadFile() {\n\t\tif (file != null) {\n\t\t\ttry {\n\t\t\t\thex = new Hexml(file);\n\t\t\t} catch (Exception e) {\n\t\t\t\te.printStackTrace();\n\t\t\t\tString message = \"Invalid xml file\";\n\t\t\t\tJOptionPane.showMessageDialog(null, message, message, JOptionPane.ERROR_MESSAGE);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tfileButton.setText(\"Using: \" + file.getName());\n\t\t\tframe.pack();\n\t\t\trevision = hex.getRevision();\n\t\t\t// portButton.setEnabled(true);\n\t\t\tfileStatus.setStatus(StatusLabel.OK);\n\t\t}\n\t}\n\n\tpublic String getRevision() {\n\t\treturn revision;\n\t}\n\n\tprivate void resetAll() {\n\t\tgetBlApp().getDevice().disconnect();\n\t\tresetPort();\n\t\tresetLoad();\n\t}\n\tpublic class LoaderChecker extends Thread {\n\t\tpublic void run() {\n\t\t\tprogress.setMaximum(getBlApp().getProgressMax());\n\t\t\tprogress.setMinimum(0);\n\t\t\t// progress.setIndeterminate(true);\n\t\t\twhile (getBlApp().isLoadDone() == false) {\n\t\t\t\ttry {\n\t\t\t\t\tThread.sleep(100);\n\t\t\t\t} catch (InterruptedException e) {\n\t\t\t\t}\n\t\t\t\tprogress.setValue(getBlApp().getProgressValue());\n\t\t\t}\n\t\t\tprogress.setIndeterminate(false);\n\t\t\tString message = \"Success! Your Bowler device is now updated to version: \" + hex.getRevision();\n\t\t\tJOptionPane.showMessageDialog(null, message, message, JOptionPane.INFORMATION_MESSAGE);\n\t\t\tresetAll();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/nrconsole/plugin/bootloader/gui/StatusLabel.java",
    "content": "package com.neuronrobotics.nrconsole.plugin.bootloader.gui;\n\nimport com.neuronrobotics.bowlerstudio.assets.AssetFactory;\n\nimport javax.swing.*;\nimport java.net.MalformedURLException;\n\npublic class StatusLabel extends JLabel {\n\tprivate static final long serialVersionUID = 1L;\n\tpublic static final int OK = 1;\n\tpublic static final int ERROR = 2;\n\n\tpublic StatusLabel() {\n\t\tsetStatus(0);\n\t}\n\n\tpublic void setStatus(int status) {\n\t\tswitch (status) {\n\t\t\tcase OK :\n\t\t\t\tsetIcon(createImageIcon(\"dyio/ok.png\"));\n\t\t\t\tbreak;\n\t\t\tcase ERROR :\n\t\t\t\tsetIcon(createImageIcon(\"dyio/error.png\"));\n\t\t\t\tbreak;\n\t\t\tdefault :\n\t\t\t\tsetIcon(createImageIcon(\"dyio/blank.png\"));\n\t\t\t\tbreak;\n\t\t}\n\n\t\tinvalidate();\n\t\t// repaint();\n\t}\n\n\tprotected ImageIcon createImageIcon(String path) {\n\t\tjava.net.URL imgURL;\n\t\ttry {\n\t\t\timgURL = AssetFactory.loadFile(path).toURI().toURL();\n\t\t\tif (imgURL != null)\n\t\t\t\treturn new ImageIcon(imgURL);\n\t\t} catch (MalformedURLException e) {\n\t\t\t// Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t} catch (Exception e) {\n\t\t\t// Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t}\n\n\t\tcom.neuronrobotics.sdk.common.Log.error(\"Couldn't find file: \" + path);\n\t\treturn null;\n\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/nrconsole/util/CommitWidget.java",
    "content": "package com.neuronrobotics.nrconsole.util;\n\nimport java.io.File;\nimport java.util.Optional;\n\n//import org.jfree.util.Log;\n\nimport com.neuronrobotics.bowlerstudio.BowlerStudio;\nimport com.neuronrobotics.bowlerstudio.IssueReportingExceptionHandler;\nimport com.neuronrobotics.bowlerstudio.assets.FontSizeManager;\nimport com.neuronrobotics.bowlerstudio.scripting.ScriptingEngine;\nimport com.neuronrobotics.sdk.common.Log;\nimport com.neuronrobotics.video.OSUtil;\n\nimport javafx.geometry.Insets;\nimport javafx.scene.Node;\nimport javafx.scene.control.ButtonType;\nimport javafx.scene.control.Dialog;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.TextArea;\nimport javafx.scene.control.TextField;\nimport javafx.scene.control.ButtonBar.ButtonData;\nimport javafx.scene.layout.GridPane;\nimport javafx.stage.Modality;\nimport javafx.stage.Stage;\nimport javafx.util.Pair;\n\npublic class CommitWidget {\n\tpublic static void commit(File currentFile, String code) {\n\t\tif (code != null)\n\t\t\tif (code.length() < 1) {\n\t\t\t\tLog.error(\"COmmit failed with no code to commit\");\n\t\t\t\treturn;\n\t\t\t}\n\t\tBowlerStudio.runLater(() -> {\n\t\t\t// Create the custom dialog.\n\t\t\tDialog<Pair<String, String>> dialog = new Dialog<>();\n\t\t\tdialog.setTitle(\"Commit message Dialog\");\n\t\t\tdialog.setHeaderText(\"Enter a commit message to publish changes\");\n\n\t\t\t// Set the button types.\n\t\t\tButtonType loginButtonType = new ButtonType(\"Publish\", ButtonData.OK_DONE);\n\t\t\tdialog.getDialogPane().getButtonTypes().addAll(loginButtonType, ButtonType.CANCEL);\n\n\t\t\t// Create the username and password labels and fields.\n\t\t\tGridPane grid = new GridPane();\n\t\t\tgrid.setHgap(10);\n\t\t\tgrid.setVgap(10);\n\t\t\tgrid.setPadding(new Insets(20, 150, 10, 10));\n\n\t\t\tTextField username = new TextField();\n\t\t\tusername.setPromptText(\"60 character description\");\n\t\t\tTextArea password = new TextArea();\n\t\t\tpassword.setPrefRowCount(5);\n\t\t\tpassword.setPrefColumnCount(40);\n\t\t\tpassword.setPromptText(\"Full Sentences describing explanation\");\n\n\t\t\tgrid.add(new Label(\"What did you change?\"), 0, 0);\n\t\t\tgrid.add(username, 1, 0);\n\t\t\tgrid.add(new Label(\"Why did you change it?\"), 0, 1);\n\t\t\tgrid.add(password, 1, 1);\n\n\t\t\t// Enable/Disable login button depending on whether a username was entered.\n\t\t\tNode loginButton = dialog.getDialogPane().lookupButton(loginButtonType);\n\t\t\tloginButton.setDisable(true);\n\n\t\t\t// Do some validation (using the Java 8 lambda syntax).\n\t\t\tusername.textProperty().addListener((observable, oldValue, newValue) -> {\n\t\t\t\tloginButton.setDisable(newValue.trim().length() < 5);\n\t\t\t});\n\n\t\t\tdialog.getDialogPane().setContent(grid);\n\n\t\t\t// Request focus on the username field by default.\n\t\t\tBowlerStudio.runLater(() -> username.requestFocus());\n\n\t\t\t// Convert the result to a username-password-pair when the login button is\n\t\t\t// clicked.\n\t\t\tdialog.setResultConverter(dialogButton -> {\n\t\t\t\tif (dialogButton == loginButtonType) {\n\t\t\t\t\treturn new Pair<>(username.getText(), password.getText());\n\t\t\t\t}\n\t\t\t\treturn null;\n\t\t\t});\n\t\t\tif (OSUtil.isOSX()) {\n\t\t\t\tModality mode = Modality.NONE;\n\t\t\t\tdialog.initModality(mode);\n\t\t\t}\n\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Show commit Dialog\");\n\t\t\tNode root = dialog.getDialogPane();\n\t\t\tStage stage = (Stage) dialog.getDialogPane().getScene().getWindow();\n\n\t\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\t\tint tmp = fontNum - 10;\n\t\t\t\tif (tmp < 12)\n\t\t\t\t\ttmp = 12;\n\t\t\t\troot.setStyle(\"-fx-font-size: \" + tmp + \"pt\");\n\t\t\t\tdialog.getDialogPane().applyCss();\n\t\t\t\tdialog.getDialogPane().layout();\n\t\t\t\tstage.sizeToScene();\n\t\t\t});\n\t\t\tOptional<Pair<String, String>> result = dialog.showAndWait();\n\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Commit Dialog finished\");\n\t\t\tdialog.close();\n\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Result: \" + result);\n\t\t\tresult.ifPresent(commitBody -> {\n\t\t\t\tnew Thread() {\n\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\tThread.currentThread().setUncaughtExceptionHandler(new IssueReportingExceptionHandler());\n\t\t\t\t\t\tString message = commitBody.getKey() + \"\\n\\n\" + commitBody.getValue();\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tString remote = ScriptingEngine.locateGitUrl(currentFile);\n\t\t\t\t\t\t\tString relativePath = ScriptingEngine.findLocalPath(currentFile);\n\t\t\t\t\t\t\tScriptingEngine.pull(remote);\n\t\t\t\t\t\t\tScriptingEngine.pushCodeToGit(remote, ScriptingEngine.getFullBranch(remote), relativePath,\n\t\t\t\t\t\t\t\t\tcode, message, true);\n\t\t\t\t\t\t} catch (Exception e1) {\n\t\t\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\t\t\te1.printStackTrace();\n\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}.start();\n\t\t\t});\n\t\t});\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/nrconsole/util/CompoundSlider.java",
    "content": "package com.neuronrobotics.nrconsole.util;\n\nimport javax.swing.JPanel;\n\npublic class CompoundSlider extends JPanel {\n\n\t/**\n\t * long\n\t */\n\tprivate static final long serialVersionUID = 6803306324376497720L;\n\tprivate int lb, ub;\n\tpublic CompoundSlider(int lowerBound, int upperBound, int center, double scale) {\n\t\tlb = lowerBound;\n\t\tub = upperBound;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/nrconsole/util/DirectoryFilter.java",
    "content": "package com.neuronrobotics.nrconsole.util;\n\nimport java.io.File;\n\nimport javax.swing.filechooser.FileFilter;\n\npublic class DirectoryFilter extends FileFilter {\n\n\tpublic String getDescription() {\n\t\treturn \"Select Directory\";\n\t}\n\n\tpublic boolean accept(File f) {\n\t\tif (f.isDirectory()) {\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/nrconsole/util/FileSelectionFactory.java",
    "content": "package com.neuronrobotics.nrconsole.util;\n\nimport java.io.File;\n\nimport javafx.stage.DirectoryChooser;\nimport javafx.stage.FileChooser;\nimport javafx.stage.FileChooser.ExtensionFilter;\nimport javafx.stage.Stage;\n\nimport com.neuronrobotics.bowlerstudio.BowlerStudio;\nimport com.neuronrobotics.bowlerstudio.BowlerStudioModularFrame;\nimport com.neuronrobotics.sdk.util.ThreadUtil;\n\npublic class FileSelectionFactory {\n\tprivate static Stage stage = null;\n\n\tprivate FileSelectionFactory() {\n\t}\n\n\tprivate static class fileHolder {\n\t\tprivate boolean done = false;\n\t\tprivate File file = null;\n\n\t\tpublic boolean isDone() {\n\t\t\treturn done;\n\t\t}\n\n\t\tpublic void setDone(boolean done) {\n\t\t\tthis.done = done;\n\t\t}\n\n\t\tpublic File getFile() {\n\t\t\treturn file;\n\t\t}\n\n\t\tpublic void setFile(File file) {\n\t\t\tthis.file = file;\n\t\t}\n\t}\n\n\tpublic static File GetFile(File start, boolean save, ExtensionFilter... filter) {\n\t\tif (start == null)\n\t\t\tthrow new NullPointerException();\n\n\t\tfinal fileHolder file = new fileHolder();\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tFileChooser fileChooser = new FileChooser();\n\n\t\t\tfileChooser.setInitialDirectory(start.isDirectory() ? start : start.getParentFile());\n\t\t\tif (filter != null)\n\t\t\t\tfileChooser.getExtensionFilters().addAll(filter);\n\t\t\tfileChooser.setTitle(\"Bowler File Chooser\");\n\t\t\tif (save)\n\t\t\t\tfile.setFile(fileChooser.showSaveDialog(getStage()));\n\t\t\telse\n\t\t\t\tfile.setFile(fileChooser.showOpenDialog(getStage()));\n\t\t\tfile.setDone(true);\n\n\t\t});\n\t\twhile (!file.isDone()) {\n\t\t\tThreadUtil.wait(16);\n\t\t}\n\n\t\treturn file.getFile();\n\t}\n\n\tprivate static Stage getStage() {\n\t\tif (stage == null)\n\t\t\tstage = BowlerStudioModularFrame.getPrimaryStage();\n\t\treturn stage;\n\t}\n\n\tpublic static File GetFile(File start, ExtensionFilter... filter) {\n\t\treturn GetFile(start, false, filter);\n\t}\n\n\tpublic static File GetDirectory(File start) {\n\t\tif (start == null)\n\t\t\tthrow new NullPointerException();\n\n\t\tfinal fileHolder file = new fileHolder();\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tDirectoryChooser fileChooser = new DirectoryChooser();\n\n\t\t\tfileChooser.setInitialDirectory(start.isDirectory() ? start : start.getParentFile());\n\t\t\tfileChooser.setTitle(\"Bowler File Chooser\");\n\t\t\tfile.setFile(fileChooser.showDialog(getStage()));\n\t\t\tfile.setDone(true);\n\n\t\t});\n\t\twhile (!file.isDone()) {\n\t\t\tThreadUtil.wait(16);\n\t\t}\n\n\t\treturn file.getFile();\n\t}\n\n\tpublic static void setStage(Stage stage) {\n\t\tFileSelectionFactory.stage = stage;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/nrconsole/util/GCodeFilter.java",
    "content": "package com.neuronrobotics.nrconsole.util;\n\nimport java.io.File;\n\nimport javax.swing.filechooser.FileFilter;\n\npublic class GCodeFilter extends FileFilter {\n\n\tpublic String getDescription() {\n\t\treturn \"GCode File\";\n\t}\n\n\tpublic boolean accept(File f) {\n\t\tif (f.isDirectory()) {\n\t\t\treturn true;\n\t\t}\n\t\tString path = f.getAbsolutePath().toLowerCase();\n\t\tif (path.endsWith(\"gcode\") && path.charAt(path.length() - 5) == '.') {\n\t\t\treturn true;\n\t\t}\n\t\tif (path.endsWith(\"ngc\") && path.charAt(path.length() - 3) == '.') {\n\t\t\treturn true;\n\t\t}\n\t\treturn f.getName().matches(\".+\\\\.gcode$\") || f.getName().matches(\".+\\\\.ngc$\");\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/nrconsole/util/GroovyFilter.java",
    "content": "package com.neuronrobotics.nrconsole.util;\n\nimport java.io.File;\n\nimport javax.swing.filechooser.FileFilter;\n\npublic class GroovyFilter extends FileFilter {\n\n\tpublic String getDescription() {\n\t\treturn \"Script (.groovy .java)\";\n\t}\n\n\tpublic boolean accept(File f) {\n\t\treturn true;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/nrconsole/util/IntegerComboBox.java",
    "content": "package com.neuronrobotics.nrconsole.util;\n\nimport javax.swing.JComboBox;\n\npublic class IntegerComboBox extends JComboBox {\n\n\t/**\n\t *\n\t */\n\tprivate static final long serialVersionUID = 2439771311089831575L;\n\n\tprivate final String noneString = \"None\";\n\tprivate int noneValue = 0xff;\n\n\tpublic IntegerComboBox() {\n\t}\n\n\tpublic IntegerComboBox(boolean withNoneOption, int noneVal) {\n\t\tif (withNoneOption) {\n\t\t\taddItem(noneString);\n\t\t\tnoneValue = noneVal;\n\t\t}\n\t}\n\n\tpublic void removeInteger(int in) {\n\t\tfor (int i = 0; i < getItemCount(); i++) {\n\t\t\ttry {\n\t\t\t\tInteger selected = (Integer) (getItemAt(i));\n\t\t\t\tif (selected != null && selected.intValue() == in) {\n\t\t\t\t\tremoveItemAt(i);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} catch (ClassCastException ex) {\n\t\t\t\t// ingnore the None case\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void addInteger(int in) {\n\t\tfor (int i = 0; i < getItemCount(); i++) {\n\t\t\ttry {\n\t\t\t\tInteger selected = (Integer) (getItemAt(i));\n\t\t\t\tif (selected != null && selected.intValue() == in) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} catch (ClassCastException ex) {\n\t\t\t\t// ingnore the None case\n\t\t\t}\n\t\t}\n\t\taddItem(in);\n\t}\n\n\tpublic void setNoneItemSelected() {\n\t\tfor (int i = 0; i < getItemCount(); i++) {\n\t\t\ttry {\n\t\t\t\tInteger selected = (Integer) (getItemAt(i));\n\t\t\t} catch (ClassCastException ex) {\n\t\t\t\tsetSelectedItem(getItemAt(i));\n\t\t\t}\n\t\t}\n\t\tsetSelectedItem(getItemAt(0));\n\t}\n\n\tpublic void setSelectedInteger(int in) {\n\t\tfor (int i = 0; i < getItemCount(); i++) {\n\t\t\ttry {\n\t\t\t\tInteger selected = (Integer) (getItemAt(i));\n\t\t\t\tif (selected != null && selected.intValue() == in) {\n\t\t\t\t\tsetSelectedItem(getItemAt(i));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} catch (ClassCastException ex) {\n\t\t\t\t// ignore\n\t\t\t}\n\t\t}\n\t\tif (in == noneValue) {\n\t\t\tsetNoneItemSelected();\n\t\t\treturn;\n\t\t}\n\t\taddInteger(in);\n\t\tsetSelectedInteger(in);\n\t}\n\n\tpublic int getSelectedInteger() {\n\t\ttry {\n\t\t\treturn Integer.parseInt(getSelectedItem().toString());\n\t\t} catch (NumberFormatException ex) {\n\t\t\treturn noneValue;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/nrconsole/util/Mp3Filter.java",
    "content": "package com.neuronrobotics.nrconsole.util;\n\nimport java.io.File;\n\nimport javax.swing.filechooser.FileFilter;\n\npublic class Mp3Filter extends FileFilter {\n\n\tpublic String getDescription() {\n\t\treturn \"wav Audio File (wav)\";\n\t}\n\n\tpublic boolean accept(File f) {\n\t\tif (f.isDirectory()) {\n\t\t\treturn true;\n\t\t}\n\t\tString path = f.getAbsolutePath().toLowerCase();\n\t\tif (path.endsWith(\"wav\") && path.charAt(path.length() - 3) == '.') {\n\t\t\treturn true;\n\t\t}\n\t\treturn f.getName().matches(\".+\\\\.wav$\");\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/nrconsole/util/NRConsoleDocumentationFactory.java",
    "content": "package com.neuronrobotics.nrconsole.util;\n\nimport java.net.URI;\n//\n//import com.neuronrobotics.nrconsole.MenuBar;\n//import com.neuronrobotics.nrconsole.plugin.DyIO.DyIOPanel;\n//import com.neuronrobotics.nrconsole.plugin.DyIO.GoogleChat.GoogleChatLogin;\nimport com.neuronrobotics.sdk.common.BowlerDocumentationFactory;\n\n/**\n * Factory used to centralize references to web pages (specifically\n * documentation). Any documentation for an object type defined in NRConsole\n * will be found here. (Hint: if the URI refers to an object type defined in\n * NRConsole, it goes here).\n *\n * Calls BowlerDocumentationFactory.getDocumentationURL(input) first, if type\n * not found it continues here.\n *\n * See also: BowlerDocumentationFactory.java\n *\n */\npublic class NRConsoleDocumentationFactory {\n\n\tprivate NRConsoleDocumentationFactory() {\n\t}\n\n\tpublic static URI getDocumentationURL(Object input) {\n\n\t\ttry {\n\t\t\treturn BowlerDocumentationFactory.getDocumentationURL(input);\n\t\t} catch (RuntimeException e) {\n\t\t\t// if (input instanceof MenuBar) {\n\t\t\t//\n\t\t\t// try {\n\t\t\t// return new URI(\n\t\t\t// \"http://neuronrobotics.github.io/DyIO-First-Steps/Testing-With-NR-Console\");\n\t\t\t// } catch (URISyntaxException uriE) {\n\t\t\t// Log.error(e.getMessage());\n\t\t\t// }\n\t\t\t//\n\t\t\t// } else if (input instanceof DyIOPanel) {\n\t\t\t//\n\t\t\t// try {\n\t\t\t// return new URI(\n\t\t\t// \"http://neuronrobotics.github.io/Getting-to-Know-the-DyIO/Anatomy-of-the-DyIO/\");\n\t\t\t// } catch (URISyntaxException uriE) {\n\t\t\t// Log.error(e.getMessage());\n\t\t\t// }\n\t\t\t//\n\t\t\t// } else if (input instanceof GoogleChatLogin) {\n\t\t\t// try {\n\t\t\t// return new URI(\n\t\t\t// \"http://wiki.neuronrobotics.com/DyIO_Cloud_Connect\");\n\t\t\t// } catch (URISyntaxException uriE) {\n\t\t\t// Log.error(e.getMessage());\n\t\t\t// }\n\t\t\t// }\n\n\t\t\tthrow new RuntimeException(\"No documentation for object of type \" + input.getClass());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/nrconsole/util/PrefsLoader.java",
    "content": "package com.neuronrobotics.nrconsole.util;\n\nimport java.util.prefs.Preferences;\n\npublic class PrefsLoader {\n\tprivate static final String SLIC3R_LOCATION = \"slic3r_path\";\n\tprivate static final String SLIC3R_RDBTN_LAST = \"slic3r_rdbtn_last\";\n\tstatic String path = \"/usr/local/Slic3r/bin/slic3r\";\n\tPreferences prefs = Preferences.userNodeForPackage(this.getClass());\n\n\tpublic String getSlic3rLocation() {\n\t\treturn prefs.get(SLIC3R_LOCATION, path);\n\t}\n\n\tpublic void setSlic3rLocation(String _path) {\n\t\tprefs.put(SLIC3R_LOCATION, _path);\n\t}\n\n\tpublic int getSlic3rRDBTNLast() {\n\t\treturn prefs.getInt(SLIC3R_RDBTN_LAST, 0);\n\t}\n\n\tpublic void setSlic3rRDBTNLast(int _btn) {\n\t\tprefs.putInt(SLIC3R_RDBTN_LAST, _btn);\n\t}\n\n\tpublic void loadDefaults() {\n\t\tsetSlic3rLocation(path);\n\t\tsetSlic3rRDBTNLast(0);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/nrconsole/util/PromptForGit.java",
    "content": "package com.neuronrobotics.nrconsole.util;\n\nimport java.util.ArrayList;\nimport java.util.Optional;\n\nimport com.neuronrobotics.bowlerstudio.BowlerStudio;\nimport com.neuronrobotics.bowlerstudio.assets.FontSizeManager;\nimport com.neuronrobotics.bowlerstudio.creature.IGistPromptCompletionListener;\nimport com.neuronrobotics.bowlerstudio.scripting.ScriptingEngine;\n\nimport javafx.scene.Node;\nimport javafx.scene.control.ChoiceDialog;\nimport javafx.scene.control.TextInputDialog;\nimport javafx.stage.Stage;\n\npublic class PromptForGit {\n\tprivate PromptForGit() {\n\t}\n\n\tpublic static void prompt(String purpose, String defaultID, IGistPromptCompletionListener listener) {\n\t\tBowlerStudio.runLater(() -> {\n\t\t\tTextInputDialog alert = new TextInputDialog(defaultID);\n\t\t\talert.setTitle(purpose);\n\t\t\talert.setHeaderText(\"Enter the URL (Clone vie HTTPS)\");\n\t\t\talert.setContentText(\"Git Clone URL: \");\n\t\t\talert.setResizable(true);\n\t\t\talert.setWidth(800);\n\t\t\tNode rt = alert.getDialogPane();\n\t\t\tStage st = (Stage) alert.getDialogPane().getScene().getWindow();\n\t\t\tst.setOnCloseRequest(ev -> alert.hide());\n\t\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\t\tint tmp = fontNum - 10;\n\t\t\t\tif (tmp < 12)\n\t\t\t\t\ttmp = 12;\n\t\t\t\trt.setStyle(\"-fx-font-size: \" + tmp + \"pt\");\n\t\t\t\talert.getDialogPane().applyCss();\n\t\t\t\talert.getDialogPane().layout();\n\t\t\t\tst.sizeToScene();\n\t\t\t});\n\t\t\t// Traditional way to get the response value.\n\t\t\tOptional<String> result = alert.showAndWait();\n\t\t\tif (result.isPresent()) {\n\n\t\t\t\tString gistcode = null;\n\t\t\t\tif (result.get().endsWith(\".git\"))\n\t\t\t\t\tgistcode = result.get();\n\t\t\t\telse\n\t\t\t\t\tgistcode = \"https://gist.github.com/\" + ScriptingEngine.urlToGist(result.get()) + \".git\";\n\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Creature Git \" + gistcode);\n\t\t\t\tArrayList<String> choices;\n\t\t\t\tString suggestedChoice = \"\";\n\t\t\t\tint numXml = 0;\n\t\t\t\ttry {\n\t\t\t\t\tchoices = ScriptingEngine.filesInGit(gistcode);\n\t\t\t\t\tfor (int i = 0; i < choices.size(); i++) {\n\t\t\t\t\t\tString s = choices.get(i);\n\t\t\t\t\t\tsuggestedChoice = s;\n\t\t\t\t\t\tnumXml++;\n\n\t\t\t\t\t}\n\t\t\t\t\tChoiceDialog<String> d = new ChoiceDialog<>(suggestedChoice, choices);\n\t\t\t\t\td.setTitle(\"Choose a file in the git\");\n\t\t\t\t\td.setHeaderText(\"Select from the files in the git to pick the Creature File\");\n\t\t\t\t\td.setContentText(\"Choose A Creature:\");\n\t\t\t\t\tNode root = d.getDialogPane();\n\t\t\t\t\tStage stage = (Stage) d.getDialogPane().getScene().getWindow();\n\t\t\t\t\tstage.setOnCloseRequest(ev -> d.hide());\n\t\t\t\t\tFontSizeManager.addListener(fontNum -> {\n\t\t\t\t\t\tint tmp = fontNum - 10;\n\t\t\t\t\t\tif (tmp < 12)\n\t\t\t\t\t\t\ttmp = 12;\n\t\t\t\t\t\troot.setStyle(\"-fx-font-size: \" + tmp + \"pt\");\n\t\t\t\t\t\td.getDialogPane().applyCss();\n\t\t\t\t\t\td.getDialogPane().layout();\n\t\t\t\t\t\tstage.sizeToScene();\n\t\t\t\t\t});\n\t\t\t\t\t// Traditional way to get the response value.\n\t\t\t\t\tOptional<String> r = d.showAndWait();\n\t\t\t\t\tif (r.isPresent()) {\n\t\t\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Your choice: \" + r.get());\n\t\t\t\t\t\tlistener.done(gistcode, r.get());\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\n\t\t\t\tif (numXml == 1) {\n\t\t\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"Found just one file at \" +\n\t\t\t\t\t// suggestedChoice);\n\t\t\t\t\t// loadMobilebaseFromGist(gistcode,suggestedChoice);\n\t\t\t\t\t// return;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t});\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/nrconsole/util/Slic3rFilter.java",
    "content": "package com.neuronrobotics.nrconsole.util;\n\nimport java.io.File;\n\nimport javax.swing.filechooser.FileFilter;\n\npublic class Slic3rFilter extends FileFilter {\n\n\tpublic String getDescription() {\n\t\treturn \"Slic3r Executable\";\n\t}\n\n\tpublic boolean accept(File f) {\n\t\tif (f.isDirectory()) {\n\t\t\treturn true;\n\t\t}\n\t\tif (f.getName().toLowerCase().contains(\"slic3r\")) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn f.getName().toLowerCase().matches(\"slic3r\");\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/nrconsole/util/StlFilter.java",
    "content": "package com.neuronrobotics.nrconsole.util;\n\nimport java.io.File;\n\nimport javax.swing.filechooser.FileFilter;\n\npublic class StlFilter extends FileFilter {\n\n\tpublic String getDescription() {\n\t\treturn \"STL File\";\n\t}\n\n\tpublic boolean accept(File f) {\n\t\tif (f.isDirectory()) {\n\t\t\treturn true;\n\t\t}\n\t\tString path = f.getAbsolutePath().toLowerCase();\n\t\tif (path.toLowerCase().endsWith(\"stl\") && path.charAt(path.length() - 3) == '.') {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn f.getName().matches(\".+\\\\.stl$\");\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/nrconsole/util/XmlFilter.java",
    "content": "package com.neuronrobotics.nrconsole.util;\n\nimport java.io.File;\n\nimport javax.swing.filechooser.FileFilter;\n\npublic class XmlFilter extends FileFilter {\n\n\tpublic String getDescription() {\n\t\treturn \"Configuration (xml)\";\n\t}\n\n\tpublic boolean accept(File f) {\n\t\tif (f.isDirectory()) {\n\t\t\treturn true;\n\t\t}\n\t\tString path = f.getAbsolutePath().toLowerCase();\n\t\tif (path.endsWith(\"xml\") && path.charAt(path.length() - 3) == '.') {\n\t\t\treturn true;\n\t\t}\n\t\treturn f.getName().matches(\".+\\\\.xml$\");\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/pidsim/CSVWriter.java",
    "content": "package com.neuronrobotics.pidsim;\n\nimport java.io.BufferedWriter;\nimport java.io.File;\nimport java.io.FileWriter;\nimport java.io.IOException;\n\nimport org.jfree.data.xy.XYDataItem;\nimport org.jfree.data.xy.XYSeries;\n\npublic class CSVWriter {\n\n\tprivate BufferedWriter writer;\n\n\tpublic void setFile(File f) {\n\t\tif (!f.getName().endsWith(\".csv\")) {\n\t\t\tf = new File(f.getAbsolutePath() + \".csv\");\n\t\t}\n\n\t\tFileWriter fstream;\n\t\ttry {\n\t\t\tfstream = new FileWriter(f.getAbsolutePath());\n\t\t} catch (IOException e) {\n\t\t\t// Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t\treturn;\n\t\t}\n\n\t\twriter = new BufferedWriter(fstream);\n\t}\n\n\tpublic void addData(XYSeries data) {\n\n\t\tXYSeries cache;\n\t\ttry {\n\t\t\tcache = data.createCopy(0, data.getItemCount() - 1);\n\t\t} catch (CloneNotSupportedException e) {\n\t\t\t// Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\twriter.write(\"Time (s),Position (degrees)\\n\");\n\t\t\tfor (Object o : cache.getItems()) {\n\t\t\t\tXYDataItem i = (XYDataItem) o;\n\t\t\t\twriter.write(i.getXValue() + \",\" + i.getYValue() + \"\\n\");\n\t\t\t}\n\t\t} catch (IOException e) {\n\n\t\t}\n\n\t}\n\n\tpublic void cleanup() {\n\t\ttry {\n\t\t\twriter.close();\n\t\t} catch (IOException e) {\n\t\t\t// Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/pidsim/DataPanel.java",
    "content": "/* ---------------------\n * DynamicDataDemo2.java\n * ---------------------\n * (C) Copyright 2003-2009, by Object Refinery Limited.\n *\n */\n\npackage com.neuronrobotics.pidsim;\n\nimport java.awt.BasicStroke;\nimport java.awt.BorderLayout;\nimport java.awt.Color;\nimport java.awt.Font;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.io.File;\n\nimport javax.swing.JButton;\nimport javax.swing.JFileChooser;\nimport javax.swing.JPanel;\n\nimport org.jfree.chart.ChartFactory;\nimport org.jfree.chart.ChartPanel;\nimport org.jfree.chart.ChartUtilities;\nimport org.jfree.chart.JFreeChart;\nimport org.jfree.chart.axis.ValueAxis;\nimport org.jfree.chart.plot.Marker;\nimport org.jfree.chart.plot.ValueMarker;\nimport org.jfree.chart.plot.XYPlot;\nimport org.jfree.chart.renderer.xy.DefaultXYItemRenderer;\nimport org.jfree.data.xy.XYSeries;\nimport org.jfree.data.xy.XYSeriesCollection;\nimport org.jfree.ui.LengthAdjustmentType;\nimport org.jfree.ui.RectangleAnchor;\nimport org.jfree.ui.TextAnchor;\n\npublic class DataPanel extends JPanel implements ActionListener {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\tprivate XYPlot plot;\n\n\tprivate XYSeries positionSer;\n\tprivate Marker setpoint = new ValueMarker(0);\n\tprivate JButton exportXlsBtn = new JButton(\"Export Excel\");\n\tprivate JButton exportCsvBtn = new JButton(\"Export CSV\");\n\n\t/**\n\t * Creates a new self-contained demo panel.\n\t */\n\tpublic DataPanel(String title) {\n\t\t// setTitle(title);\n\t\tsetLayout(new BorderLayout());\n\n\t\tpositionSer = new XYSeries(\"True Position\");\n\n\t\tXYSeriesCollection dataset1 = new XYSeriesCollection(positionSer);\n\n\t\tJFreeChart chart = ChartFactory.createTimeSeriesChart(\"Position\", \"Time (seconds)\", \"Angle (degrees)\", dataset1,\n\t\t\t\ttrue, true, false);\n\n\t\tplot = (XYPlot) chart.getPlot();\n\t\tplot.setRenderer(1, new DefaultXYItemRenderer());\n\t\tplot.mapDatasetToRangeAxis(1, 1);\n\n\t\tValueAxis axis = plot.getDomainAxis();\n\t\taxis.setAutoRange(true);\n\t\taxis.setFixedAutoRange(20000.0); // 20 seconds\n\n\t\tChartUtilities.applyCurrentTheme(chart);\n\n\t\tChartPanel chartPanel = new ChartPanel(chart);\n\t\tchartPanel.setPreferredSize(new java.awt.Dimension(500, 270));\n\n\t\texportXlsBtn.setActionCommand(\"EXPORT_XLS\");\n\t\texportCsvBtn.setActionCommand(\"EXPORT_CSV\");\n\n\t\texportXlsBtn.addActionListener(this);\n\t\texportCsvBtn.addActionListener(this);\n\n\t\tJPanel exportPanel = new JPanel();\n\t\texportPanel.add(exportXlsBtn);\n\t\texportPanel.add(exportCsvBtn);\n\n\t\tJPanel mainPanel = new JPanel(new BorderLayout());\n\t\tmainPanel.add(chartPanel);\n\t\tmainPanel.add(exportPanel, BorderLayout.SOUTH);\n\n\t\tadd(mainPanel);\n\t}\n\n\tpublic void addPosition(double position, long time) {\n\t\tpositionSer.add(time, position);\n\t}\n\n\tpublic void setSetpoint(double sp) {\n\t\tplot.removeRangeMarker(setpoint);\n\t\tsetpoint = new ValueMarker(sp);\n\t\tsetpoint.setLabelOffsetType(LengthAdjustmentType.EXPAND);\n\t\tsetpoint.setPaint(Color.red);\n\t\tsetpoint.setStroke(new BasicStroke(2.0f));\n\t\tsetpoint.setLabel(\"Setpoint\");\n\t\tsetpoint.setLabelFont(new Font(\"SansSerif\", Font.PLAIN, 11));\n\t\tsetpoint.setLabelPaint(Color.red);\n\t\tsetpoint.setLabelAnchor(RectangleAnchor.TOP_LEFT);\n\t\tsetpoint.setLabelTextAnchor(TextAnchor.BOTTOM_LEFT);\n\t\tplot.addRangeMarker(setpoint);\n\t}\n\n\t@Override\n\tpublic void actionPerformed(ActionEvent e) {\n\t\tJFileChooser fc = new JFileChooser();\n\t\tint fcRtn = fc.showSaveDialog(this);\n\n\t\tif (fcRtn != JFileChooser.APPROVE_OPTION) {\n\t\t\treturn;\n\t\t}\n\n\t\tFile f = fc.getSelectedFile();\n\n\t\tif (e.getActionCommand().equalsIgnoreCase(\"EXPORT_XLS\")) {\n\t\t\tExcelWriter ew = new ExcelWriter();\n\t\t\tew.setFile(f);\n\t\t\tew.addData(positionSer);\n\t\t\tew.cleanup();\n\t\t} else {\n\t\t\tif (!f.getName().endsWith(\".csv\")) {\n\t\t\t\tf = new File(f.getAbsolutePath() + \".csv\");\n\t\t\t}\n\t\t\tCSVWriter cw = new CSVWriter();\n\t\t\tcw.setFile(f);\n\t\t\tcw.addData(positionSer);\n\t\t\tcw.cleanup();\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/pidsim/ExcelWriter.java",
    "content": "package com.neuronrobotics.pidsim;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.Locale;\n\nimport jxl.Workbook;\nimport jxl.WorkbookSettings;\nimport jxl.write.Label;\nimport jxl.write.Number;\nimport jxl.write.WritableSheet;\nimport jxl.write.WritableWorkbook;\nimport jxl.write.WriteException;\nimport jxl.write.biff.RowsExceededException;\n\nimport org.jfree.data.xy.XYDataItem;\nimport org.jfree.data.xy.XYSeries;\n\npublic class ExcelWriter {\n\tprivate WorkbookSettings wbSettings = new WorkbookSettings();\n\tprivate WritableWorkbook workbook;\n\tprivate WritableSheet excelSheet;\n\tprivate int lineOffset = 0;\n\n\tpublic ExcelWriter() {\n\t\twbSettings.setLocale(Locale.of(\"en\", \"EN\"));\n\t}\n\n\tprivate void addNumber(int column, int row, double d) throws WriteException, RowsExceededException {\n\t\tNumber number;\n\t\tnumber = new Number(column, row, d);\n\t\texcelSheet.addCell(number);\n\t}\n\n\tprivate void addLabel(int column, int row, String s) throws WriteException, RowsExceededException {\n\t\tLabel label;\n\t\tlabel = new Label(column, row, s);\n\t\texcelSheet.addCell(label);\n\t}\n\n\tpublic void setFile(File f) {\n\t\tif (!f.getName().endsWith(\".xls\")) {\n\t\t\tf = new File(f.getAbsolutePath() + \".xls\");\n\t\t}\n\n\t\ttry {\n\t\t\tworkbook = Workbook.createWorkbook(f, wbSettings);\n\t\t\tworkbook.createSheet(\"Data\", 0);\n\t\t\texcelSheet = workbook.getSheet(0);\n\t\t} catch (IOException e) {\n\t\t\t// Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t}\n\t}\n\n\tpublic void addData(XYSeries data) {\n\t\ttry {\n\t\t\tint col = 1;\n\t\t\taddLabel(lineOffset, 0, \"Time (s)\");\n\t\t\taddLabel(lineOffset + 1, 0, \"Position (degrees)\");\n\n\t\t\tXYSeries cache;\n\t\t\ttry {\n\t\t\t\tcache = data.createCopy(0, data.getItemCount() - 1);\n\t\t\t} catch (CloneNotSupportedException e) {\n\t\t\t\t// Auto-generated catch block\n\t\t\t\te.printStackTrace();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tfor (Object o : cache.getItems()) {\n\t\t\t\tXYDataItem i = (XYDataItem) o;\n\t\t\t\taddNumber(lineOffset, col, i.getXValue());\n\t\t\t\taddNumber(lineOffset + 1, col, i.getYValue());\n\t\t\t\tcol++;\n\t\t\t}\n\t\t} catch (RowsExceededException e) {\n\t\t\t// Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t} catch (WriteException e) {\n\t\t\t// Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t}\n\t\tlineOffset += 2;\n\t}\n\n\tpublic void cleanup() {\n\t\ttry {\n\t\t\tworkbook.write();\n\t\t\tworkbook.close();\n\t\t} catch (IOException e) {\n\t\t\t// Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/pidsim/GraphingPanel.java",
    "content": "package com.neuronrobotics.pidsim;\n\nimport java.awt.Color;\nimport java.awt.Dimension;\nimport java.awt.Font;\nimport java.awt.GradientPaint;\nimport java.awt.Point;\n\nimport javax.swing.JPanel;\nimport javax.swing.JSlider;\nimport javax.swing.event.ChangeEvent;\nimport javax.swing.event.ChangeListener;\n\nimport net.miginfocom.swing.MigLayout;\n\nimport org.jfree.chart.ChartPanel;\nimport org.jfree.chart.JFreeChart;\nimport org.jfree.chart.plot.dial.DialBackground;\nimport org.jfree.chart.plot.dial.DialCap;\nimport org.jfree.chart.plot.dial.DialPlot;\nimport org.jfree.chart.plot.dial.DialPointer;\nimport org.jfree.chart.plot.dial.DialTextAnnotation;\nimport org.jfree.chart.plot.dial.DialValueIndicator;\nimport org.jfree.chart.plot.dial.StandardDialFrame;\nimport org.jfree.chart.plot.dial.StandardDialScale;\nimport org.jfree.data.general.DefaultValueDataset;\nimport org.jfree.ui.GradientPaintTransformType;\nimport org.jfree.ui.StandardGradientPaintTransformer;\n\nclass GraphingPanel extends JPanel implements ChangeListener {\n\tprivate static final long serialVersionUID = 1L;\n\tprivate DefaultValueDataset dataset1;\n\tprivate DefaultValueDataset dataset2;\n\tprivate JSlider setpointSlider;\n\t// private JButton settingBtn = new JButton(\"Settings\");\n\t// private JButton showDataBtn = new JButton(\"Data\");\n\tprivate boolean ignoreUpdate = false;\n\tprivate SettingsDialog settingsDialog;\n\tprivate PIDSim sim;\n\tprivate DataPanel dataFrame;\n\tprivate PIDConstantsDialog constants;\n\n\tpublic GraphingPanel(PIDSim sim, PIDConstantsDialog constants, String title) {\n\t\tthis.sim = sim;\n\t\tthis.constants = constants;\n\n\t\tdataFrame = new DataPanel(\"Live Data\");\n\t\t// dataFrame.pack();\n\n\t\tsettingsDialog = new SettingsDialog(sim, constants);\n\n\t\tdataset1 = new DefaultValueDataset(0.0);\n\t\tdataset2 = new DefaultValueDataset(0.0);\n\n\t\tDialPlot plot = new DialPlot();\n\t\tplot.setView(0.0, 0.0, 1.0, 1.0);\n\t\tplot.setDataset(0, dataset1);\n\t\tplot.setDataset(1, dataset2);\n\t\tStandardDialFrame dialFrame = new StandardDialFrame();\n\t\tdialFrame.setBackgroundPaint(Color.lightGray);\n\t\tdialFrame.setForegroundPaint(Color.darkGray);\n\t\tplot.setDialFrame(dialFrame);\n\n\t\tGradientPaint gp = new GradientPaint(new Point(), new Color(255, 255, 255), new Point(),\n\t\t\t\tnew Color(170, 170, 220));\n\t\tDialBackground db = new DialBackground(gp);\n\t\tdb.setGradientPaintTransformer(new StandardGradientPaintTransformer(GradientPaintTransformType.VERTICAL));\n\t\tplot.setBackground(db);\n\n\t\tDialTextAnnotation annotation1 = new DialTextAnnotation(\"Degrees\");\n\t\tannotation1.setFont(new Font(\"Dialog\", Font.BOLD, 14));\n\t\tannotation1.setRadius(0.7);\n\n\t\tplot.addLayer(annotation1);\n\n\t\tDialValueIndicator dvi = new DialValueIndicator(0);\n\t\tdvi.setFont(new Font(\"Dialog\", Font.PLAIN, 10));\n\t\tdvi.setOutlinePaint(Color.darkGray);\n\t\tdvi.setRadius(0.60);\n\t\tdvi.setAngle(-103.0);\n\t\tplot.addLayer(dvi);\n\n\t\tDialValueIndicator dvi2 = new DialValueIndicator(1);\n\t\tdvi2.setFont(new Font(\"Dialog\", Font.PLAIN, 10));\n\t\tdvi2.setOutlinePaint(Color.red);\n\t\tdvi2.setRadius(0.60);\n\t\tdvi2.setAngle(-77.0);\n\t\tplot.addLayer(dvi2);\n\n\t\tStandardDialScale scale = new StandardDialScale(0, 180, 0, 180, 30.0, 4);\n\t\tscale.setTickRadius(0.88);\n\t\tscale.setTickLabelOffset(0.15);\n\t\tscale.setTickLabelFont(new Font(\"Dialog\", Font.PLAIN, 14));\n\t\tplot.addScale(0, scale);\n\n\t\tStandardDialScale scale2 = new StandardDialScale(0, 180, 0, 180, 30.0, 4);\n\t\tscale2.setTickRadius(0.50);\n\t\tscale2.setTickLabelOffset(0.15);\n\t\tscale2.setTickLabelFont(new Font(\"Dialog\", Font.PLAIN, 10));\n\t\tscale2.setMajorTickPaint(Color.red);\n\t\tscale2.setMinorTickPaint(Color.red);\n\t\tplot.addScale(1, scale2);\n\t\tplot.mapDatasetToScale(1, 1);\n\n\t\tDialPointer needle2 = new DialPointer.Pin(1);\n\t\tneedle2.setRadius(0.55);\n\t\tplot.addPointer(needle2);\n\n\t\tDialPointer needle = new DialPointer.Pointer(0);\n\t\tplot.addPointer(needle);\n\n\t\tDialCap cap = new DialCap();\n\t\tcap.setRadius(0.10);\n\t\tplot.setCap(cap);\n\n\t\tJFreeChart chart1 = new JFreeChart(plot);\n\t\tchart1.setTitle(\"Position\");\n\t\tChartPanel cp1 = new ChartPanel(chart1);\n\t\tcp1.setPreferredSize(new Dimension(400, 400));\n\n\t\tsetpointSlider = new JSlider(0, 1800);\n\t\tsetpointSlider.setMajorTickSpacing(300);\n\t\tsetpointSlider.setPaintTicks(true);\n\t\tsetpointSlider.setValue(1800);\n\t\tsetpointSlider.addChangeListener(this);\n\n\t\t// settingBtn.addActionListener(new ActionListener() {\n\t\t//\n\t\t// @Override\n\t\t// public void actionPerformed(ActionEvent arg0) {\n\t\t// settingsDialog.setVisible(true);\n\t\t// }\n\t\t// });\n\n\t\t// showDataBtn.addActionListener(new ActionListener() {\n\t\t//\n\t\t// @Override\n\t\t// public void actionPerformed(ActionEvent arg0) {\n\t\t// dataFrame.setVisible(true);\n\t\t// }\n\t\t// });\n\n\t\tJPanel settingsPanel = new JPanel(new MigLayout());\n\t\tsettingsPanel.add(settingsDialog, \"cell 0 0\");\n\t\tsettingsPanel.add(setpointSlider, \"cell 1 0\");\n\t\tsettingsPanel.add(dataFrame, \"cell 2 0\");\n\n\t\tJPanel panel = new JPanel(new MigLayout());\n\t\tpanel.add(cp1, \"wrap\");\n\t\tpanel.add(settingsPanel, \"wrap\");\n\n\t\t// setTitle(title);\n\t\tadd(panel);\n\t}\n\n\t/**\n\t * Handle a change in the slider by updating the dataset value. This\n\t * automatically triggers a chart repaint.\n\t *\n\t * @param e\n\t *            the event.\n\t */\n\tpublic void stateChanged(ChangeEvent e) {\n\t\tif (ignoreUpdate) {\n\t\t\treturn;\n\t\t}\n\n\t\tdouble value = (double) (1800 - setpointSlider.getValue()) / 10;\n\t\tsetSetPoint(value);\n\t\tsim.setSetPoint(value);\n\t}\n\n\tpublic void setPosition(double value) {\n\t\tif (value > 180) {\n\t\t\tvalue = 180;\n\t\t}\n\n\t\tif (value < 0) {\n\t\t\tvalue = 0;\n\t\t}\n\t\tdataset1.setValue(value);\n\t\tdataFrame.addPosition(value, sim.getTime());\n\t}\n\n\tpublic void setSetPoint(double value) {\n\t\tif (value > 180) {\n\t\t\tvalue = 180;\n\t\t}\n\n\t\tif (value < 0) {\n\t\t\tvalue = 0;\n\t\t}\n\n\t\tignoreUpdate = true;\n\t\tdataset2.setValue(value);\n\t\tsetpointSlider.setValue(1800 - (int) value * 10);\n\t\tdataFrame.setSetpoint(value);\n\t\tignoreUpdate = false;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/pidsim/LinearPhysicsEngine.java",
    "content": "package com.neuronrobotics.pidsim;\n\nimport java.util.ArrayList;\n\nimport com.neuronrobotics.bowlerstudio.IssueReportingExceptionHandler;\nimport com.neuronrobotics.sdk.common.NonBowlerDevice;\n\npublic class LinearPhysicsEngine extends NonBowlerDevice {\n\t/**\n\t *\n\t */\n\tprivate double torque = 0;// Newton meters\n\tprivate double w = 0;// Radians/second\n\tprivate double angle = 0;// radians\n\tprivate double mass;// in kg\n\tprivate double linkLen;// inmeters\n\tprivate double muStatic;// Newton meters\n\tprivate double muDynamic;// Newton meters\n\tprivate PIDSim pid;\n\tprivate double step = .005;// in seconds\n\tprivate double maxTorque = 20;// Newton meters\n\tprivate long time = 0l;// Seconds\n\tdouble acceleration;\n\tprivate boolean run = true;\n\n\tpublic void setEnabled(boolean isEnabled) {\n\t\trun = isEnabled;\n\t}\n\n\tpublic void setTorque(double torque) throws Exception {\n\t\tif (Math.abs(torque) <= Math.abs(getMaxTorque())) {\n\t\t\tthis.torque = torque;\n\t\t\treturn;\n\t\t}\n\t\tif (torque > 0)\n\t\t\tthis.torque = getMaxTorque();\n\t\telse\n\t\t\tthis.torque = getMaxTorque() * -1;\n\t\tthrow new Exception(\n\t\t\t\t\"Max Torque exceded, actuator saturates at: \" + maxTorque + \" N*M, tried to set to value: \" + torque);\n\t}\n\n\tpublic void setMass(double mass) {\n\t\tthis.mass = mass;\n\t}\n\n\tpublic double getMass() {\n\t\treturn mass;\n\t}\n\n\tpublic void setLinkLen(double linkLen) {\n\t\tthis.linkLen = linkLen;\n\t}\n\n\tpublic double getLinkLen() {\n\t\treturn linkLen;\n\t}\n\n\tpublic void setMuStatic(double muStatic) {\n\t\tthis.muStatic = muStatic;\n\t}\n\n\tpublic double getMuStatic() {\n\t\treturn muStatic;\n\t}\n\n\tpublic void setMuDynamic(double muDynamic) {\n\t\tthis.muDynamic = muDynamic;\n\t}\n\n\tpublic double getMuDynamic() {\n\t\treturn muDynamic;\n\t}\n\n\tpublic void setMaxTorque(double maxTorque) {\n\t\tthis.maxTorque = maxTorque;\n\t}\n\n\tpublic double getMaxTorque() {\n\t\treturn maxTorque;\n\t}\n\n\tpublic void setTime(long time) {\n\t\tthis.time = time;\n\t}\n\n\tpublic long getTime() {\n\t\treturn time;\n\t}\n\n\t@Override\n\tpublic void disconnectDeviceImp() {\n\t\trun = false;\n\t}\n\n\t@Override\n\tpublic boolean connectDeviceImp() {\n\t\trun = true;\n\t\tgetPid();\n\t\tnew Thread() {\n\t\t\tpublic void run() {\n\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Starting physics engine.\");\n\t\t\t\tThread.currentThread().setUncaughtExceptionHandler(new IssueReportingExceptionHandler());\n\n\t\t\t\twhile (run) {\n\n\t\t\t\t\tlong localStep = (long) (step * 1000);\n\n\t\t\t\t\tsetTime(getTime() + localStep);\n\t\t\t\t\tgetPid().setTime(getTime());\n\t\t\t\t\tdouble I = getMass() * getLinkLen() * getLinkLen();\n\t\t\t\t\tdouble tGravity = (getLinkLen() * Math.cos(angle)) * ((getMass() * -9.8)); // the torque due to\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// gravity\n\t\t\t\t\tdouble tTotal = torque + tGravity;\n\n\t\t\t\t\tif (w == 0 && (getMuStatic() > Math.abs(tTotal))) {\n\t\t\t\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"Static friction not overcome\");\n\t\t\t\t\t\ttTotal = 0;\n\t\t\t\t\t}\n\t\t\t\t\tif (w != 0) {\n\t\t\t\t\t\tdouble t = tTotal;\n\t\t\t\t\t\tif (tTotal > 0) {\n\t\t\t\t\t\t\ttTotal = t - getMuDynamic() * w;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\ttTotal = t + getMuDynamic() * w * -1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// if(tTotal!=0)\n\t\t\t\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"Torque: \\n\\tgravity=\"+ tGravity+\"\n\t\t\t\t\t\t// \\n\\tgravity plus set=\"+t+\" \\n\\tafter friction=\"+tTotal);\n\t\t\t\t\t}\n\n\t\t\t\t\tacceleration = tTotal / I;\n\n\t\t\t\t\tw += acceleration * step * step;\n\n\t\t\t\t\tif (w != 0) {\n\t\t\t\t\t\tangle += w * step;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (Math.toDegrees(angle) > 181) {\n\t\t\t\t\t\tangle = Math.PI;\n\t\t\t\t\t\tw = 0;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (Math.toDegrees(angle) < -1) {\n\t\t\t\t\t\tangle = 0;\n\t\t\t\t\t\tw = 0;\n\t\t\t\t\t}\n\n\t\t\t\t\t// com.neuronrobotics.sdk.common.Log.error(\"Controls: \\n\\ttorque: \"+torque+\"\n\t\t\t\t\t// \\n\\tTorque Total: \"+tTotal+\" \\n\\tTg: \"+tGravity+\" \\n\\tAceleration:\n\t\t\t\t\t// \"+acceleration+\" \\n\\tAngular velocity: \"+w+\" \\n\\tAngle:\n\t\t\t\t\t// \"+Math.toDegrees(angle));\n\t\t\t\t\tgetPid().setPosition(Math.toDegrees(angle));\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tThread.sleep(localStep);\n\t\t\t\t\t} catch (InterruptedException e) {\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}.start();\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic ArrayList<String> getNamespacesImp() {\n\t\t// Auto-generated method stub\n\t\treturn null;\n\t}\n\n\tpublic PIDSim getPid() {\n\t\tif (pid == null)\n\t\t\tsetPid(new PIDSim(this));\n\t\treturn pid;\n\t}\n\n\tpublic void setPid(PIDSim pid) {\n\t\tthis.pid = pid;\n\t\tsetMass(getPid().getMass());\n\t\tsetLinkLen(getPid().getLength());\n\t\tsetMuStatic(getPid().getStaticFriction());\n\t\tsetMuDynamic(getPid().getDynamicFriction());\n\t\tsetTime(System.currentTimeMillis());\n\t\tthis.maxTorque = pid.getMaxTorque();\n\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"Torque set= \" + torque + \" max torque\" + maxTorque;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/pidsim/PIDConstantsDialog.java",
    "content": "package com.neuronrobotics.pidsim;\n\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport javax.swing.JButton;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\nimport javax.swing.JTextField;\n\nimport net.miginfocom.swing.MigLayout;\n\npublic class PIDConstantsDialog extends JPanel {\n\tprivate double kp = 1;\n\tprivate double ki = 0;\n\tprivate double kd = 0;\n\n\tprivate JTextField pData = new JTextField(5);\n\tprivate JTextField iData = new JTextField(5);\n\tprivate JTextField dData = new JTextField(5);\n\tprivate JButton set = new JButton(\"Set\");\n\t/**\n\t *\n\t */\n\tprivate static final long serialVersionUID = 1L;\n\tpublic PIDConstantsDialog(double p, double i, double d) {\n\t\tsetLayout(new MigLayout());\n\t\tsetKp(p);\n\t\tsetKi(i);\n\t\tsetKd(d);\n\t\tadd(new JLabel(\"Kp\"));\n\t\tadd(pData, \"wrap\");\n\t\tadd(new JLabel(\"Ki\"));\n\t\tadd(iData, \"wrap\");\n\t\tadd(new JLabel(\"Kd\"));\n\t\tadd(dData, \"wrap\");\n\t\tpData.setText(Double.toString(getKp()));\n\t\tiData.setText(Double.toString(getKi()));\n\t\tdData.setText(Double.toString(getKd()));\n\t\tset.addActionListener(new ActionListener() {\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\ttry {\n\t\t\t\t\tsetKp(Double.parseDouble(pData.getText()));\n\t\t\t\t} catch (NumberFormatException ex) {\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tsetKi(Double.parseDouble(iData.getText()));\n\t\t\t\t} catch (NumberFormatException ex) {\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tsetKd(Double.parseDouble(dData.getText()));\n\t\t\t\t} catch (NumberFormatException ex) {\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tadd(set);\n\t}\n\n\tpublic void setKp(double kp) {\n\t\tthis.kp = kp;\n\t}\n\n\tpublic double getKp() {\n\t\treturn kp;\n\t}\n\n\tpublic void setKi(double ki) {\n\t\tthis.ki = ki;\n\t}\n\n\tpublic double getKi() {\n\t\treturn ki;\n\t}\n\n\tpublic void setKd(double kd) {\n\t\tthis.kd = kd;\n\t}\n\n\tpublic double getKd() {\n\t\treturn kd;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/pidsim/PIDSim.java",
    "content": "package com.neuronrobotics.pidsim;\n\npublic class PIDSim {\n\n\tprivate GraphingPanel graphingPanel;\n\tprivate LinearPhysicsEngine phy;\n\n\tprivate double sp;\n\tprivate double position;\n\tprivate double mass = .01;\n\tprivate double length = 1;\n\tprivate double stFriction = .5;\n\tprivate double dyFriction = .3;\n\tprivate long time = 0;\n\tprivate double maxTorque = 20;// Newton meters\n\tprivate PIDConstantsDialog constants = new PIDConstantsDialog(.1, // kp\n\t\t\t0, // ki\n\t\t\t0);// kd\n\n\tpublic PIDSim(LinearPhysicsEngine eng) {\n\t\tgraphingPanel = new GraphingPanel(this, constants, \"CommonWealthRobotics PIDSim\");\n\t\tphy = eng;\n\t}\n\n\tpublic double getSetPoint() {\n\n\t\treturn sp;\n\t}\n\n\tpublic void setSetPoint(double value) {\n\t\tsp = value;\n\t\tgetGraphingPanel().setSetPoint(sp);\n\t}\n\n\t/**\n\t * setting a torque in kg/m\n\t *\n\t * @param value\n\t * @throws Exception\n\t *             if max torque is exceded\n\t */\n\tpublic void setTorque(double value) throws Exception {\n\t\tphy.setTorque(value);\n\t}\n\n\tpublic long getTime() {\n\t\treturn time;\n\t}\n\n\tprotected void setTime(long t) {\n\t\ttime = t;\n\t}\n\n\tprotected void setPosition(double value) {\n\t\tposition = value;\n\t\tgetGraphingPanel().setPosition(position);\n\t}\n\n\tpublic double getPosition() {\n\t\treturn position;\n\t}\n\n\tpublic double getMass() {\n\t\treturn mass;\n\t}\n\n\tpublic void setMass(double value) {\n\t\tphy.setMass(mass);\n\t\tmass = value;\n\t}\n\n\tpublic double getLength() {\n\t\treturn length;\n\t}\n\n\tpublic void setLength(double value) {\n\t\tphy.setLinkLen(value);\n\t\tlength = value;\n\t}\n\n\tpublic double getStaticFriction() {\n\t\treturn stFriction;\n\t}\n\n\tpublic void setStaticFriction(double value) {\n\t\tphy.setMuStatic(value);\n\t\tstFriction = value;\n\t}\n\n\tpublic double getDynamicFriction() {\n\t\treturn dyFriction;\n\t}\n\n\tpublic void setDynamicFriction(double value) {\n\t\tphy.setMuDynamic(value);\n\t\tdyFriction = value;\n\t}\n\n\tpublic double getMaxTorque() {\n\t\treturn maxTorque;\n\t}\n\n\tpublic void setMaxTorque(double maxTorque) {\n\t\tthis.maxTorque = maxTorque;\n\t}\n\n\tpublic GraphingPanel getGraphingPanel() {\n\t\treturn graphingPanel;\n\t}\n\n\tpublic PIDConstantsDialog getConstants() {\n\t\treturn constants;\n\t}\n\n\tpublic void setConstants(PIDConstantsDialog constants) {\n\t\tthis.constants = constants;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/pidsim/PIDSimFullTest.java",
    "content": "package com.neuronrobotics.pidsim;\n\npublic class PIDSimFullTest {\n\tpublic static void main(String[] args) throws InterruptedException {\n\t\tLinearPhysicsEngine engine = new LinearPhysicsEngine();\n\t\tPIDSim pid = engine.getPid();\n\t\tpid.setMass(0.01);\n\t\tpid.setLength(0.1);\n\t\tpid.setDynamicFriction(0.3);\n\t\tpid.setStaticFriction(0.5);\n\t\tpid.setSetPoint(45);\n\t\tPIDConstantsDialog d = pid.getConstants();\n\t\td.setKp(1);\n\t\td.setKd(0);\n\t\td.setKi(0);\n\n\t\tdouble IntegralCircularBuffer[] = new double[100];\n\t\tdouble iTotal = 0;\n\t\tint index = 0;\n\t\tdouble previousState = 0;\n\t\tfor (int i = 0; i < IntegralCircularBuffer.length; i++) {\n\t\t\tIntegralCircularBuffer[i] = 0;\n\t\t}\n\t\twhile (true) {\n\t\t\tThread.sleep(10); // Wait 10 ms to make a 100 hz control loop\n\t\t\tdouble set = pid.getSetPoint();\n\t\t\tdouble now = pid.getPosition();\n\n\t\t\tdouble torque;\n\n\t\t\tdouble error = now - set;\n\t\t\tiTotal -= IntegralCircularBuffer[index];\n\t\t\tiTotal += error;\n\n\t\t\tdouble derivitave = (error - previousState);\n\t\t\tindex++;\n\t\t\tif (index == IntegralCircularBuffer.length) {\n\t\t\t\tindex = 0;\n\t\t\t}\n\t\t\tIntegralCircularBuffer[index] = error;\n\t\t\ttorque = (d.getKp()) * (error) + (d.getKd()) * derivitave\n\t\t\t\t\t+ (d.getKi()) * (iTotal / IntegralCircularBuffer.length);\n\t\t\tdouble tGravity = 0;\n\t\t\tdouble tFriction = 0;\n\t\t\t// tGravity = pid.getLength() * (pid.getMass() * Math.cos(Math.toRadians(now)) *\n\t\t\t// -9.8);\n\t\t\tpreviousState = error;\n\t\t\ttry {\n\t\t\t\tpid.setTorque((torque * -1) - tGravity + tFriction);\n\t\t\t} catch (Exception ex) {\n\t\t\t\tcom.neuronrobotics.sdk.common.Log.error(\"Max Torque exceded\");\n\t\t\t}\n\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/pidsim/PidLab.java",
    "content": "package com.neuronrobotics.pidsim;\n\nimport javafx.embed.swing.SwingNode;\nimport javafx.scene.control.ScrollPane;\n\nimport com.neuronrobotics.bowlerstudio.tabs.AbstractBowlerStudioTab;\nimport com.neuronrobotics.sdk.common.BowlerAbstractDevice;\n\npublic class PidLab extends AbstractBowlerStudioTab {\n\tLinearPhysicsEngine engine;\n\tprivate SwingNode wrapper;\n\n\t@Override\n\tpublic void onTabClosing() {\n\t\t// Auto-generated method stub\n\n\t}\n\n\t@Override\n\tpublic String[] getMyNameSpaces() {\n\t\treturn new String[0];\n\t}\n\n\t@Override\n\tpublic void initializeUI(BowlerAbstractDevice pm) {\n\t\tengine = (LinearPhysicsEngine) pm;\n\t\twrapper = new SwingNode();\n\n\t\twrapper.setContent(engine.getPid().getGraphingPanel());\n\t\tScrollPane s1 = new ScrollPane();\n\n\t\ts1.setContent(wrapper);\n\t\tsetContent(s1);\n\t\tsetText(\"PID Lab\");\n\n\t}\n\n\t@Override\n\tpublic void onTabReOpening() {\n\t\t// Auto-generated method stub\n\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/pidsim/SettingsDialog.java",
    "content": "\npackage com.neuronrobotics.pidsim;\n\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\n\nimport javax.swing.JButton;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\nimport javax.swing.JTextField;\n\nimport net.miginfocom.swing.MigLayout;\n\nclass SettingsDialog extends JPanel implements ActionListener {\n\tprivate static final long serialVersionUID = 1L;\n\tprivate PIDSim sim;\n\tprivate JTextField massTxt = new JTextField(5);\n\tprivate JTextField lenTxt = new JTextField(5);\n\tprivate JTextField stFricTxt = new JTextField(5);\n\tprivate JTextField dyFricTxt = new JTextField(5);\n\tprivate JButton saveBtn = new JButton(\"Save\");\n\tprivate PIDConstantsDialog constants;\n\n\tpublic SettingsDialog(PIDSim sim, PIDConstantsDialog constants) {\n\t\tthis.sim = sim;\n\t\tthis.constants = constants;\n\n\t\tsaveBtn.addActionListener(this);\n\n\t\tJPanel p = new JPanel(new MigLayout());\n\t\tp.add(new JLabel(\"Mass (Kg):\"), \"cell 0 0\");\n\t\tp.add(massTxt, \" cell 1 0\");\n\t\tp.add(new JLabel(\"Link Length (m):\"), \"cell 0 1\");\n\t\tp.add(lenTxt, \" cell 1 1\");\n\t\tp.add(new JLabel(\"Static Friction Coefficient:\"), \"cell 0 2\");\n\t\tp.add(stFricTxt, \" cell 1 2\");\n\t\tp.add(new JLabel(\"Dynamic Friction Coefficient:\"), \"cell 0 3\");\n\t\tp.add(dyFricTxt, \" cell 1 3\");\n\t\tp.add(saveBtn, \"cell 0 4, spanx\");\n\t\tp.add(constants, \"cell 0 5, spanx\");\n\t\trefreshValues();\n\n\t\tadd(p);\n\t}\n\n\tpublic void refreshValues() {\n\t\tmassTxt.setText(Double.toString(sim.getMass()));\n\t\tlenTxt.setText(Double.toString(sim.getLength()));\n\t\tstFricTxt.setText(Double.toString(sim.getStaticFriction()));\n\t\tdyFricTxt.setText(Double.toString(sim.getDynamicFriction()));\n\t}\n\n\tprivate double cleanOrZero(String txt) {\n\t\tdouble d;\n\t\ttry {\n\t\t\td = Double.parseDouble(txt);\n\t\t} catch (Exception e) {\n\t\t\td = 0;\n\t\t}\n\n\t\treturn d;\n\t}\n\n\t@Override\n\tpublic void actionPerformed(ActionEvent arg0) {\n\t\tsim.setMass(cleanOrZero(massTxt.getText()));\n\t\tsim.setLength(cleanOrZero(lenTxt.getText()));\n\t\tsim.setStaticFriction(cleanOrZero(stFricTxt.getText()));\n\t\tsim.setDynamicFriction(cleanOrZero(dyFricTxt.getText()));\n\t\trefreshValues();\n\t\tsetVisible(true);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/sdk/addons/kinematics/FirmataBowler.java",
    "content": "package com.neuronrobotics.sdk.addons.kinematics;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\n\nimport org.firmata4j.firmata.FirmataDevice;\n\nimport com.neuronrobotics.sdk.common.NonBowlerDevice;\n\npublic class FirmataBowler extends NonBowlerDevice {\n\n\tprivate FirmataDevice device;\n\tpublic FirmataBowler(String port) {\n\t\tsetFirmataDevice(new FirmataDevice(port));\n\n\t}\n\n\t@Override\n\tpublic boolean connectDeviceImp() {\n\n\t\ttry {\n\t\t\tgetFirmataDevice().start(); // initiate communication to the device\n\t\t\tgetFirmataDevice().ensureInitializationIsDone();\n\t\t} catch (Exception e) {\n\t\t\t// Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t\treturn false;\n\t\t} // wait for initialization is done\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic void disconnectDeviceImp() {\n\t\tcom.neuronrobotics.sdk.common.Log.error(\"Closing Firmata\");\n\t\ttry {\n\t\t\tgetFirmataDevice().stop();\n\t\t} catch (IOException e) {\n\t\t\t// Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t} // stop communication to the device\n\t}\n\n\t@Override\n\tpublic ArrayList<String> getNamespacesImp() {\n\t\t// Auto-generated method stub\n\t\treturn new ArrayList<String>();\n\t}\n\n\tpublic FirmataDevice getFirmataDevice() {\n\t\treturn device;\n\t}\n\n\tpublic void setFirmataDevice(FirmataDevice device) {\n\t\tthis.device = device;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/neuronrobotics/sdk/addons/kinematics/FirmataLink.java",
    "content": "package com.neuronrobotics.sdk.addons.kinematics;\n\nimport java.io.IOException;\n\nimport org.firmata4j.IOEvent;\nimport org.firmata4j.Pin;\nimport org.firmata4j.PinEventListener;\n\nimport com.neuronrobotics.sdk.common.DeviceManager;\n\npublic class FirmataLink extends AbstractLink implements PinEventListener {\n\n\tprivate Pin pin;\n\n\tpublic FirmataLink(LinkConfiguration arg0, FirmataBowler device)\n\t\t\tthrows InterruptedException, IllegalArgumentException, IOException {\n\t\tsuper(arg0);\n\n\t\tpin = device.getFirmataDevice().getPin(arg0.getHardwareIndex());\n\t\tpin.setMode(Pin.Mode.SERVO);\n\t\t// our listeners will get event about this change\n\t\tpin.addEventListener(this);\n\t}\n\n\t@Override\n\tpublic void cacheTargetValueDevice() {\n\t\t// Auto-generated method stub\n\n\t}\n\n\t@Override\n\tpublic void flushAllDevice(double arg0) {\n\t\t// Auto-generated method stub\n\t\tflushDevice(arg0);\n\t}\n\n\t@Override\n\tpublic void flushDevice(double arg0) {\n\t\ttry {\n\t\t\tpin.setValue((long) getTargetValue());\n\t\t} catch (IllegalStateException | IOException e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t}\n\n\t@Override\n\tpublic double getCurrentPosition() {\n\t\treturn pin.getValue();\n\t}\n\n\t@Override\n\tpublic void onModeChange(IOEvent event) {\n\t}\n\n\t@Override\n\tpublic void onValueChange(IOEvent event) {\n\t\tfireLinkListener(getCurrentPosition());\n\n\t}\n\n\tpublic static void addLinkFactory() {\n\t\tINewLinkProvider lp = new INewLinkProvider() {\n\t\t\t@Override\n\t\t\tpublic AbstractLink generate(LinkConfiguration config) {\n\t\t\t\tFirmataBowler dev = (FirmataBowler) DeviceManager.getSpecificDevice(FirmataBowler.class,\n\t\t\t\t\t\tconfig.getDeviceScriptingName());\n\t\t\t\tif (dev != null)\n\t\t\t\t\ttry {\n\t\t\t\t\t\treturn new FirmataLink(config, dev);\n\t\t\t\t\t} catch (IllegalArgumentException e) {\n\t\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\t\te.printStackTrace();\n\t\t\t\t\t} catch (InterruptedException e) {\n\t\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\t\te.printStackTrace();\n\t\t\t\t\t} catch (IOException e) {\n\t\t\t\t\t\t// Auto-generated catch block\n\t\t\t\t\t\te.printStackTrace();\n\t\t\t\t\t}\n\t\t\t\treturn null;\n\t\t\t}\n\t\t};\n\t\tLinkFactory.addLinkProvider(\"firmata-servo-rotory\", lp);\n\t\tLinkFactory.addLinkProvider(\"firmata-servo-prismatic\", lp);\n\t}\n}\n"
  },
  {
    "path": "src/main/resources/com/neuronrobotics/bowlerstudio/build.properties",
    "content": "app.name=BowlerStudio\napp.version=source\n"
  },
  {
    "path": "src/main/resources/javax.usb.properties",
    "content": "javax.usb.services = org.usb4java.javax.Services"
  },
  {
    "path": "src/main/resources/speech.properties",
    "content": "# Copyright 2001 Sun Microsystems, Inc.\n# All Rights Reserved.  Use is subject to license terms.\n# \n# See the file \"license.terms\" for information on usage and\n# redistribution of this file, and for a DISCLAIMER OF ALL \n# WARRANTIES.\n#\n\n# Modify this accordingly...\n#\n#TextSynthEngineCentral=com.sun.speech.engine.synthesis.text.TextEngineCentral\n#\nFreeTTSSynthEngineCentral=com.sun.speech.freetts.jsapi.FreeTTSEngineCentral\n"
  },
  {
    "path": "test.dxf",
    "content": "  0\nSECTION\n  2\nHEADER\n  9\n$ACADVER\n  1\nAC1014\n  9\n$HANDSEED\n  5\nFFFF\n  9\n$MEASUREMENT\n 70\n     1\n  0\nENDSEC\n  0\nSECTION\n  2\nTABLES\n  0\nTABLE\n  2\nVPORT\n  5\n8\n330\n0\n100\nAcDbSymbolTable\n 70\n     4\n  0\nVPORT\n  5\n2E\n330\n8\n100\nAcDbSymbolTableRecord\n100\nAcDbViewportTableRecord\n  2\n*ACTIVE\n 70\n     0\n 10\n0.0\n 20\n0.0\n 11\n1.0\n 21\n1.0\n 12\n210.0\n 22\n148.5\n 13\n0.0\n 23\n0.0\n 14\n10.0\n 24\n10.0\n 15\n10.0\n 25\n10.0\n 16\n0.0\n 26\n0.0\n 36\n1.0\n 17\n0.0\n 27\n0.0\n 37\n0.0\n 40\n341.0\n 41\n1.24\n 42\n50.0\n 43\n0.0\n 44\n0.0\n 50\n0.0\n 51\n0.0\n 71\n     0\n 72\n   100\n 73\n     1\n 74\n     3\n 75\n     0\n 76\n     0\n 77\n     0\n 78\n     0\n  0\nENDTAB\n  0\nTABLE\n  2\nLTYPE\n  5\n5\n330\n0\n100\nAcDbSymbolTable\n 70\n     1\n  0\nLTYPE\n  5\n14\n330\n5\n100\nAcDbSymbolTableRecord\n100\nAcDbLinetypeTableRecord\n  2\nBYBLOCK\n 70\n     0\n  3\n\n 72\n    65\n 73\n     0\n 40\n0.0\n  0\nLTYPE\n  5\n15\n330\n5\n100\nAcDbSymbolTableRecord\n100\nAcDbLinetypeTableRecord\n  2\nBYLAYER\n 70\n     0\n  3\n\n 72\n    65\n 73\n     0\n 40\n0.0\n  0\nLTYPE\n  5\n16\n330\n5\n100\nAcDbSymbolTableRecord\n100\nAcDbLinetypeTableRecord\n  2\nCONTINUOUS\n 70\n     0\n  3\nSolid line\n 72\n    65\n 73\n     0\n 40\n0.0\n  0\nENDTAB\n  0\nTABLE\n  2\nLAYER\n  5\n2\n100\nAcDbSymbolTable\n 70\n1\n  0\nLAYER\n  5\n50\n100\nAcDbSymbolTableRecord\n100\nAcDbLayerTableRecord\n  2\n0\n 70\n0\n  6\nCONTINUOUS\n  0\nENDTAB\n  0\nTABLE\n  2\nSTYLE\n  5\n3\n330\n0\n100\nAcDbSymbolTable\n 70\n     1\n  0\nSTYLE\n  5\n11\n330\n3\n100\nAcDbSymbolTableRecord\n100\nAcDbTextStyleTableRecord\n  2\nSTANDARD\n 70\n     0\n 40\n0.0\n 41\n1.0\n 50\n0.0\n 71\n     0\n 42\n2.5\n  3\ntxt\n  4\n\n  0\nENDTAB\n  0\nTABLE\n  2\nVIEW\n  5\n6\n330\n0\n100\nAcDbSymbolTable\n 70\n     0\n  0\nENDTAB\n  0\nTABLE\n  2\nUCS\n  5\n7\n330\n0\n100\nAcDbSymbolTable\n 70\n     0\n  0\nENDTAB\n  0\nTABLE\n  2\nAPPID\n  5\n9\n330\n0\n100\nAcDbSymbolTable\n 70\n     2\n  0\nAPPID\n  5\n12\n330\n9\n100\nAcDbSymbolTableRecord\n100\nAcDbRegAppTableRecord\n  2\nACAD\n 70\n     0\n  0\nENDTAB\n  0\nTABLE\n  2\nDIMSTYLE\n  5\nA\n330\n0\n100\nAcDbSymbolTable\n 70\n     1\n  0\nDIMSTYLE\n105\n27\n330\nA\n100\nAcDbSymbolTableRecord\n100\nAcDbDimStyleTableRecord\n  2\nISO-25\n 70\n     0\n  3\n\n  4\n\n  5\n\n  6\n\n  7\n\n 40\n1.0\n 41\n2.5\n 42\n0.625\n 43\n3.75\n 44\n1.25\n 45\n0.0\n 46\n0.0\n 47\n0.0\n 48\n0.0\n140\n2.5\n141\n2.5\n142\n0.0\n143\n0.03937007874016\n144\n1.0\n145\n0.0\n146\n1.0\n147\n0.625\n 71\n     0\n 72\n     0\n 73\n     0\n 74\n     0\n 75\n     0\n 76\n     0\n 77\n     1\n 78\n     8\n170\n     0\n171\n     3\n172\n     1\n173\n     0\n174\n     0\n175\n     0\n176\n     0\n177\n     0\n178\n     0\n270\n     2\n271\n     2\n272\n     2\n273\n     2\n274\n     3\n340\n11\n275\n     0\n280\n     0\n281\n     0\n282\n     0\n283\n     0\n284\n     8\n285\n     0\n286\n     0\n287\n     3\n288\n     0\n  0\nENDTAB\n  0\nTABLE\n  2\nBLOCK_RECORD\n  5\n1\n330\n0\n100\nAcDbSymbolTable\n 70\n     1\n  0\nBLOCK_RECORD\n  5\n1F\n330\n1\n100\nAcDbSymbolTableRecord\n100\nAcDbBlockTableRecord\n  2\n*MODEL_SPACE\n  0\nBLOCK_RECORD\n  5\n1B\n330\n1\n100\nAcDbSymbolTableRecord\n100\nAcDbBlockTableRecord\n  2\n*PAPER_SPACE\n  0\nENDTAB\n  0\nENDSEC\n  0\nSECTION\n  2\nBLOCKS\n  0\nBLOCK\n  5\n20\n330\n1F\n100\nAcDbEntity\n  8\n0\n100\nAcDbBlockBegin\n  2\n*MODEL_SPACE\n 70\n     0\n 10\n0.0\n 20\n0.0\n 30\n0.0\n  3\n*MODEL_SPACE\n  1\n\n  0\nENDBLK\n  5\n21\n330\n1F\n100\nAcDbEntity\n  8\n0\n100\nAcDbBlockEnd\n  0\nBLOCK\n  5\n1C\n330\n1B\n100\nAcDbEntity\n 67\n     1\n  8\n0\n100\nAcDbBlockBegin\n  2\n*PAPER_SPACE\n  1\n\n  0\nENDBLK\n  5\n1D\n330\n1B\n100\nAcDbEntity\n 67\n     1\n  8\n0\n100\nAcDbBlockEnd\n  0\nENDSEC\n  0\nSECTION\n  2\nENTITIES\n  0\nLWPOLYLINE\n  5\n100\n100\nAcDbEntity\n  8\n0\n 62\n7\n100\nAcDbPolyline\n 90\n6\n 70\n1\n 10\n0.048796\n 20\n399.626667\n 30\n0.0\n 10\n399.736733\n 20\n399.578689\n 30\n0.0\n 10\n399.685933\n 20\n-0.110067\n 30\n0.0\n 10\n0.000000\n 20\n-0.059267\n 30\n0.0\n 10\n0.047978\n 20\n399.626667\n 30\n0.0\n 10\n0.048796\n 20\n399.626667\n 30\n0.0\n  0\nLWPOLYLINE\n  5\n101\n100\nAcDbEntity\n  8\n0\n 62\n7\n100\nAcDbPolyline\n 90\n6\n 70\n1\n 10\n99.982679\n 20\n299.692784\n 30\n0.0\n 10\n299.801844\n 20\n299.643800\n 30\n0.0\n 10\n299.753867\n 20\n99.824822\n 30\n0.0\n 10\n99.934889\n 20\n99.872800\n 30\n0.0\n 10\n99.982867\n 20\n299.691778\n 30\n0.0\n 10\n99.982679\n 20\n299.692784\n 30\n0.0\n  0\nENDSEC\n  0\nSECTION\n  2\nOBJECTS\n  0\nDICTIONARY\n  5\nC\n330\n0\n100\nAcDbDictionary\n  3\nACAD_GROUP\n350\nD\n  3\nACAD_MLINESTYLE\n350\n17\n  0\nDICTIONARY\n  5\nD\n330\nC\n100\nAcDbDictionary\n  0\nDICTIONARY\n  5\n1A\n330\nC\n100\nAcDbDictionary\n  0\nDICTIONARY\n  5\n17\n330\nC\n100\nAcDbDictionary\n  3\nSTANDARD\n350\n18\n  0\nDICTIONARY\n  5\n19\n330\nC\n100\nAcDbDictionary\n  0\nENDSEC\n  0\nEOF\n"
  },
  {
    "path": "wrappers/linux/BowlerStudio.desktop",
    "content": "[Desktop Entry]\nType=Application\nName=Bowler Studio\nExec=/usr/bin/bowlerstudio\nCategories=Application;Development;Programming;3DGraphics;Education;Network;WebBrowser;\nIcon=/usr/share/themes/base/neuronrobotics/icons/NeuronRobotics.png\n"
  },
  {
    "path": "wrappers/linux/bowlerstudio",
    "content": "#!/bin/bash\n\nNRDIR=/usr/share/bowlerstudio/\nJAR=BowlerStudio.jar\n\nif (test -d $NRDIR)then\n\tjava -jar $NRDIR$JAR \"$@\"\nelse\n\tzenity --error  --text=\"#ERROR: $JAR is not where i expect it to be, $NRDIR\"\nfi\n"
  },
  {
    "path": "wrappers/linux/control",
    "content": "Source: bowlerstudio\nPackage: bowlerstudio\nPriority: extra\nMaintainer: Customer Support <mad.hephaestus@gmail.com>\nArchitecture: all\nVersion: BOWLERVERSION\nDepends: oracle-java8-set-default,libopencv2.4-java,libopencv2.4-jni,slic3r,jarwrapper,arduino\nProvides: bowlerstudio\nConflicts: modemmanager,nr-rdk-java\nReplaces: modemmanager,nr-rdk-java\nDescription: Robotics Development Engironment and runtime. \n A scripting platform for writing, designing, simulating \n and manufacturing robots.  \n"
  },
  {
    "path": "wrappers/osx/BowlerStudio",
    "content": "#! /bin/bash\n\nDIR=$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" && pwd )\n\nexport OPENCV_DIR=$DIR/opencv249build/\necho Setting OPENCV_DIR = ${OPENCV_DIR}\nif (java -jar \"$DIR/JavaVersionCheck.jar\" 8 45) ; then\n    java -jar \"$DIR/bin/BowlerStudio.jar\" \nelse\n    echo \"Update Java and try again\"\nfi\n\n"
  },
  {
    "path": "wrappers/osx/bowler-scripting-kernel.sh",
    "content": "#! /bin/bash\n\nDIR=$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" && pwd )\n\nexport OPENCV_DIR=$DIR/opencv249build/\necho Setting OPENCV_DIR = ${OPENCV_DIR}\nif (java -jar \"$DIR/JavaVersionCheck.jar\" 8 45) ; then\n    java -jar \"$DIR/bin/BowlerStudio.jar\"  -r \"$@\"\nelse\n    echo \"Update Java and try again\"\nfi\n\n"
  }
]