Skip to content
#edge computing Open access

dfsp-spirit/scimesh: v0.4.0

Sep 2026 · Zenodo (CERN European Organization for Nuclear Research)

Abstract

Version 0.4.0 -- Batch primitives, tubes, line layers and text labels BRK: clip planes are now defined in world space by default. In 0.3.4 and earlier the normal and offset were interpreted in eye (camera) space, so the cut travelled with the camera and had to be re-tuned for every viewpoint. For the old behaviour, pass PlaneSpace::EYE (C++) or clip_plane(..., space = "eye") (R). Note that the old behaviour was also documented incorrectly: the docs described a world-space plane. See ClipPlane::space and ?clip_plane. BRK: fog distances (fog_start/fog_end) are now given in world units (distance from the camera) by default, and are therefore independent of the near/far planes and of the projection type. The legacy interpretation as raw normalized device depth is still available via FogSpace::NDC (C++) or fog_space = "ndc" (R). Previously the C++ docs claimed world units (which they were not) and the R docs claimed normalized depth in [0, 1] (the values were actually raw depth buffer values in [-1, 1]). NEW: polyline tubes. generate_tube() sweeps a circular cross-section along a path of points, and generate_multi_tubes() batches many such paths into a single mesh. The cross-section frames are computed by parallel transport (rotation-minimizing frames), so tubes do not twist around their own axis, and mitered joints keep the segments watertight. R: generate_tube() and generate_tubes(). Useful for curved edges, streamlines and any shape that is not a straight cylinder. NEW: spline sampling — smooth curves through ordered 3D points. A new header-only module (src/core/scimesh/spline.h) turns a coarse list of waypoints into a dense path, which is what the path-taking features of the library need and what callers previously had to bring themselves (the generate_tube() documentation promised "arcs, Bezier samples, streamlines" without offering a way to produce one). catmull_rom_path() interpolates the input points and is the default choice; it is centripetal by default, which is what keeps unevenly spaced points (the norm for measured data) from producing cusps, loops and self-intersections, and alpha = 0 recovers the textbook uniform curve. hermite_path() takes caller-supplied tangents instead of deriving them, bspline_path() approximates the points (for smoothing noisy input, at the cost of not passing through them) and bezier_path() samples a control polygon with de Casteljau. Around those, resample_by_arclength() evens out the spacing along a path — the sweep places exactly one cross-section per path point, so uniform arc length spacing is what makes a tube look evenly subdivided — and path_length(), path_tangents() and path_curvature() are the queries that drive it. path_curvature() is the one worth knowing about: sweeping a tube of radius r along a curve whose curvature reaches kappa folds the tube inside out, so 1 / max(path_curvature(path)) is the largest radius a path can take. Open curves get a reflected phantom neighbour at both ends (so they start and end exactly at the first and last point, along the outer segment), closed curves use their wrapped neighbours, and both closed = true cases end on a copy of the first point, so a swept tube closes on itself; the arc-length resampling of a closed path is covered in a whole number of steps so the seam is a regular step instead of a leftover gap. This is pure geometry on std::vector — no mesh, no renderer, no I/O, no new dependency, and not a domain-specific feature — so it composes with primitives.h, lines.h and your own code alike, including camera paths. R: spline_path(), bezier_path(), resample_path(), path_length() and path_curvature(). examples/cpp/spline_tube/ and examples/R/spline_tube/ render the same waypoints as a faceted polyline tube and as a smooth spline tube side by side, plus a closed trefoil knot. NEW: line layers — screen-space lines as first-class scene primitives. A LineLayer stores segments, per-segment colors and a width in pixels and creates no geometry, which makes it the cheap way to draw thousands of thin lines (wireframes, graph or connectome edges, trajectories). Line layers are drawn by Renderer::render_scene() in the same pass as the meshes (same camera, same depth buffer, back-to-front blending for translucent lines), they contribute to Scene::compute_bounding_box() unless their affects_bounds flag is false (see below), and the mesh exporters skip them (scimesh_write_gltf() warns). R: line_layer(), the lines argument of scene(), and render_segments() for free-standing lines. NEW: line layers are content by default, i.e. they define the bounding box of their scene exactly like a mesh does, and the camera fitted to the scene covers them. This is what makes a scene that contains only lines drawable at all: a tractogram or a connectome without a brain surface used to have no geometry to derive a camera from. Lines that are decoration rather than content (a leader line pointing at a label, an axis cross, a scale bar drawn as segments) can opt out with LineLayer::affects_bounds = false (R: line_layer(..., affects_bounds = FALSE), and scene_set_line_affects_bounds(scene, index/name, affects_bounds) for a layer that is already part of a scene), so that adding such a layer can never push the camera away from the data. A scene without meshes is framed by its lines even if they all opted out, since there is nothing else to fit. Note that text layers are still ignored by the bounding box, because the extent of a label depends on the font and the output size rather than on world space. NEW: R function camera_fit_scene() fits a camera to a whole scene, i.e. to its meshes together with the line layers that affect the bounds. It is the scene counterpart of camera_auto() (which takes meshes), and the two share the same framing convention (bounding box center, distance from the extent). camera_auto() now also documents that it does not consider scene contents. NEW: Renderer::render_lines_raw() renders line segments directly, and Rasterizer::rasterize_line() rasterizes a segment with a screen-space width (flat/unlit by default, like hardware line rendering). NEW: R functions generate_multi_spheres() and generate_multi_cylinders() expose the batched primitive generators (one mesh for thousands of spheres or cylinders), which previously existed only in C++. NEW: caps argument for generate_cylinder() and generate_multi_cylinders() (R and C++). Open cylinders/tubes use about half the vertices and triangles, which matters for large edge bundles whose ends are hidden inside node spheres. The default is unchanged (caps = TRUE). PERF: the batched generators (generate_multi_spheres(), generate_multi_cylinders(), generate_multi_tubes()) reserve the exact final size before merging, instead of growing the arrays incrementally. FIX: the batched generators no longer read out of bounds when the radii or colors array is empty (missing entries are recycled from the first entry; an empty array now means "use the default", i.e. radius 1.0 and white). FIX: Rasterizer::rasterize_line() qualified the shadowed width/height members in its pixel bounds check (the width parameter of the function shadowed Rasterizer::width), which made the line invisible. FIX: the C++ core now compiles with libc++ (clang on macOS). src/core/primitives.cpp used std::array (pyramid and tetrahedron faces) without including , and std::swap without . libstdc++ (GCC, i.e. the Linux and Windows builds) pulls both in transitively, so the bug was invisible there; libc++ does not, so the package failed to install on macOS with "implicit instantiation of undefined template 'std::array<...>'". libc++ only forward-declares std::array in <__tuple> (for tuple_size and tuple_element), which is why the message says "undefined template" instead of "no member named array". This is the installation ERROR reported by the CRAN checks for 0.3.4 on r-release-macos-x86_64, r-oldrel-macos-x86_64 and r-oldrel-macos-arm64. All source files are now verified to compile with libc++ 14 (the toolchain used by those three flavors) and libc++ 18, as well as with libstdc++. NEW: global anti-aliasing default. render_options() now takes aa_samples = NULL and resolves that NULL from the global option scimesh.aa_samples (default 1, i.e. no AA). Setting options(scimesh.aa_samples = 2) therefore switches on 2x2 supersampling for every render call that does not pass aa_samples explicitly (2 for 2x2, 4 for 4x4 SSAA). This is mainly interesting for screen-space lines and points, whose hard edges become smooth boundaries under supersampling; the cost is proportional to aa_samples^2 in render time and memory. Passing a number explicitly still overrides the option, and invalid values (including an invalid global option) are reported as errors instead of being ignored. NEW: text labels — annotations for figures, without any geometry. A TextLayer stores strings with anchor positions, a font size in pixels, colors and optional halos, and is drawn by Renderer::render_scene() after the meshes and the lines, so labels end up on top of the geometry. Positions are either world space (projected with the scene camera, so a label sticks to the brain region or atom it annotates) or screen space (output pixels, which is what titles, panel tags and captions need). Labels are billboards: they always face the camera and keep their pixel size, and they are hidden by geometry in front of their anchor unless depth_test = FALSE, so a world-space label behaves like an annotation of a surface instead of shining through the model. adj places the anchor on the text box (as in rgl), pixel offsets nudge a label next to its anchor, multi-line labels are supported, and halos keep text readable on dark or busy geometry. Glyphs are rasterized with stb_truetype (vendored, public domain) from the bundled inst/extdata/Inter-Regular.ttf (SIL OFL 1.1), which can be replaced per label or by setting th

