Skip to content

Commit

Permalink
starting work on WebAssembly bindings (based on Embind)
Browse files Browse the repository at this point in the history
  • Loading branch information
wojdyr committed Mar 19, 2024
1 parent 31bd2de commit dff1720
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 0 deletions.
39 changes: 39 additions & 0 deletions wasm/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
CXX = em++

INCLUDE = -I../include

FLAGS = \
-O3 -flto -Wall -Wextra -std=c++20 \
-DEMSCRIPTEN_HAS_UNBOUND_TYPE_NAMES=0 -fno-rtti \
-s STRICT=1 \
-s DISABLE_EXCEPTION_CATCHING=0

LINK_FLAGS = \
--pre-js pre.js \
-s EXPORTED_RUNTIME_METHODS=ccall,writeArrayToMemory,getValue,UTF8ToString \
-s EXPORTED_FUNCTIONS=_malloc \
-s INCOMING_MODULE_JS_API=print,printErr,setStatus,onRuntimeInitialized \
-s ALLOW_MEMORY_GROWTH=1 -s MODULARIZE=1

# -s ASSERTIONS=1 -s SAFE_HEAP=1 \
GEMMI_OBJS = mmcif.o polyheur.o resinfo.o sprintf.o json.o

BINDING_OBJS = mol.o

gemmi.js: $(GEMMI_OBJS) $(BINDING_OBJS)
$(CXX) --bind $(FLAGS) $(LINK_FLAGS) $(GEMMI_OBJS) $(BINDING_OBJS) -o $@

$(GEMMI_OBJS): %.o: ../src/%.cpp
$(CXX) $(INCLUDE) $(FLAGS) -c $< -o $@

$(BINDING_OBJS): %.o: %.cpp
$(CXX) $(INCLUDE) $(FLAGS) -c $< -o $@

%.o: %.cpp
$(CXX) $(INCLUDE) $(FLAGS) -c $<

clean:
rm -f *.o gemmi.js *.wasm

.PHONY: clean
94 changes: 94 additions & 0 deletions wasm/mol.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Copyright Global Phasing Ltd.

#include <gemmi/model.hpp>
#include <gemmi/select.hpp> // for Selection
#include <gemmi/mmread.hpp> // for read_structure_from_memory
#include <emscripten/bind.h>
#include <emscripten/val.h>

namespace em = emscripten;

gemmi::CoorFormat format_to_enum(const std::string& format) {
using gemmi::CoorFormat;
if (format == "unknown")
return CoorFormat::Unknown;
if (format == "detect")
return CoorFormat::Detect;
if (format == "pdb")
return CoorFormat::Pdb;
if (format == "mmcif")
return CoorFormat::Mmcif;
if (format == "mmjson")
return CoorFormat::Mmjson;
if (format == "chemcomp")
return CoorFormat::ChemComp;
gemmi::fail("unknown file format: " + format);
}

// IIUC passing string by value is OK here, it's copied the JS side anyway
gemmi::Structure read_structure(std::string buf, std::string name, std::string format) {
return gemmi::read_structure_from_memory(buf.data(), buf.size(), name,
format_to_enum(format));
}

template <typename T>
size_t get_children_length(const T& t) { return t.children().size(); }

template <typename T>
typename T::child_type* get_child(T& t, size_t n) { return &t.children().at(n); }

template <typename T>
em::class_<T> wrap_children() {
return em::class_<T>(T::what())
.template constructor<>()
.property("length", &get_children_length<T>)
.function("get", &get_child<T>, em::allow_raw_pointers())
;
}

EMSCRIPTEN_BINDINGS(Gemmi) {
em::class_<gemmi::UnitCell>("UnitCell")
.property("a", &gemmi::UnitCell::a)
.property("b", &gemmi::UnitCell::b)
.property("c", &gemmi::UnitCell::c)
.property("alpha", &gemmi::UnitCell::alpha)
.property("beta", &gemmi::UnitCell::beta)
.property("gamma", &gemmi::UnitCell::gamma)
;

wrap_children<gemmi::Structure>()
.property("name", &gemmi::Structure::name)
.property("cell", &gemmi::Structure::cell)
;

wrap_children<gemmi::Model>()
.property("name", &gemmi::Model::name)
.function("count_occupancies", &gemmi::count_occupancies<gemmi::Model>,
em::allow_raw_pointers())
;

wrap_children<gemmi::Chain>()
.property("name", &gemmi::Chain::name)
;

wrap_children<gemmi::Residue>()
.property("subchain", &gemmi::Residue::subchain)
;

em::class_<gemmi::Atom>("Atom")
.constructor<>()
.property("name", &gemmi::Atom::name)
.property("altloc", &gemmi::Atom::altloc)
.property("charge", &gemmi::Atom::charge)
.property("serial", &gemmi::Atom::serial)
.property("occ", &gemmi::Atom::occ)
.property("b_iso", &gemmi::Atom::b_iso)
;

em::class_<gemmi::Selection>("Selection")
.constructor<>()
;

// wrapped in pre.js to add default value
em::function("_read_structure", &read_structure);
}
4 changes: 4 additions & 0 deletions wasm/pre.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

Module['read_structure'] = function (buf, name, format) {
return Module._read_structure(buf, name, format || 'unknown');
};

0 comments on commit dff1720

Please sign in to comment.