Skip to content

thi-ng/geom

Repository files navigation

thi.ng/geom

./assets/svo-d7.jpg

https://badges.gitter.im/Join%20Chat.svg

Discussion forum | thi.ng website | training workshops

Contents

About the project

Leiningen coordinates

Latest snapshot

[thi.ng/geom "0.0.1178-SNAPSHOT"]

Latest stable

[thi.ng/geom "0.0.908"]

Additional dependencies for JOGL

If you’re intending to use this library’s OpenGL functionality under Clojure (not ClojureScript), the following additional native dependencies must be added to your project (for each platform you intend to use):

[org.jogamp.gluegen/gluegen-rt "2.3.2" :classifier "natives-macosx-universal"]
[org.jogamp.jogl/jogl-all "2.3.2" :classifier "natives-macosx-universal"]

The full list of supported platform :classifier’s:

natives-android-aarch64.jar
natives-android-armv6.jar
natives-linux-amd64.jar
natives-linux-armv6.jar
natives-linux-armv6hf.jar
natives-linux-i586.jar
natives-macosx-universal.jar
natives-solaris-amd64.jar
natives-solaris-i586.jar
natives-windows-amd64.jar
natives-windows-i586.jar

Overview

thi.ng/geom is a comprehensive and modular geometry & visualization toolkit for Clojure/ClojureScript. It provides a large set of purely math & geometry oriented data types, a polymorphic, largely protocol based API to transform/convert types and various ways to create interactive visualizations in SVG, WebGL, OpenGL, both in the browser and in desktop environments.

Embracing Clojure’s approach of data transformations, the library’s core philosophy is based on a functional approach to generative design tasks with hundreds of hours spent on refinining & optimizing the core API for both Clojure & Clojurescript.

Unlike most other open source projects, this project has been developed in a literate programming style and has been in active, regular development since late 2011, currently in its 4th iteration/rewrite cycle.

You’re highly encouraged to consult the source code, which contains documentation, examples, diagrams and general usage pattern hints.

This project is part of the thi.ng collection of Clojure & Clojurescript libraries and makes uses of several other projects in this collection (see dependencies further below).

Example usage

A growing number (currently ~40) of small examples are included in this repo under the /examples directory:

Interactive examples:

Growing list of thi.ng workshop repositories (These workshops were running on a monthly basis & internationally in 2015/2016):

  • WS-LDN-1
  • WS-LDN-2
  • WS-BLN-1
  • WS-LDN-5 (WebGL, source code forthcoming)
  • WS-LDN-6 (Desktop apps, OpenGL/WebGL, source code forthcoming)
  • WS-LDN-8 (WebGL, WebRTC, Web workers, asm.js)

A preliminary list of other projects using this library:

Project structure

Use the diagram below to quickly navigate to any namespace in the project. Nodes in the graph have tooltips with a brief description of each namespace. Note: Due to GH restrictions on SVG files, first click anywhere on the diagram before trying to navigate to a specific namespace.

http://dev.thi.ng/geom/overview2.svg