View source

Similar papers

#computer vision Review Sep 2017

Agile Software Development Methods: Review and Analysis

This publication proposes a definition and a classification of agile software development approaches and analyses ten software development methods that can be characterized as being "agile" against the defined criterion.

P. Abrahamsson, O. Salo, Jussi Ronkainen et al. · 727 citations · ⚡54
#computer vision Jun 2008

The impact of agile practices on communication in software development

The study shows that agile practices improve both informal and formal communication, but indicates that, in larger development situations involving multiple external stakeholders, a mismatch of adequate communication mechanisms can sometimes even hinder the communication.

M. Pikkarainen, Jukka Haikara, O. Salo et al. · 401 citations · ⚡48
#machine learning Review Open access Oct 2014

Software development in startup companies: A systematic mapping study

The results indicate that software engineering work practices are chosen opportunistically, adapted and configured to provide value under the constrains imposed by the startup context.

Nicolò Paternoster, Carmine Giardino, M. Unterkalmsteiner et al. · 394 citations · ⚡54

Related blog posts

Microsoft Research Blog Oct 6, 2026

What AI gets wrong and what failure teaches us

Jennifer Neville did not want to go into computer science—but that’s exactly where she landed. Neville discusses the starts and stops that led to her professional sweet spot and her work identifying “surprising failures” making it hard for AI to handle complexity.  The post What AI gets wrong and what failure teaches us appeared first on Microsoft Research.

We use cookies to run the site and, with your consent, for analytics and to show ads. See our Cookie Policy.