From ed0f4f9f4429283000066c366b7fbc53ac6743da Mon Sep 17 00:00:00 2001
From: Danny Farrell <16297104+danpf@users.noreply.github.com>
Date: Thu, 16 Nov 2023 11:57:07 -0600
Subject: [PATCH] Squash 2
---
.github/workflows/cpp.yml | 92 +++++
.github/workflows/emscripten.yml | 61 +++
.github/workflows/pip.yml | 46 +++
.github/workflows/wheels.yml | 89 +++++
.gitignore | 168 ++++++++
.gitmodules | 18 +-
.travis.yml | 129 +++++--
CHANGELOG.md | 6 +-
CMakeLists.txt | 32 +-
Catch2 | 1 -
README.md | 66 +++-
bindings/4lgr.mmtf | Bin 41493 -> 0 bytes
bindings/bindings.cpp | 189 ---------
bindings/check.py | 7 -
bindings/compile.sh | 2 -
bindings/mmtf_t.py | 173 ---------
ci/build_and_run_python_tests.sh | 10 +
ci/build_and_run_tests.sh | 10 +
ci/setup-travis.sh | 14 +-
ci/travis-test-example.sh | 8 +-
examples/CMakeLists.txt | 7 +-
include/mmtf.hpp | 2 +
include/mmtf/binary_decoder.hpp | 120 +++---
include/mmtf/binary_encoder.hpp | 28 +-
include/mmtf/map_decoder.hpp | 16 +-
include/mmtf/structure_data.hpp | 2 +-
mmtf_spec | 1 -
msgpack-c | 1 -
pybind11 | 1 -
pyproject.toml | 85 ++++
src/python/CMakeLists.txt | 21 +
src/python/bindings.cpp | 515 +++++++++++++++++++++++++
src/python/mmtf_cppy/__init__.py | 31 ++
src/python/mmtf_cppy/structure_data.py | 495 ++++++++++++++++++++++++
src/python/tests/tests.py | 196 ++++++++++
submodules/mmtf_spec | 1 +
tests/CMakeLists.txt | 18 +-
tests/mmtf_tests.cpp | 82 ++--
tests/multi_cpp_test.cpp | 2 +-
39 files changed, 2161 insertions(+), 584 deletions(-)
create mode 100644 .github/workflows/cpp.yml
create mode 100644 .github/workflows/emscripten.yml
create mode 100644 .github/workflows/pip.yml
create mode 100644 .github/workflows/wheels.yml
delete mode 160000 Catch2
delete mode 100644 bindings/4lgr.mmtf
delete mode 100644 bindings/bindings.cpp
delete mode 100644 bindings/check.py
delete mode 100755 bindings/compile.sh
delete mode 100644 bindings/mmtf_t.py
create mode 100755 ci/build_and_run_python_tests.sh
create mode 100755 ci/build_and_run_tests.sh
delete mode 160000 mmtf_spec
delete mode 160000 msgpack-c
delete mode 160000 pybind11
create mode 100644 pyproject.toml
create mode 100644 src/python/CMakeLists.txt
create mode 100644 src/python/bindings.cpp
create mode 100644 src/python/mmtf_cppy/__init__.py
create mode 100644 src/python/mmtf_cppy/structure_data.py
create mode 100644 src/python/tests/tests.py
create mode 160000 submodules/mmtf_spec
diff --git a/.github/workflows/cpp.yml b/.github/workflows/cpp.yml
new file mode 100644
index 0000000..6f74bca
--- /dev/null
+++ b/.github/workflows/cpp.yml
@@ -0,0 +1,92 @@
+name: "cpp"
+
+on:
+ workflow_dispatch:
+ pull_request:
+ push:
+ branches:
+ - master
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ build:
+ name: Build and test cpp
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - os: ubuntu-22.04
+ cc: gcc
+ cxx: g++
+ - os: ubuntu-22.04
+ cc: gcc
+ cxx: g++
+ env_list: EMSCRIPTEN=ON
+ - os: ubuntu-22.04
+ cc: clang
+ cxx: clang++
+ - os: ubuntu-22.04
+ cc: gcc
+ cxx: g++
+ cmake_args: "-DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS='-march=native'"
+ - os: macos-latest
+ cc: clang
+ cxx: clang++
+ - os: windows-latest
+ cc: ''
+ cxx: ''
+ runs-on: ${{ matrix.os }}
+ env:
+ CC: ${{ matrix.cc }}
+ CXX: ${{ matrix.cxx }}
+ CMAKE_ARGS: ${{ matrix.cmake_args }}
+
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: true
+ fetch-depth: 0
+
+ # - name: install mingw & deps
+ # uses: msys2/setup-msys2@v2
+ # with:
+ # update: true
+ # install: >-
+ # git
+ # make
+ # pacboy: >-
+ # toolchain:p
+ # cmake:p
+ # ninja:p
+ # if: matrix.os == 'windows-latest'
+
+ - name: Set environment list variables
+ run: |
+ env_vars="${{ matrix.env_list }}"
+ for var in $env_vars; do
+ echo "$var" >> $GITHUB_ENV
+ done
+ if: matrix.os != 'windows-latest'
+
+ - name: Setup cmake
+ uses: jwlawson/actions-setup-cmake@v1.13
+ with:
+ cmake-version: '3.16.x'
+
+ - uses: seanmiddleditch/gha-setup-ninja@master
+
+ - name: build and test
+ run: |
+ mkdir build && cd build
+ cmake $CMAKE_ARGS -G Ninja -Dmmtf_build_examples=ON -DBUILD_TESTS=ON -DCMAKE_BUILD_TYPE=debug ..
+ ninja
+ ./tests/mmtf_tests
+ ./tests/multi_cpp_test
+ ./examples/mmtf_demo ../submodules/mmtf_spec/test-suite/mmtf/173D.mmtf
+ ./examples/traverse ../submodules/mmtf_spec/test-suite/mmtf/173D.mmtf
+ ./examples/traverse ../submodules/mmtf_spec/test-suite/mmtf/173D.mmtf json
+ ./examples/traverse ../submodules/mmtf_spec/test-suite/mmtf/173D.mmtf print
+ ./examples/print_as_pdb ../submodules/mmtf_spec/test-suite/mmtf/173D.mmtf
diff --git a/.github/workflows/emscripten.yml b/.github/workflows/emscripten.yml
new file mode 100644
index 0000000..4c200d9
--- /dev/null
+++ b/.github/workflows/emscripten.yml
@@ -0,0 +1,61 @@
+name: WASM
+
+on:
+ workflow_dispatch:
+ pull_request:
+ branches:
+ - master
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ build-wasm-emscripten:
+ name: Pyodide
+ runs-on: ubuntu-22.04
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: true
+ fetch-depth: 0
+
+ - uses: actions/setup-python@v4
+ with:
+ python-version: "3.11"
+
+ - name: Install pyodide-build
+ run: pip install pyodide-build==0.23.4
+
+ - name: Compute emsdk version
+ id: compute-emsdk-version
+ run: |
+ pyodide xbuildenv install --download
+ EMSCRIPTEN_VERSION=$(pyodide config get emscripten_version)
+ echo "emsdk-version=$EMSCRIPTEN_VERSION" >> $GITHUB_OUTPUT
+
+ - uses: mymindstorm/setup-emsdk@v12
+ with:
+ version: ${{ steps.compute-emsdk-version.outputs.emsdk-version }}
+ actions-cache-folder: emsdk-cache
+
+ # A future version of pyodide may switch to -fwasm-exceptions
+ - name: Build
+ run: CFLAGS=-fexceptions LDFLAGS=-fexceptions pyodide build
+
+ - uses: actions/upload-artifact@v3
+ with:
+ path: dist/*.whl
+
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 18
+
+ - name: Set up Pyodide virtual environment
+ run: |
+ pyodide venv .venv-pyodide
+ .venv-pyodide/bin/pip install $(echo -n dist/*.whl)
+
+ - name: Test
+ run: .venv-pyodide/bin/python -m unittest src/python/tests/tests.py
+
diff --git a/.github/workflows/pip.yml b/.github/workflows/pip.yml
new file mode 100644
index 0000000..cfa5cbb
--- /dev/null
+++ b/.github/workflows/pip.yml
@@ -0,0 +1,46 @@
+name: "Pip"
+
+on:
+ workflow_dispatch:
+ pull_request:
+ push:
+ branches:
+ - master
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ build:
+ name: Build with Pip
+ runs-on: ${{ matrix.platform }}
+ strategy:
+ fail-fast: false
+ matrix:
+ platform: [windows-latest, macos-latest, ubuntu-latest]
+ python-version: ["3.8", "3.11", "3.12", "pypy-3.8"]
+
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: true
+ fetch-depth: 0
+
+ - uses: actions/setup-python@v4
+ with:
+ python-version: ${{ matrix.python-version }}
+
+ # - name: Setup cmake
+ # uses: jwlawson/actions-setup-cmake@v1.13
+ # with:
+ # cmake-version: '3.16.x'
+
+ # - uses: seanmiddleditch/gha-setup-ninja@master
+
+ - name: Build and install
+ run: pip install --verbose .
+
+ - name: Test
+ run: python src/python/tests/tests.py
+
diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml
new file mode 100644
index 0000000..e4f92b5
--- /dev/null
+++ b/.github/workflows/wheels.yml
@@ -0,0 +1,89 @@
+name: Wheels
+
+on:
+ workflow_dispatch:
+ pull_request:
+ push:
+ branches:
+ - master
+ release:
+ types:
+ - published
+
+env:
+ FORCE_COLOR: 3
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ build_sdist:
+ name: Build SDist
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: true
+
+ - name: Build SDist
+ run: pipx run build --sdist
+
+ - name: Check metadata
+ run: pipx run twine check dist/*
+
+ - uses: actions/upload-artifact@v3
+ with:
+ path: dist/*.tar.gz
+
+
+ build_wheels:
+ name: Wheels on ${{ matrix.os }}
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ubuntu-latest, macos-latest, windows-latest]
+
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: true
+
+ # - name: Set CMAKE_GENERATOR for Windows
+ # if: matrix.os == 'windows-latest'
+ # run: echo "CMAKE_GENERATOR=MinGW Makefiles" >> $GITHUB_ENV
+
+ - uses: pypa/cibuildwheel@v2.16.2
+ env:
+ CIBW_ARCHS_MACOS: universal2
+ CIBW_ARCHS_WINDOWS: auto ARM64
+ CMAKE_GENERATOR: ${{ env.CMAKE_GENERATOR }}
+
+ - name: Verify clean directory
+ run: git diff --exit-code
+ shell: bash
+
+ - uses: actions/upload-artifact@v3
+ with:
+ path: wheelhouse/*.whl
+
+ upload_all:
+ name: Upload if release
+ needs: [build_wheels, build_sdist]
+ runs-on: ubuntu-latest
+ if: github.event_name == 'release' && github.event.action == 'published'
+
+ steps:
+ - uses: actions/setup-python@v4
+ with:
+ python-version: "3.x"
+
+ - uses: actions/download-artifact@v3
+ with:
+ name: artifact
+ path: dist
+
+ - uses: pypa/gh-action-pypi-publish@release/v1
+ with:
+ password: ${{ secrets.pypi_password }}
diff --git a/.gitignore b/.gitignore
index a8de19a..e649fb8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -39,3 +39,171 @@ build/*
docs/html/*
examples/out/*
examples/out_json_ref/*
+
+# python eggs
+src/python/*.egg-info
+**/__pycache__
+**/*.pyc
+
+
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+cover/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+.pybuilder/
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+# For a library or package, you might want to ignore these files since the code is
+# intended to run in multiple environments; otherwise, check them in:
+# .python-version
+
+# pipenv
+# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+# However, in case of collaboration, if having platform-specific dependencies or dependencies
+# having no cross-platform support, pipenv may install dependencies that don't work, or not
+# install all needed dependencies.
+#Pipfile.lock
+
+# poetry
+# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
+# This is especially recommended for binary packages to ensure reproducibility, and is more
+# commonly ignored for libraries.
+# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
+#poetry.lock
+
+# pdm
+# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
+#pdm.lock
+# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
+# in version control.
+# https://pdm.fming.dev/#use-with-ide
+.pdm.toml
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# pytype static type analyzer
+.pytype/
+
+# Cython debug symbols
+cython_debug/
+
+# PyCharm
+# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
+# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
+# and can be added to the global gitignore or merged into this file. For a more nuclear
+# option (not recommended) you can uncomment the following to ignore the entire idea folder.
+#.idea/
+
diff --git a/.gitmodules b/.gitmodules
index 3cd3c3c..1582527 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,12 +1,6 @@
-[submodule "Catch2"]
- path = Catch2
- url = https://github.com/catchorg/Catch2
-[submodule "msgpack-c"]
- path = msgpack-c
- url = https://github.com/msgpack/msgpack-c
-[submodule "mmtf_spec"]
- path = mmtf_spec
- url = https://github.com/rcsb/mmtf
-[submodule "pybind11"]
- path = pybind11
- url = git@github.com:pybind/pybind11.git
+[submodule "submodules/pybind11"]
+ path = submodules/pybind11
+ url = https://github.com/pybind/pybind11.git
+[submodule "submodules/mmtf_spec"]
+ path = submodules/mmtf_spec
+ url = https://github.com/rcsb/mmtf.git
diff --git a/.travis.yml b/.travis.yml
index 401c088..70fd8ea 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,57 +1,106 @@
+---
language: cpp
sudo: false
dist: trusty
linux64_addons:
addons: &linux64
- apt:
- sources:
- - ubuntu-toolchain-r-test
- packages:
- - g++-4.8
+ apt:
+ sources:
+ - ubuntu-toolchain-r-test
+ packages:
+ - g++-4.8
linux32_addons:
addons: &linux32
- apt:
- sources:
- - ubuntu-toolchain-r-test
- packages:
- - g++-4.8
- - g++-4.8-multilib
- - linux-libc-dev:i386
- - libc6-dev-i386
+ apt:
+ sources:
+ - ubuntu-toolchain-r-test
+ packages:
+ - g++-4.8
+ - g++-4.8-multilib
+ - linux-libc-dev:i386
+ - libc6-dev-i386
+linux64_cpp17addons:
+ addons: &linux64cpp17
+ apt:
+ sources:
+ - ubuntu-toolchain-r-test
+
+linux64_cpp17addons:
+ addons: &linux64cpp17
+ apt:
+ sources:
+ - ubuntu-toolchain-r-test
+
+
+linux64_cpp17addons_py:
+ addons: &linux64cpp17py
+ apt:
+ sources:
+ - ubuntu-toolchain-r-test
+ - gcc-7
+ - g++-7
+
+python_test_command_sub: &python_test_command TEST_COMMAND=$TRAVIS_BUILD_DIR/ci/build_and_run_python_tests.sh CC=gcc
# Set empty values for allow_failures to work
-env:
+env: TEST_COMMAND=$TRAVIS_BUILD_DIR/ci/build_and_run_tests.sh
+python:
matrix:
- fast_finish: true
- include:
- - os: linux
- env: EMSCRIPTEN=ON
- addons: *linux64
- - os: linux
- compiler: clang
- addons: *linux64
- - os: linux
- compiler: gcc
- env: ARCH=x86 CMAKE_EXTRA=-DHAVE_LIBM=/lib32/libm.so.6
- addons: *linux32
- - os: osx
- compiler: clang
+ fast_finish: true
+ include:
+ - os: linux
+ env: EMSCRIPTEN=ON TEST_COMMAND=$TRAVIS_BUILD_DIR/ci/build_and_run_tests.sh
+ addons: *linux64
+ - os: linux
+ compiler: clang
+ addons: *linux64
+ - os: linux
+ compiler: gcc
+ env: ARCH=x86 CMAKE_EXTRA=-DHAVE_LIBM=/lib32/libm.so.6 TEST_COMMAND=$TRAVIS_BUILD_DIR/ci/build_and_run_tests.sh
+ addons: *linux32
+ - os: osx
+ compiler: clang
+ - os: linux
+ compiler: gcc
+ env: CMAKE_EXTRA="-DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS='-march=native'" TEST_COMMAND=$TRAVIS_BUILD_DIR/ci/build_and_run_tests.sh
+ addons: *linux64cpp17
+ dist: bionic
+ - os: linux
+ compiler: gcc
+ addons: *linux64cpp17
+ dist: bionic
+ - os: linux
+ compiler: gcc
+ addons: *linux64cpp17py
+ dist: bionic
+ env: *python_test_command
+ python: 3.8
+ language: python
+ - os: linux
+ compiler: gcc
+ addons: *linux64cpp17py
+ dist: bionic
+ env: *python_test_command
+ python: 3.11
+ language: python
+ - os: linux
+ compiler: gcc
+ addons: *linux64cpp17py
+ dist: bionic
+ env: *python_test_command
+ python: 3.12
+ language: python
before_install:
- # Setting environement
- - cd $TRAVIS_BUILD_DIR
- - source ci/setup-travis.sh
- - $CC --version
- - $CXX --version
+ # Setting environement
+ - cd $TRAVIS_BUILD_DIR
+ - source ci/setup-travis.sh
+ - $CC --version
+ - $CXX --version
script:
- - cd $TRAVIS_BUILD_DIR
- - mkdir build && cd build
- - $CMAKE_CONFIGURE cmake $CMAKE_ARGS $CMAKE_EXTRA ..
- - make -j2
- - ctest -j2 --output-on-failure
- - bash $TRAVIS_BUILD_DIR/ci/travis-test-example.sh
- - cd $TRAVIS_BUILD_DIR
+ - echo $TEST_COMMAND
+ - (eval "$TEST_COMMAND")
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8962d68..f4da72e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/),
and this project adheres to [Semantic Versioning](https://semver.org/).
## [Unreleased]
+
+## v1.1.0 - 2022-10-03
### Added
- New mapDecoderFrom.. functions to decode only part of an MMTF file
- Support for extra fields in MMTF files according to the
@@ -15,10 +17,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
[MMTF specification](https://github.com/rcsb/mmtf/pull/35).
- New methods to find polymer chains and HETATM following discussions in
[rcsb/mmtf#28](https://github.com/rcsb/mmtf/issues/28).
+- Altered submodule locations [rcsb/mmtf-cpp#37](https://github.com/rcsb/mmtf-cpp/pull/37)
+ from the base directory to the new submodules directory.
## v1.0.0 - 2019-02-05
### Added
- Initial release including decoder and encoder for the
[MMTF specification 1.0](https://github.com/rcsb/mmtf/blob/v1.0/spec.md).
-[Unreleased]: https://github.com/rcsb/mmtf-cpp/compare/v1.0.0...HEAD
+[Unreleased]: https://github.com/rcsb/mmtf-cpp/compare/v1.1.0...HEAD
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5d95e07..9e4d72f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,10 +1,10 @@
-
-cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
+cmake_minimum_required(VERSION 3.15...3.26 FATAL_ERROR)
project(mmtf-cpp VERSION 1.0.0 LANGUAGES CXX)
-option(mmtf_build_local "Use the submodule dependencies for building" OFF)
option(mmtf_build_examples "Build the examples" OFF)
+SET(MSGPACK_USE_BOOST OFF CACHE BOOL "msgpack-c uses boost by default, lets keep that off")
+# option(MSGPACK_USE_BOOST "msgpack-c uses boost by default, lets keep that off" OFF)
add_library(MMTFcpp INTERFACE)
target_compile_features(MMTFcpp INTERFACE cxx_auto_type)
@@ -13,23 +13,23 @@ target_include_directories(MMTFcpp INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}/include
)
-if (mmtf_build_local)
- # use header only
- set(MSGPACKC_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/msgpack-c/include)
- add_library(msgpackc INTERFACE)
- target_include_directories(msgpackc INTERFACE ${MSGPACKC_INCLUDE_DIR})
- if (BUILD_TESTS)
- set(CATCH_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Catch2/single_include)
- add_library(Catch INTERFACE)
- target_include_directories(Catch INTERFACE ${CATCH_INCLUDE_DIR})
- endif()
+include(FetchContent)
+FetchContent_Declare(
+ msgpack-cxx
+ GIT_REPOSITORY https://github.com/msgpack/msgpack-c.git
+ GIT_TAG cpp-6.1.0)
+FetchContent_MakeAvailable(msgpack-cxx)
+
+if(WIN32)
+ target_link_libraries(MMTFcpp INTERFACE ws2_32)
endif()
-if (NOT TARGET msgpackc)
- find_package(msgpack)
+target_link_libraries(MMTFcpp INTERFACE msgpack-cxx)
+
+if (build_py)
+ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/python)
endif()
-target_link_libraries(MMTFcpp INTERFACE msgpackc)
if (BUILD_TESTS)
enable_testing()
diff --git a/Catch2 b/Catch2
deleted file mode 160000
index cf4b7ee..0000000
--- a/Catch2
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit cf4b7eead92773932f32c7efd2612e9d27b07557
diff --git a/README.md b/README.md
index d6964a5..4fa1df8 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,7 @@
-[![Release](https://img.shields.io/github/release/rcsb/mmtf-cpp.svg?style=flat)](https://github.com/rcsb/mmtf-cpp/releases)
-[![License](https://img.shields.io/github/license/rcsb/mmtf-cpp.svg?style=flat)](https://github.com/rcsb/mmtf-cpp/blob/master/LICENSE)
-[![Build Status (Travis)](https://img.shields.io/travis/rcsb/mmtf-cpp/master.svg?style=flat)](https://travis-ci.org/rcsb/mmtf-cpp)
-[![Build Status (AppVeyor)](https://img.shields.io/appveyor/ci/rcsb/mmtf-cpp/master.svg?style=flat)](https://ci.appveyor.com/project/rcsb/mmtf-cpp)
+[![Release](https://img.shields.io/github/v/release/rcsb/mmtf-cpp)](https://github.com/rcsb/mmtf-cpp/releases)
+[![License](https://img.shields.io/github/license/rcsb/mmtf-cpp)](https://github.com/rcsb/mmtf-cpp/blob/master/LICENSE)
+[![Build Status (Travis)](https://img.shields.io/travis/com/rcsb/mmtf-cpp/master)](https://app.travis-ci.com/github/rcsb/mmtf-cpp)
The macromolecular transmission format
([MMTF](http://mmtf.rcsb.org)) is a binary encoding of biological structures.
@@ -46,6 +45,53 @@ Here, `` and `` are the paths to the
For your more complicated projects, a `CMakeLists.txt` is included for you.
+
+### Python bindings
+
+The C++ MMTF library now can build python bindings using pybind11. To use them
+you must have A) a c++11 compatible compiler and B) python >= 3.6
+
+to install, it is as simple as `pip install .`
+
+(in the future possible `pip install mmtf-cpp`)
+
+```python
+from mmtf_cppy import StructureData
+import numpy as np
+import math
+
+
+def rotation_matrix(axis, theta):
+ """
+ Return the rotation matrix associated with counterclockwise rotation about
+ the given axis by theta radians.
+ from https://stackoverflow.com/a/6802723
+ """
+ axis = np.asarray(axis)
+ axis = axis / math.sqrt(np.dot(axis, axis))
+ a = math.cos(theta / 2.0)
+ b, c, d = -axis * math.sin(theta / 2.0)
+ aa, bb, cc, dd = a * a, b * b, c * c, d * d
+ bc, ad, ac, ab, bd, cd = b * c, a * d, a * c, a * b, b * d, c * d
+ return np.array([[aa + bb - cc - dd, 2 * (bc + ad), 2 * (bd - ac)],
+ [2 * (bc - ad), aa + cc - bb - dd, 2 * (cd + ab)],
+ [2 * (bd + ac), 2 * (cd - ab), aa + dd - bb - cc]])
+
+
+theta = 1.2
+axis = [0, 0, 1]
+
+sd = StructureData("my_favorite_structure.mmtf")
+sd.atomProperties["pymol_colorList"] = [1 if x % 2 == 0 else 5 for x in sd.xCoordList]
+xyz = np.column_stack((sd.xCoordList, sd.yCoordList, sd.zCoordList))
+xyz_rot = rotation_matrix(axis, theta).dot(xyz.T).T
+sd.xCoordList, sd.yCoordList, sd.zCoordList = np.hsplit(xyz_rot, 3)
+sd.write_to_file("my_favorite_structure_rot.mmtf")
+
+```
+
+
+
## Installation
You can also perform a system wide installation with `cmake` and `ninja` (or `make`).
To do so:
@@ -73,7 +119,7 @@ To build the tests + examples we recommend using the following lines:
git submodule update --init --recursive
mkdir build
cd build
-cmake -G Ninja -DBUILD_TESTS=ON -Dmmtf_build_local=ON -Dmmtf_build_examples=ON ..
+cmake -G Ninja -DBUILD_TESTS=ON -Dmmtf_build_examples=ON ..
ninja
chmod +x ./tests/mmtf_tests
./tests/mmtf_tests
@@ -83,18 +129,18 @@ Example codes:
- mmtf_demo.cpp: Loads an MMTF file and checks internal consistency using
mmtf::StructureData::hasConsistentData.
```bash
-./examples/mmtf_demo ../mmtf_spec/test-suite/mmtf/173D.mmtf
+./examples/mmtf_demo ../submodules/mmtf_spec/test-suite/mmtf/173D.mmtf
```
- traverse.cpp: Loads an MMTF file and dumps it in human-readable forms.
```bash
-./examples/traverse ../mmtf_spec/test-suite/mmtf/173D.mmtf
-./examples/traverse ../mmtf_spec/test-suite/mmtf/173D.mmtf json
-./examples/traverse ../mmtf_spec/test-suite/mmtf/173D.mmtf print
+./examples/traverse ../submodules/mmtf_spec/test-suite/mmtf/173D.mmtf
+./examples/traverse ../submodules/mmtf_spec/test-suite/mmtf/173D.mmtf json
+./examples/traverse ../submodules/mmtf_spec/test-suite/mmtf/173D.mmtf print
```
- print_as_pdb.cpp: Loads an MMTF file and prints it in pdb format.
```bash
-./examples/print_as_pdb ../mmtf_spec/test-suite/mmtf/173D.mmtf
+./examples/print_as_pdb ../submodules/mmtf_spec/test-suite/mmtf/173D.mmtf
```
## Benchmark
diff --git a/bindings/4lgr.mmtf b/bindings/4lgr.mmtf
deleted file mode 100644
index 53d34fb74f38b0e962e40a8a66fdd9c8da94767c..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 41493
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 |