Main features (non-exhaustive list)

  • Comprehensive & optimized 2D/3D vector & matrix algebra / transformations
  • Custom, optimized vector types w/ GLSL style vector swizzling & full Clojure sequence API support
  • Unified, easy-to-learn & extensible core API defined via ~50 polymorphic protocols
  • Unified OpenGL (v3.3+) & WebGL abstractions of common features, shader, buffer & texture utilities
  • Declarative GLSL shader specs and code generation
  • Optional OpenGL/WebGL shader presets based on thi.ng/shadergraph
  • Declarative OpenGL/WebGL FBO-based multi-pass shader pipeline creation & execution tools
  • OpenGL helpers for desktop apps (wrapping JOGL)
  • React.js component helpers for WebGL
  • 3D SVG mesh renderer with software facet shader support
  • SVG generation API & optional conversion of geom types
  • Declararitve 2D data visualization module with various modes (area, bar, line, scatter, contours, stacks etc., cartesian/polar axes)
  • 35+ custom (mainly immutable) geometry types implementing core protocols
  • Shape analysis (surface area, circumference, distance & volume calculations) for all implemented types
  • 2D/3D intersection & classification checks (vs. point, line, ray, shapes)
  • Access entities as graph-like structures (vertices/edges)
  • 2D/3D platonic entity to 3D polygon mesh conversion (w/ many options)
  • Optimized conversions to OpenGL/WebGL (with attributes, indexed, non-indexed)
  • Flexible & customizable mesh vertex attribute generators
  • Subdivision meshes (Catmull-Clark, Doo-Sabin, Butterfly)
  • Parallel-transport frame sweep mesh generation from point sequences (skinning with arbitrary profiles, incl. profile morphing)
  • 3D Lathe meshes from 2D curves
  • 2D shape extrusions as 3D mesh (solid or walled)
  • Delaunay triangulation of 2D point clouds
  • Tesselation of simple 2D polygons (no holes)
  • Basic SVG <path> parsing with different segment types
  • Basic insetting of simple 2D polygons (no miter support)
  • Shape subdivision (only lines, triangles, rects, quads, tetrahedrons)
  • Shape boundary sampling (at fixed resolution or uniform distance)
  • Sutherland-Hodgeman clipping of 2D polygons
  • 3D Boolean (CSG) operations on meshes (union, difference, intersection)
  • 3D geometry export (PLY, STL, OBJ, OFF formats)
  • 3D mesh repair tools (T-junctions, unify vertices etc.)
  • 2D/3D particle based Verlet Physics with customizable behaviors & constraints
  • 2D convex hull
  • 2D/3D quadtree/octree (mutable) for fast spatial indexing
  • Automatic curve generation from point seqs (cubic, Chaikin etc.)
  • Unfolding of 3D meshes to 2D (WIP) for digital fabrication

…

API scope

Since the core library does only deal with pure “platonic” geometry types, it doesn’t directly address any display or rendering functionality at all. However a number of support modules are provided, incl. OpenGL 3/4, WebGL & SVG support, to allow visualizing results and/or exporting generated assets. 2D/3D shape/mesh exporters are provided as well and together with sister libraries like thi.ng/luxor, it’s also possible to generate complete 3D scenes for high quality & high resolution non-realtime rendering using Luxrender.

Furthermore, providing all functionality under a common API for both Clojure & Clojurescript, makes it trivial to offload heavy operations (e.g. large mesh processing) to the server side.

Project structure

core

source

These namespaces define the core functionality of this library, including the approx. 50 protocols and implementations of fundamental geometry types/functions like 2d/3d vector algebra, matrices, quaternion (+ related convenience constructors & conversions)

types

source

This directory contains all high-level 2d/3d data types with their implementations of the various core protocols. From a user perspective, these namespaces defined here provide most of this project’s core functionality.

utils

source

A number of often needed utility functions to deal with point collections, normals, path sampling, triangle properties etc. Also included here are shape intersection tests, curve subdivisions and 2D Delaunay triangulation.

mesh

source

Several tools & operations related to working with 3d meshes, incl. I/O, subdivisions, repair / cleaning, CSG / Boolean mesh merge, mesh generators (polyhedra, lathe etc.)

physics

source

This module provides a simple 2d/3d particle-based physics engine with Verlet integration and support for custom behaviors and constaints, both for individual particles and global. Particles can be connected with springs of varying stiffness as well as made interdependent using positive or negative force fields (attractors).

svg

source

A module to help with building SVG based visualizations of geom entities using hiccup compatible syntax. Includes a customizable 3D mesh renderer w/ software shader support.

viz

source

Declarative, highly customizable 2D data visualization module with ~10 different layout methods, 3 axis types, cartesian and polar domain support. Currently SVG only, but planned to be format-independent.

voxel

source

An experimental implementation of a in-memory sparse voxel tree (SVO) and related functionality to extract isosurface polygon meshes from the tree.

OpenGL & WebGL

source

This module provides a unified API to common OpenGL/WebGL functionality (context creation, shader management & presets, buffer management, textures, FBO etc.), as well as a number of optimized mesh types, conversion & rendering functions, cameras etc. to simplify the use of other geometry types defined in this project with OpenGL, both on the desktop and in the browser. The Clojure version wraps JOGL.

Status

The project has been in active, regular development since late 2011 and is currently in its 4th iteration/rewrite cycle. It should still be considered BETA quality. On the other hand, the library is mature and has been used successfully in several commercial projects over the past 3 years and can be considered stable for most use cases.

