Commit 01b2fe3
committed
[1.3.82] 2026-08-23
## Core
- Added `annotation_io.h`, a set of utilities for writing machine-learning image annotations in the standard YOLO and COCO formats. These operate on plain binary masks and bounding boxes rather than on any particular camera or renderer, so any plug-in that can produce a per-pixel object label can write annotations in the same formats. The radiation and synthetic annotation plug-ins now share this implementation.
- The `nlohmann/json` header is now bundled with the core library at `core/lib/json/` and is on the include path of every plug-in, rather than being private to the radiation plug-in. Code that includes `json.hpp` is unaffected.
- Fixed `loadXML()` throwing "ERROR (Context::addVoxel): Voxel has size of zero." on any file containing a `<voxel>` element. The loader constructed the placeholder voxel with a size of zero before applying its transformation matrix, so a scene containing voxels could be written by `writeXML()` but never read back. Voxel center and size now survive the round-trip.
- Removed the declaration of `point_distance()` from `global.h`. It was declared but never defined anywhere in the library, so any user who called it got a link error rather than a distance.
## Radiation
- Removed the deprecated overloads of `writeImageBoundingBoxes()` and `writeImageBoundingBoxes_ObjectData()` that took `(..., imagefile_base, image_path, append_label_file, frame)`. They silently captured calls written against the current `(..., image_file, classes_txt_file, image_path)` signature whenever the trailing arguments were string literals, because their by-value `uint` class-ID parameter was a better match than the current overloads' `const uint &`. The class name file was then treated as the output directory and the call failed with "Expected a directory path but got a file path for argument 'image_path'", with no deprecation warning to indicate what had happened.
- Fixed the first two values of each bounding-box annotation written by `writeImageBoundingBoxes()` being formatted differently from the last two, because the fixed-precision format was applied partway through the output expression. The centre coordinates could be written in scientific notation while the width and height were not.
- The COCO and YOLO writing, connected-component labeling and boundary tracing now come from the shared core annotation utilities rather than being private to `RadiationModel`. Output is unchanged.
- Fixed `writeImageSegmentationMasks()` and `writeImageSegmentationMasks_ObjectData()` writing at most one annotation per label value, silently omitting every other separate region carrying that value. Any object split into more than one visible region — most commonly by an occluder in front of it, such as a leaf behind a stem — had all but one of its pieces dropped from the COCO file with no warning or error, so the output looked complete while objects plainly visible in the image were missing. Files regenerated with this version will contain more annotations than before wherever this occurred.
- Fixed connected components in the segmentation mask writers being found with 4-connectivity while their boundaries were traced with 8-connectivity. A diagonally-connected region was therefore split into one component per pixel, which combined with the above to drop it from the output entirely.
- `createWithBackend()`, `exportColorCorrectionMatrixXML()` and `loadColorCorrectionMatrixXML()` are now private. All three were public solely so the test suite could reach them — the first is documented as "INTERNAL TEST-ONLY … not for production use", and the latter two are implementation details of `autoCalibrateCameraImage()` that were marked "public for testing" — so none had any user-facing meaning. The tests reach them through a `RadiationModelTestHelper` friend class defined solely in the plug-in's `selfTest.cpp`, so nothing test-related is exposed to library users.
## LiDAR
- Removed the public `forceBruteForceLeafArea()`. It existed only so the self-tests could A/B the brute-force leaf-area path against the fast DDA path, has no effect on results, and was documented as "not needed in normal use"; the tests now set the underlying private flag through a `LiDARTestHelper` friend class defined in the plug-in's `selfTest.cpp`.
## Visualizer
- Added `enableExactColorMode()` and `disableExactColorMode()`, which control whether primitive colors are reproduced exactly in the rendered image. The fragment shader multiplies vertex-interpolated colors by 1.5 to brighten ordinary renders, and did so unconditionally -- including under `LIGHTING_NONE` -- so a color set with `Context::setPrimitiveColor()` never matched the color read back with `getWindowPixelsRGB()`, and any channel above 170 saturated to 255. This only matters when the rendered image is used to carry data rather than to be looked at; the default behavior is unchanged.
- Fixed `clearGeometry()` leaving the GPU-side geometry buffers untouched. Clearing all geometry marks no individual primitive as dirty, and the buffer upload is skipped when nothing is dirty, so the previously uploaded geometry was retained and continued to be rendered.
- Added `displayImageWithSegmentationMasks()`, which displays an image with the segmentation masks from a COCO JSON annotation file overlaid, each drawn as a translucent filled polygon with a solid outline and its class name on a filled chip. This is the format written by `RadiationModel::writeImageSegmentationMasks()`; class names are read from the file's own `categories` array, so no separate class name file is needed, and masks are colored per annotation rather than per class so that touching objects of the same class stay distinguishable. The fill opacity and the class labels can each be turned off with optional arguments, and the fill is computed by an even-odd scanline fill so that it is correct for the self-intersecting contours the writer produces wherever a mask narrows to a one-pixel neck. The new `readSegmentationMaskFile()` exposes the parsing on its own.
- Added `displayImageWithBoundingBoxes()`, which displays an image with the bounding boxes from a YOLO-format annotation file overlaid as colored outlines, each labeled with its class name on a filled chip inside the box's top-left corner. Class names are read from a file in either the `<class_ID> <class_name>` form written by `RadiationModel::writeImageBoundingBoxes()` or the standard one-name-per-line Ultralytics form; when no class file is given, `classes.txt` beside the annotation file is used if present, and boxes are otherwise labeled with their numeric class ID. The new `readBoundingBoxFile()` and `readBoundingBoxClassNames()` expose the parsing on its own.
- Added `getTextboxSize()`, which returns the extent a string would occupy if rendered by `addTextboxByCenter()`, in window-normalized units. `addTextboxByCenter()` centers text and discarded the width it computed internally, so there was previously no way to align text by any edge.
- Fixed `displayImage()` and `clearGeometry()` throwing `std::out_of_range` when called after anything had already been plotted. Both clear all geometry but left the visualizer's cached identifiers for the watermark, background rectangle, background sky, coordinate axes, navigation gizmo and colorbar pointing at geometry that no longer existed; deleting by one of those afterwards looked up a map key that had been erased. The sequence `plotUpdate(); displayImage(file);` aborted instead of displaying the image.
- Corrected the documentation of `COORDINATES_WINDOW_NORMALIZED`, which stated that an object at z=0.5 would be in front of an object at z=0. Smaller z is nearer the viewer, as the plug-in documentation already stated.
- `createShadowFramebuffer()`, `setupOffscreenFramebuffer()`, `cleanupOffscreenFramebuffer()` and `renderToOffscreenBuffer()` are now private. They are internal framebuffer lifecycle machinery driven by headless/offscreen rendering, with no user-facing meaning; the first three had no caller outside the plug-in at all, and the tests reach the last through the existing `VisualizerTestHelper` friend class.
- Fixed `Visualizer` construction changing the working directory of the host process on macOS. `Visualizer::initialize()` calls `glfwInit()`, whose `GLFW_COCOA_CHDIR_RESOURCES` init hint defaults to enabled and moves the process to the `Contents/Resources` directory of the host application's bundle; the hint is now disabled beforehand. A bare Helios executable has no bundle and was unaffected, but a `.app`-packaged host — most commonly a macOS framework-build Python interpreter, whose real executable lives inside `Python.app` — had its working directory silently relocated, after which construction failed with `ERROR (Context::readJPEG): File plugins/visualizer/textures/gradient_background.jpg could not be opened` for a file that exists and is readable, and every subsequent relative path in the host application resolved against the wrong directory.
- The six remaining visualizer textures loaded by a path relative to the process working directory — the gradient and transparent backgrounds used by `setBackgroundGradient()`, `setBackgroundTransparent()` and `printWindow()`, and the three navigation gizmo bubbles — now resolve through `helios::resolvePluginAsset()`, as the shaders, fonts, skydome and watermark already did. Any host that had changed the working directory before using the visualizer, for any reason, previously failed to load them.
## Plant Architecture
- Fixed `pruneBranch()` failing partway through when pruning a whole branch system. Pruning a shoot also empties every shoot descending from it, so a loop over shoot IDs reaches those descendants again later; `pruneBranch()` rejected them with `Node index 0 is out of range for shoot N`, aborting the loop and leaving the plant half-pruned. Pruning a shoot that has already been pruned away is now a no-op, while a node index that is genuinely out of range on a live shoot is still an error.
- Fixed `advanceTime()` segfaulting on a plant containing a pruned branch when internode context geometry was disabled with `disableInternodeContextBuild()`. A pruned shoot's apical meristem was killed only as a side effect of deleting its internode tube object, so with no tube objects to delete the emptied shoot was still treated as growable and `advanceTime()` dereferenced its (now non-existent) last phytomer. `pruneBranch()` now terminates the apical bud of any shoot it empties.
- Fixed `getShootTaper()` segfaulting when called on a pruned shoot, which has no internode geometry to read a radius from. It now throws an error naming the shoot and pointing at the new `isShootPruned()`.
- Fixed `Shoot::sumShootLeafArea()` and `Shoot::sumChildVolume()` throwing `Start node index out of range` for a pruned shoot, which has no nodes for index 0 to be in range of. Both now return zero. This also affected live shoots: because a pruned shoot was left in its parent's child list, summing the leaf area of an ancestor descended into the pruned shoot and threw, so the leaf area of any branch containing a pruned shoot could not be obtained at all.
- A shoot pruned away by `pruneBranch()` is now unlinked from its parent's child list and its deleted internode tube object ID is reset to the sentinel, matching `pruneGroundCollisions()`. Previously the shoot remained reachable as a child of a live shoot despite having no phytomers or geometry, so recursive traversals of the plant structure descended into it, and it retained the object ID of a tube object that had been deleted from the Context.
- Added `PlantArchitecture::isShootPruned()` and `Shoot::isPruned()`, which report whether a shoot has been pruned away entirely. A pruned shoot keeps its slot in the plant's shoot tree so that shoot IDs are not renumbered by a prune, so its ID stays valid and is still returned by `getAllShootIDs()` even though the shoot no longer forms part of the plant; there was previously no way to tell the two apart.
- Added `setPlantMaxAge()` and `getPlantMaxAge()` to set and query the age at which a plant stops growing. `PlantInstance::max_age` defaults to 999 days and every library builder overrides it, but a plant assembled through the manual API (`addPlantInstance()` with `addBaseStemShoot()`/`appendShoot()`/`addChildShoot()`) kept the default and so silently stopped growing after roughly 1000 days, with no message and no public way to raise the limit.
- Maize ear position is now defined relative to the tassel rather than at fixed node indices, and a maize plant now bears one ear by default instead of three. `MaizePhytomerCreationFunction()` placed ears at absolute nodes 9-11, which only approximated the biology at the default `max_nodes` of 17; raising `max_nodes` left the ears stranded low in the canopy, so a taller plant produced fewer ears than a shorter one. The ear now forms at `max_nodes - 6` (node 14 of the new 20-node default, node 19 of 25), matching the observation that ear meristems form at every node except the upper six to eight and only the uppermost develops. A maize plant now bears 8 fruit objects and 2 peduncles where it previously bore 10 and 4.
- Maize main-stem node count raised from 17 to 20 and the phyllochron from 2 to 3 days per node. Corn Belt grain hybrids carry 19-21 main-stem leaves (range 16-23) and add a leaf collar roughly every 3 days over most of vegetative development; the previous values produced a 17-leaf plant that finished growing about twice as fast as a field crop. Tasseling now occurs near 60 days after emergence rather than 32, within the reported 54-63 day range. A default maize plant is correspondingly taller and takes longer to reach its final size, so simulations that advanced maize by a fixed number of days may now see a less mature plant than before.
- Maize `time_to_fruit_maturity` raised from 10 to 58 days. Grain fill from silking to physiological maturity takes 55-65 days in the field, so ears previously reached full size roughly five times too quickly. This value is the divisor controlling how fast the ear scales up, and it also sets the sink duration seen by the carbohydrate and nitrogen models.
- Fixed the maize mainstem ignoring `ShootParameters::internode_length_max`. `buildMaizePlant()` passed a hardcoded 0.08 m to `addBaseStemShoot()`, which takes precedence over the value stored on the shoot type, so the 0.22 m set in `initializeMaizeShoots()` was never used and editing it had no effect on the plant. The builder now forwards the shoot-type value, as the bougainvillea and capsicum builders already did. A default maize plant is now about 2.2 m tall rather than 1.4 m, within the 2-3 m range of a field hybrid.
- Sorghum phyllochron raised from 2 to 3.5 days per node and `time_to_fruit_maturity` from 15 to 35 days. A grain sorghum plant reaches half-bloom about 60 days after emergence and fills grain over roughly 35 days (Vanderlip, Kansas State S-3; Gerik et al., Texas A&M B-6137); the previous values completed the plant by day 30 and filled grain in 15. The main-stem node count of 16 is unchanged, being already within the 15-19 leaf range spanned by early to late maturity classes, as is plant height, which was already correct for a dwarfed grain hybrid.
- Fixed the sorghum mainstem ignoring `ShootParameters::internode_length_max`, the same defect as maize: `buildSorghumPlant()` passed a hardcoded 0.06 m to `addBaseStemShoot()` in preference to the stored value. The builder now forwards the shoot-type value. Note the stored value was simultaneously corrected from 0.26 m to the 0.06 m that was actually in force, so sorghum geometry is unchanged by this fix; the previously inert 0.26 m would have produced a roughly 5.5 m plant, far outside the 0.6-1.5 m range of a grain hybrid.
- Corrected the plant phenology table in the Plant Architecture documentation, which had the `time_to_fruit_maturity` values for "rice" and "sorghum" transposed (listing 15 and 10 where the library uses 10 and 15).
- `writePlantStructureXML()` now writes the plant's maximum age as a `<max_age>` tag and `readPlantStructureXML()` restores it. It was previously not persisted, so any plant written and read back silently reverted to the default of 999 days regardless of the value its builder or `setPlantMaxAge()` had set. The tag is optional on read, so files written by earlier versions still load.
- Fixed `duplicatePlantInstance()` producing a plant that differed from the one it copied. It rebuilt the shoot structure but carried over only the shoot-parameter snapshot, so the duplicate silently fell back to the `PlantInstance` defaults for everything not derivable from that structure: a copy of an apple tree reported its name as "custom", took a 999-day maximum age instead of 1460, and lost its phenological thresholds, epicormic-shoot probability, and carbohydrate and nitrogen parameters — so it entered dormancy, flowered and fruited on a different schedule from the original. All of these are now copied.
- Fixed `duplicatePlantInstance()` attaching child shoots at the wrong node. It passed the node at which the *parent* shoot joins the grandparent where the attachment node of the shoot being copied was required, so on any plant whose branches leave the trunk at different heights every branch was relocated onto a single wrong node and the copy's architecture did not match the original's.
- Fixed `duplicatePlantInstance()` returning a dormant copy of an actively-growing plant. Shoots are constructed dormant, and the duplicate's dormancy was never reconciled with its source, so the copy stalled until its dormancy broke while the plant it was copied from kept growing. Dormancy state and time since dormancy are now matched to the source.
- `duplicatePlantInstance()` now translates per-plant attraction points onto the duplicate's base position. They are absolute world coordinates, so the trellis of a trained plant (for example the apple fruiting wall) was previously not carried over at all; copying them unchanged would instead have steered the duplicate's growth toward the original plant's trellis.
- Removed `setPlantAge()`. Its body was commented out, so it silently did nothing when called, and it was documented as "Don't use this". `getPlantAge()` is unaffected.
- Fixed `disablePlantPhenology()` corrupting fruit geometry without bound. It set `dd_to_fruit_maturity` to `-1`, matching the flower and fruit-set stages beside it, but that field is not a "skip this stage" sentinel — it is used only as a divisor during fruit growth, so the scale factor became `0.25 - 0.75 * time_counter` and went negative within the first day. A debug build then aborted on the assertion in `Phytomer::setInflorescenceScaleFraction()`, while a release build mirror-scaled the fruit by a negative factor and compounded the error on every subsequent step: a sorghum panicle reached roughly 550 m², against a mature size of 0.16 m², after 100 days. It is now `1e6`, matching the `PlantInstance` default, so fruit simply never matures. The fruit-growth block in `advanceTime()` is additionally guarded against a non-positive `dd_to_fruit_maturity`, which `setPlantPhenologicalThresholds()` accepts and `readPlantStructureXML()` restores without validation. Only plants with a fruiting bud were affected, which requires a model defining a fruit prototype.
- Added `ShootParameters::inheritCustomFunctionsFrom()`, which copies the five user-defined function pointers held in a shoot type's phytomer parameters (the phytomer creation and callback hooks, and the leaf, flower, and fruit prototype functions) from another `ShootParameters`. `defineShootType()` and `updateCurrentShootParameters()` replace a shoot type entry in its entirety, which is harmless when the structure was copied but strips the species' customizations when it was reconstructed from plain values, as a scripting-language binding that serializes the parameters must do. For maize, committing such a reconstruction previously produced a tassel at nearly every node and no ears.
- Corrected the documentation example for modifying the parameters of all shoot types, which assigned the result of `getCurrentShootParameters(shoot_type_label)` to a `std::map` and so did not compile. It now calls the no-argument overload that returns the map.
- Fixed `ShootParameters::operator=` not copying `elongation_rate_max`. It was the only member missing from the assignment operator, so assigning a `ShootParameters` silently reset the shoot's maximum elongation rate to the default of 0.2, discarding the value set by the library or the user. Every library model sets a different value, so any path that assigned a `ShootParameters` — including reading a shoot type with `getCurrentShootParameters()` and writing it back with `updateCurrentShootParameters()` — lost it and the plant grew at the wrong rate.
- `defineShootType()` and `updateCurrentShootParameters()` now store the shoot-level parameter values they are given instead of re-drawing the random ones. `ShootParameters::operator=` resampled every distributed shoot-level parameter, while defining a shoot type under a label that did not yet exist did not, so the same input produced different stored values depending only on whether the label already existed, and reading a shoot type and writing it straight back silently perturbed parameters the caller never edited. Per-shoot variation is unaffected, as it comes from the growth engine resampling when each shoot is created. The phytomer parameters nested inside `ShootParameters` still resample on assignment, which is what varies phytomers along a shoot.
## LeafOptics
- `PROSPECT()` now validates its inputs, matching `getLeafSpectra()`. It is a spectrum-computing entry point in its own right but bypassed `validateProperties()` entirely, so calling it directly with a structure parameter of `numberlayers = 0` divided by zero when computing the mean absorption coefficient, and negative constituent contents contributed negative absorption — both returning non-finite or unphysical spectra with no diagnostic instead of the documented runtime error.
## Energy Balance
- Fixed primitive data `surface_humidity` being applied to the air vapor pressure instead of the surface vapor pressure in the latent heat flux. The term was computed as `e_s(T_s) - e_s(T_a)*h*f_s` rather than `e_s(T_s)*f_s - e_s(T_a)*h`, so lowering `surface_humidity` below 1 to represent a drying surface increased evaporation instead of suppressing it, and could flip the sign of `latent_flux`. Results are unchanged at the default `surface_humidity` of 1, which is why this went unnoticed; any simulation that set it below 1 had incorrect latent and sensible fluxes and surface temperatures. Both the CPU and GPU paths were affected.
- Fixed the reported `latent_flux` primitive data omitting `surface_humidity` entirely, so the value written out disagreed with the flux the energy balance actually solved whenever `surface_humidity` was not 1.
## Synthetic Annotation
- `render()` now writes bounding boxes in the YOLO annotation format, as a single file per view named after the rendered image, with every class in the one file and a `classes.txt` beside it. This replaces the previous `rectangular_labels_<label>.txt` files, which held one label per file in a format no training pipeline reads.
- `render()` now writes instance segmentation masks as a COCO JSON file per view (`instances.json`). This replaces the previous per-object `instance_segmentation_<label>_<object>.txt` pixel grids.
- Both new outputs are in the formats read by `Visualizer::displayImageWithBoundingBoxes()` and `Visualizer::displayImageWithSegmentationMasks()`, so annotations can be displayed over the rendered image directly, and match what the radiation plug-in writes for its cameras.
- The instance masks now describe each object's **visible** extent rather than its full un-occluded extent, which is the convention the COCO format uses. As a result the masks no longer require a separate rendering pass per object: rendering a scene with many labeled objects is substantially faster, and the cost no longer grows with the number of objects.
- Fixed the plug-in failing to configure unless the visualizer plug-in also happened to be listed in the project's `PLUGINS`. It declared a build dependency on `visualizer` without causing it to be loaded, so building with only `syntheticannotation` selected aborted at the CMake configure step with "The dependency target visualizer of target syntheticannotation does not exist". The visualizer is now loaded automatically when it is not already present.
- Fixed the `object_detection`, `semantic_segmentation` and `instance_segmentation` flags read from a loaded XML file being inverted in `render()`. The values were tested with `std::string::compare()` as though it returned a boolean, and it returns 0 on a match, so specifying "enabled" turned the output off and "disabled" turned it on. An unrecognized value is now a runtime error rather than being silently ignored.
- Fixed `render()` writing every object of a label group to the same instance segmentation file. The output filename omitted the object counter, so the field width intended for it padded the ".txt" extension instead and each object overwrote its predecessor, leaving only the last object of each label on disk.
- Fixed the label ID codes recovered from the rendered image being wrong, which made every annotation the plug-in produced meaningless. IDs are encoded as RGB colors and decoded back from the rendered pixels, but the visualizer brightened those colors by a factor of 1.5, so an object labeled 1 was recovered as 2 and IDs whose color channels exceeded 170 saturated and became indistinguishable from one another. `render()` now renders the ID pass with `Visualizer::enableExactColorMode()`. The semantic segmentation mask was the most visible symptom: no decoded ID ever matched a label group, so the mask came out uniformly blank.
- Fixed each per-object instance segmentation mask containing the *previous* object rather than its own. The ID pass was rendered to a window, and the windowed pixel readback picks between the front and back buffer by sampling nine pixels and taking whichever has more non-black content; against the white background used for the ID pass both score identically and the stale buffer wins. The ID pass now renders headless, to an offscreen framebuffer that is read back directly.
- Implemented `labelUnlabeledPrimitives()`, which was an empty stub that silently did nothing. Every primitive not already labeled is now assigned the given label as its own object, so primitives the user expected to be annotated are no longer omitted from the output.
- Implemented `addSkyDome()`, which was an empty stub. The sky dome texture used as the background of the RGB rendering is now configurable rather than hardcoded, and a path that does not exist is a runtime error rather than being silently ignored.
- Added `setMinimumLabelPixels()` to set how many pixels an object must cover to be written to the annotations, replacing a hardcoded value of 10 that could not be changed. Values below 3 are rejected, since an object covering fewer pixels has no traceable outline and would be dropped from the segmentation masks without a diagnostic.
- Added `disableMessages()` and `enableMessages()`. The plug-in printed progress messages unconditionally with no way to silence them, including two debug lines that printed a label name for every label on every view.
- Fixed `ID_mapping.txt` being written as a zero-byte file, which left the integer object IDs appearing in `pixelID_combined.txt` and the instance masks impossible to trace back to a label. It now contains one row per object giving its object ID, label, and class index. A `classes.txt` listing class names in class-index order is also written.
- Fixed the bounding-box annotations recording a class index of 0 for every object regardless of its label, which made the output unusable for training a multi-class detector. Each label now writes its own class index.
- Fixed `render()` leaving the Context modified when it threw. Primitive colors are overwritten with label ID codes during rendering and were only restored at the end of the function, so an error partway through left the caller's entire scene colored in ID codes. Colors are now restored on every exit path, and the internal `object_label` primitive data the plug-in adds is removed rather than left behind.
- Removed a hardcoded 5-second delay per camera view in `render()`. Rendering six views took 39 seconds, of which 30 were spent sleeping; the same run now takes 8 seconds and produces byte-identical images.
- Fixed the bounding-box annotations measuring `y_center` from the bottom of the image. The YOLO format requires the origin at the top-left, so every box was vertically mirrored with respect to the rendered image it describes; an object in the upper half of the frame was annotated as being in the lower half. `getGroupRectangularBBox()` now reports the box in top-down image coordinates.
- Fixed `pixelID_combined.txt` being written vertically mirrored relative to `RGB_rendering.jpeg` and the segmentation masks, so that a pixel at a given row in one file did not correspond to the same row in the others.
- Fixed the instance segmentation masks, where the bounding box on the first line was measured bottom-up while the mask below it was written top-down, so the two did not describe the same rows of the image.
- Fixed the first two values of each bounding-box annotation being written in a different numeric format from the last two, because the fixed-precision format was applied partway through the output expression.
- The header of `semantic_segmentation_ID_mapping.txt` is now `label class_ID`, matching the order of the columns beneath it. It previously read `Element Label`, which named the columns in the opposite order to the data.
- Fixed the pixel indexing used to write the semantic and instance segmentation masks in `render()`. The offset was computed such that the first row read addressed a full row past the end of the pixel buffer while the last row was never read at all, so the written masks were shifted by one row and their first row was filled from out-of-bounds memory.1 parent df16118 commit 01b2fe3
72 files changed
Lines changed: 8220 additions & 1719 deletions
File tree
- core
- include
- lib/json
- src
- tests
- doc
- images/validation
- tutorials
- plugins
- boundarylayerconductance/doc
- collisiondetection
- doc
- src
- energybalance
- src
- tests
- leafoptics
- doc
- src
- tests
- lidar
- doc
- include
- tests
- photosynthesis
- doc
- include
- plantarchitecture
- doc
- include
- src
- tests
- planthydraulics/doc
- radiation
- doc
- include
- src
- tests
- solarposition/doc
- stomatalconductance/doc
- syntheticannotation
- doc
- include
- src
- tests
- visualizer
- doc
- include
- shaders
- src
- tests
- weberpenntree/doc
- samples
- tutorial12
- tutorial14
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
13 | 13 | | |
14 | 14 | | |
15 | 15 | | |
| 16 | + | |
16 | 17 | | |
17 | 18 | | |
18 | 19 | | |
| |||
27 | 28 | | |
28 | 29 | | |
29 | 30 | | |
| 31 | + | |
30 | 32 | | |
31 | 33 | | |
32 | 34 | | |
| 35 | + | |
33 | 36 | | |
34 | 37 | | |
35 | 38 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1125 | 1125 | | |
1126 | 1126 | | |
1127 | 1127 | | |
1128 | | - | |
1129 | | - | |
1130 | | - | |
1131 | | - | |
1132 | | - | |
1133 | | - | |
1134 | | - | |
1135 | | - | |
1136 | 1128 | | |
1137 | 1129 | | |
1138 | 1130 | | |
| |||
File renamed without changes.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1801 | 1801 | | |
1802 | 1802 | | |
1803 | 1803 | | |
1804 | | - | |
| 1804 | + | |
1805 | 1805 | | |
1806 | 1806 | | |
1807 | 1807 | | |
| |||
1815 | 1815 | | |
1816 | 1816 | | |
1817 | 1817 | | |
1818 | | - | |
| 1818 | + | |
1819 | 1819 | | |
1820 | 1820 | | |
1821 | 1821 | | |
| |||
0 commit comments