A full test suite, website & tutorials are actively being worked on and the various examples are used as test cases as well.

Note: This library heavily relies on the conditional reader syntax of recent Clojure & Clojurescript versions and therefore is not compatible with Clojure versions < 1.7.0…

Project definition

Building & testing this project

This project is written in a literate programming format and requires Emacs & Org-mode to generate usable source code. Assuming both tools are installed, the easiest way to generate a working project is via command line (make sure emacs is on your path or else edit its path in tangle.sh):

git clone https://github.com/thi-ng/geom.git
cd geom

# tangle all modules...
./tangle-all.sh

# tangle a single module
./tangle-module.sh svg

# ...or just tangle selected files
./tangle.sh geom-core/src/*.org geom-core/test/*.org

# ...or individual file
./tangle.sh geom-core/src/utils.org

Tangling is the process of extracting & combining source blocks from .org files into an actual working project/source tree. This project currently consists of 30+ namespaces and 13,000+ lines of code (excluding examples), the tangling of which takes approx. 2 mins on a 2010 MBP…

Once tangling is complete, you can cd into the generated project directory of a module (babel in this case) and then use lein as usual.

Testing

The project.clj files of each module define an alias to trigger a complete build & tests for both CLJ & CLJS versions.

cd <<module-name>>/babel
lein cleantest # some tests currently fail due to still missing protocol impls

Important note: Due to the newness of the conditional reader expressions and the CLJC file format introduced with Clojure 1.7, the lein test task does currently not find any of the Clojure tests automatically and you’ll need to specify them manually as argument to this command. E.g. like this:

lein test thi.ng.geom.core.test.core

Afterwards, to build the Clojurescript version simply run lein cljsbuild test from the same directory. A small HTML harness for the resulting JS file is also located in that folder (babel/index.html), allowing for further experimentation in the browser.

Working with the REPL

Editing code blocks / files in Org-mode, then re-loading & testing changes is quite trivial. Simply launch a REPL (via lein or Emacs) as usual. Everytime you’ve made changes to an .org file, re-tangle it from Emacs or tangle.sh, then reload the namespace in the REPL via (require 'thi.ng.geom... :reload) or similar.

Injected properties

https://github.com/thi-ng/geom
(concat
 "0.0."
 (replace-regexp-in-string
  "\\`[ \t\n]*" ""
  (replace-regexp-in-string
   "[ \t\n]*\\'" ""
   (shell-command-to-string "git describe --match 'v0.0' --dirty | cut -d '-' -f 2,4"))))

Dependencies

Runtime

(Please also see note about optional OpenGL dependencies)

[org.clojure/clojure "1.8.0"]
[org.clojure/clojurescript "1.9.89"]
[thi.ng/color "1.2.0"]
[thi.ng/dstruct "0.2.1"]
[thi.ng/math "0.2.1"]
[thi.ng/ndarray "0.3.2"]
[thi.ng/strf "0.2.2"]
[thi.ng/typedarrays "0.1.6"]
[thi.ng/xerror "0.1.0"]

Development

[lein-cljsbuild "1.1.3"]
[criterium "0.4.4"]
[com.cemerick/clojurescript.test "0.3.3"]
[perforate "0.3.4"]
[perforate-x "0.1.0"]
[lein-npm "0.6.2"]
[benchmark "1.0.0"]

Leiningen project file

(defproject <<project-name>> "<<project-version()>>"
  :description  "thi.ng geometry kit - meta project spec including all modules"
  :url          "<<project-url>>"
  :license      {:name "Apache Software License"
                 :url  "http://www.apache.org/licenses/LICENSE-2.0"
                 :distribution :repo}
  :scm          {:name "git"
                 :url  "<<project-url>>"}

  :min-lein-version "2.4.0"

  :dependencies  [<<dep-clj>>
                  <<dep-cljs>>
                  <<dep-color>>
                  <<dep-dstruct>>
                  <<dep-math>>
                  <<dep-ndarray>>
                  <<dep-shadergraph>>
                  <<dep-strf>>
                  <<dep-tarrays>>
                  <<dep-xerror>>
                  <<dep-jogl>>
                  <<dep-cljs-log>>
                  <<dep-hiccup>>]

  :perforate    {:environments [{:namespaces [thi.ng.geom.bench.core.vector]}]}

  :profiles     {:dev   {:dependencies      [<<dep-criterium>>]
                         :plugins           [<<dep-cljsbuild>>
                                             <<dep-cljs-test>>]
                         :node-dependencies [<<dep-npm-benchmark>>]
                         :global-vars       {*warn-on-reflection* true}
                         :jvm-opts          ^:replace ["-Dclojure.compiler.direct-linking=false"]
                         :aliases           {"cleantest" ["do" "clean," "test," "cljsbuild" "test"]
                                             "bench" ["with-profile" "bench" "do" "clean," "perforate," "cljsbuild" "test"]}}
                 :bench {:dependencies [<<dep-perforate-x>>]
                         :plugins      [<<dep-perforate>>
                                        <<dep-lein-npm>>]
                         :cljsbuild
                         {:builds
                          [{:id             "bench"
                            :source-paths   ["src" "test" "benchmarks"]
                            :notify-command ["node" "target/cljs/benchmark.js"]
                            :compiler       {:target        :nodejs
                                             :output-to     "target/cljs/benchmark.js"
                                             :optimizations :simple
                                             :pretty-print  true}}]
                          :test-commands {"unit-tests" ["phantomjs" :runner "<<cljs-artefact-path>>"]}}}}

  :cljsbuild    {:builds [{:id           "simple"
                           :source-paths ["src" "test" "examples/gl"]
                           :compiler     {:output-to     "<<cljs-artefact-path>>"
                                          :optimizations :whitespace
                                          :pretty-print  true}}
                          {:source-paths ["src" "examples/gl"]
                           :id           "prod"
                           :compiler     {:output-to      "<<cljs-artefact-path>>"
                                          :optimizations  :advanced
                                          :pretty-print   false}}]
                 :test-commands {"unit-tests" ["phantomjs" :runner "<<cljs-artefact-path>>"]}}

  :pom-addition [:developers
                 [:developer
                  [:name "Karsten Schmidt"]
                  [:url "http://postspectacular.com"]
                  [:timezone "0"]]])

ClojureScript HTML harness

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0">
    <title><<project-name>> <<project-version()>> test</title>
    <style type="text/css" media="screen">
      .stats { position: fixed; top: 0px; left: 0px; }
    </style>
  </head>
  <body>
    <canvas id="main" width="1280" height="720"></canvas>
    <script type="text/javascript" src="dev-resources/stats.js"></script>
    <script type="text/javascript" src="dev-resources/webgl-texture-float-extension-shims.js"></script>
    <script type="text/javascript" src="<<cljs-artefact-path>>"></script>
  </body>
</html>

Accessing library version during runtime

The autogenerated namespace thi.ng.geom.version contains a single symbol version holding the version string defined above:

(use 'thi.ng.geom.version)

(prn version)
; "<<project-version()>>"

Version namespace

(ns thi.ng.geom.version)
(def version "<<project-version()>>")

Release history & changelog

See CHANGELOG.org for further details.

VersionReleasedLein coordinatesTagged Github URL
0.0.9082015-11-08[thi.ng/geom "0.0.908"]r908
0.0.8812015-06-21[thi.ng/geom "0.0.881"]r881
0.0.8592015-06-15[thi.ng/geom "0.0.859"]r859
0.0.8562015-06-14[thi.ng/geom "0.0.856"]r856
0.0.8152015-06-01[thi.ng/geom "0.0.815"]r815
0.0.8032015-05-26[thi.ng/geom "0.0.803"]r803
0.0.7832015-04-27[thi.ng/geom "0.0.783"]r783
0.0.7702015-03-29[thi.ng/geom "0.0.770"]r770
0.0.7432015-03-23[thi.ng/geom "0.0.743"]r743
0.0.7372015-03-22[thi.ng/geom "0.0.737"]r737
0.0-7252015-03-15[thi.ng/geom "0.0-725"]r725
0.0-7152015-02-25[thi.ng/geom "0.0-715"]r715
0.0-7092015-02-22[thi.ng/geom "0.0-709"]r709
0.2.02014-03-10[thi.ng/geom "0.2.0"]0.2.0

Contributors

NameRoleWebsite
Karsten Schmidtinitiator & principal developerpostspectacular.com
thi.ng

License

This project is open source and licensed under the Apache Software License 2.0.