Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support rendering more dtypes #8325

Open
wants to merge 60 commits into
base: master
Choose a base branch
from
Open

Support rendering more dtypes #8325

wants to merge 60 commits into from

Conversation

philippotto
Copy link
Member

@philippotto philippotto commented Jan 14, 2025

This PR adds rendering support for uint32 color layers, int8, int16, int32 for color + segmentation, and int64 for segmentation layers.

Histogram and thumbnail support were only done partially. The rest can happen in another PR.

URL of deployed dev instance (used for testing):

  • https://___.webknossos.xyz

Steps to test:

  • see 15 new datasets (dtype_test_*) on dev instance
  • monkey test them (also use histogram view, brushing and editable mappings)
  • no need to test all combinations, because there are automated screenshot tests for that

TODOs:

dtype color layers segmentation layers histogram thumbnail
uint8 unchanged unchanged unchanged unchanged
uint16 unchanged unchanged unchanged unchanged
uint24 rgb unchanged does not apply unchanged unchanged
uint32 ☑️ unchanged unchanged unchanged
uint64 unchanged unchanged unchanged (segmentation only)
int8 ☑️ ☑️ ☑️
int16 ☑️ ☑️ ☑️
int32 ☑️ ☑️ ☑️
int64 ☑️
float unchanged unchanged
double

Issues:


(Please delete unneeded items, merge only when none are left open)

@philippotto philippotto self-assigned this Jan 14, 2025
Copy link
Contributor

coderabbitai bot commented Jan 14, 2025

📝 Walkthrough

Walkthrough

This pull request introduces extensive modifications across both frontend and backend codebases. Highlights include a new linter rule to flag unused imports, updates to TypedArray types and method signatures, and refactoring of shader modules and texture handling logic. Several unused imports and redundant parameters have been removed throughout the code, and test suites have been revised to reflect these changes. Additionally, backend Scala services now better handle signed versus unsigned data. Overall, the changes enhance type safety, streamline functionality, and improve code clarity.

Changes

File(s) Change Summary
biome.json Added new linter rule "noUnusedImports": "error" to enforce import usage.
frontend/javascripts/libs/UpdatableTexture.ts
frontend/javascripts/oxalis/constants.ts
Updated TypedArray handling: imported TypedArray and expanded types (added BigInt64Array) for unified texture parameter types.
frontend/javascripts/oxalis/geometries/materials/plane_material_factory.ts
frontend/javascripts/oxalis/geometries/materials/plane_material_factory_helpers.ts
Refactored texture layer information and uniform updates; removed channelCount-based logic and simplified method signatures.
frontend/javascripts/libs/cuckoo/abstract_cuckoo_table.ts
frontend/javascripts/oxalis/model/bucket_data_handling/texture_bucket_manager.ts
Consolidated texture initialization and bucket data handling; removed redundant byte parameters and adjusted constructor signatures.
frontend/javascripts/libs/utils.ts Enhanced conversion functions to correctly apply the sign of numbers in base256 and 64-bit tuple conversions.
frontend/javascripts/messages.tsx Removed error message for unsupported segmentation format (int64).
frontend/javascripts/oxalis/api/cross_origin_api.ts
frontend/javascripts/oxalis/model/sagas/*
frontend/javascripts/oxalis/model/reducers/{skeletontracing_reducer.ts,ui_reducer.ts}
frontend/javascripts/oxalis/view/action-bar/toolbar_view.ts
Removed unused comments, imports (e.g. take, Predicate, ControlModeEnum, transformation helpers) and redundant effects from various sagas, reducers, and view files.
frontend/javascripts/oxalis/model.ts
frontend/javascripts/oxalis/model/accessors/dataset_accessor.ts
frontend/javascripts/oxalis/model_initialization.ts
Cleaned up imports and validation logic; delegated value range determination to a new helper function (getSupportedValueRangeForElementClass).
frontend/javascripts/oxalis/shaders/*.glsl.ts Updated shader modules by expanding the Params type (adding properties like isSigned, glslPrefix, etc.), switching uniform types from vec4 to uint, and adding new conversion and utility functions.
frontend/javascripts/oxalis/view/
frontend/javascripts/oxalis/view/left-border-tabs/
Revised UI components: adjusted click condition checks, removed obsolete enum imports, and introduced supportFractionals to handle fractional slider steps.
frontend/javascripts/test/** Updated test suites and Puppeteer scripts: removed obsolete parameters, standardized screenshot paths with new helpers (getUrlForScreenshotTests, SCREENSHOTS_BASE_PATH), and refactored function signatures across binary and screenshot tests.
frontend/javascripts/types/globals.d.ts
package.json
Enhanced global types by adding an apiReady property to webknossos and introduced a new dev dependency (semaphore-promise).
webknossos-datastore/app/com/scalableminds/webknossos/datastore/** Updated backend Scala services: unified handling of signed/unsigned element classes in AdHocMeshService, refactored conversion logic in DataConverter (renamed to toMaybeUnsigned), adjusted histogram calculations in FindDataService, and added an isSigned method to the ElementClass object.

Suggested labels

bug, frontend, testing

Poem

I'm a rabbit in a code-filled glen,
Hopping through changes again and again,
Linter rules and shaders make my ears perk high,
With tests refined, my code soars to the sky,
Every hop simplifies a tangled code pit—
Here's to clean commits and bytes that perfectly fit! 🐇💻


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@@ -593,12 +611,11 @@ export class DataBucket {

switch (this.state) {
case BucketStateEnum.REQUESTED: {
// Clone the data for the unmergedBucketDataLoaded event,
// as the following merge operation is done in-place.
const dataClone = new TypedArrayClass(data);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is unrelated to the actual PR. the cloning of data was always done even though it's only necessary before doing a merge.

tools/test.sh Outdated
@@ -109,7 +109,8 @@ then
elif [ $cmd == "test-screenshot" ]
then
ensureUpToDateTests
export NODE_PATH="$testBundlePath" && ava $(find "$testBundlePath" -name "*.screenshot.js") "$@"
# todop: revert
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

revert

const PAGE_WIDTH = 1920;
const PAGE_HEIGHT = 1080;

// todop: change to false before merging
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

todo

@philippotto philippotto changed the title [WIP] Support more dtypes Support rendering more dtypes Feb 6, 2025
@philippotto philippotto marked this pull request as ready for review February 6, 2025 12:05
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

🔭 Outside diff range comments (1)
frontend/javascripts/test/puppeteer/dataset_rendering_helpers.ts (1)

445-454: Fix incorrect usage of page.evaluate with browserstack_executor.

The current implementation of getBrowserstackSessionId incorrectly uses page.evaluate. The function should pass the BrowserStack command directly as a string to page.evaluate without the no-op function.

Apply this diff to correct the usage:

-  const response = (await page.evaluate(
-    (_) => {},
-    `browserstack_executor: ${JSON.stringify({ action: "getSessionDetails" })}`,
-  )) as unknown as string;

+  const response = (await page.evaluate(
+    `browserstack_executor: ${JSON.stringify({ action: "getSessionDetails" })}`
+  )) as string;
♻️ Duplicate comments (2)
frontend/javascripts/test/puppeteer/dataset_rendering_helpers.ts (1)

23-24: 🛠️ Refactor suggestion

Fix the typo in the comment.

There's a typo in the comment at line 23. It should be corrected for clarity.

Apply this diff to fix the typo:

-// todop: change to false before merging
+// TODO: change to false before merging
tools/test.sh (1)

112-113: ⚠️ Potential issue

Revert temporary test scope limitation.

This change was previously requested to be reverted. The narrowed test scope could miss potential regressions in other screenshot tests.

Apply this diff to restore the original behavior:

-  # todop: revert
-  export NODE_PATH="$testBundlePath" && ava $(find "$testBundlePath" -name "dtype_datasets_rendering.screenshot.js") "$@"
+  export NODE_PATH="$testBundlePath" && ava $(find "$testBundlePath" -name "*.screenshot.js") "$@"
🧰 Tools
🪛 Shellcheck (0.10.0)

[warning] 113-113: Quote this to prevent word splitting.

(SC2046)

🧹 Nitpick comments (14)
webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/DataConverter.scala (1)

50-58: LGTM! Consider adding documentation.

The changes to toMaybeUnsigned look good. The method name change and type adjustments better reflect its behavior of handling both signed and unsigned types, which aligns with the PR's goal of supporting more dtypes.

Consider adding ScalaDoc to document:

  • The method's purpose
  • Why some types are converted to unsigned while others remain signed
  • The relationship with the supported dtypes (uint32, int8, int16, int32, int64)
+  /**
+   * Converts numeric arrays to their unsigned counterparts where appropriate.
+   * Bytes are kept as-is to support int8, while other numeric types are converted
+   * to their unsigned versions to support uint32, int16, int32, and int64.
+   *
+   * @param data Array of numeric values
+   * @return Array with appropriate unsigned conversions
+   */
   def toMaybeUnsigned(data: Array[_ >: Byte with Short with Int with Long with Float])
frontend/javascripts/oxalis/geometries/materials/plane_material_factory_helpers.ts (1)

26-32: Consider enhancing the documentation with specific examples.

The implementation looks good and the comment provides a helpful reference. Consider adding examples of when manual internal format setting is needed, particularly for the new data types (uint32, int8, int16, int32, int64) that this PR introduces.

webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/FindDataService.scala (1)

160-163: Consider adding documentation and optimizing the index calculation.

While the implementation correctly handles signed bytes, consider these improvements:

  1. Add a comment explaining the +128 offset for handling signed bytes
  2. Consider using a constant for the offset value
 case byteData: Array[Byte] => {
+  // Add 128 to handle signed bytes (-128 to 127) in the histogram array indices (0 to 255)
+  private val SIGNED_BYTE_OFFSET = 128
-  byteData.foreach(el => counts(el.toInt + 128) += 1)
+  byteData.foreach(el => counts(el.toInt + SIGNED_BYTE_OFFSET) += 1)
   extrema = (-128, 128)
 }
frontend/javascripts/test/puppeteer/dtype_datasets_rendering.screenshot.ts (1)

23-27: Remove debugging flags before merging.

The USE_LOCAL_CHROME flag is set to false with a comment indicating a pending change. Ensure that all debugging flags and comments are appropriately set or removed before merging.

Apply this diff to address the issue:

-// todop: change to false before merging
 const USE_LOCAL_CHROME = false;
-// Only relevant when USE_LOCAL_CHROME. Set to false to actually see the browser open.
+// Only relevant when USE_LOCAL_CHROME is true. Set HEADLESS to false to see the browser open.
 const HEADLESS = true;
frontend/javascripts/test/puppeteer/screenshot_helpers.ts (1)

11-28: Consider adding URL validation.

While the function handles the https:// prefix, it might be good to validate the URL format to prevent invalid URLs.

 export function getUrlForScreenshotTests() {
   let URL = "https://master.webknossos.xyz/";
 
   if (!process.env.URL) {
     console.warn(
       "[Warning] No url specified, assuming dev master. If you want to specify a URL, prepend URL=<url> to the command.",
     );
   } else {
     URL = process.env.URL;
 
     // Prepend https:// if not specified
     if (!/^https?:\/\//i.test(URL)) {
       URL = `https://${URL}`;
     }
+    // Validate URL format
+    try {
+      new URL(URL);
+    } catch (e) {
+      throw new Error(`Invalid URL format: ${URL}`);
+    }
   }
 
   return URL;
 }
frontend/javascripts/oxalis/model/helpers/shader_editor.ts (3)

86-86: Set Ace Editor mode to 'glsl' for accurate syntax highlighting

Currently, the Ace Editor mode is set to 'c_cpp', which may not provide optimal syntax highlighting for GLSL shader code. Changing the mode to 'glsl' will enhance the editing experience with appropriate syntax highlighting.

Apply this diff to set the mode to 'glsl':

-  mode: "c_cpp",
+  mode: "glsl",

118-125: Use CSS classes instead of inline styles for 'buttonContainer'

Setting styles directly via setAttribute with inline CSS can make the code harder to maintain and read. Consider assigning a CSS class to the buttonContainer element and defining styles in a stylesheet to improve readability and promote reusability.


141-148: Use CSS classes instead of inline styles for 'outerContainer'

Applying styles using setAttribute and inline styles can lead to code that is difficult to maintain. Defining styles in a CSS file and applying them via classes makes the code cleaner and more maintainable.

frontend/javascripts/oxalis/shaders/segmentation.glsl.ts (2)

99-207: Optimize 'convertCellIdToRGB' to avoid unnecessary conversions

In convertCellIdToRGB, idHigh_uint and idLow_uint are converted back to vec4s using uintToVec4, only to perform operations on them. Since you're working with uints, consider rewriting the function to operate directly on uints and avoid unnecessary conversions to improve performance.


18-79: Add comments to explain bit manipulation logic

Functions involving bit manipulation, such as vec4ToUint, vec4ToInt, and the various *ToUint64 functions, can be difficult to understand. Adding detailed comments explaining each step can improve readability and help future maintainers understand the code.

frontend/javascripts/oxalis/shaders/main_data_shaders.glsl.ts (1)

291-323: Refactor uint32 aggregation to use bitwise operations

In the uint32 case, current implementation uses floating-point calculations with pow(256., n), which may introduce precision issues. Refactoring to use bitwise shifts enhances performance and accuracy.

Apply this diff to refactor the uint32 value aggregation:

               // Combine bytes into a uint32 (assuming little-endian order)
-              // Scale from [0,1] to [0,255] so that we can convert to an uint
-              // below.
-              uvec4 four_bytes = uvec4(255. * maybe_filtered_color.color);
-              highp uint hpv =
-                uint(four_bytes.a) * uint(pow(256., 3.))
-                + uint(four_bytes.b) * uint(pow(256., 2.))
-                + uint(four_bytes.g) * 256u
-                + uint(four_bytes.r);
+              uvec4 four_bytes = uvec4(255. * maybe_filtered_color.color);
+              highp uint hpv = four_bytes.r | (four_bytes.g << 8) | (four_bytes.b << 16) | (four_bytes.a << 24);

               uint typed_min = <%= name %>_min;
               uint typed_max = <%= name %>_max;
               hpv = clamp(hpv, typed_min, typed_max);
               color_value = vec3(
-                float(hpv - typed_min) / (float(typed_max - typed_min) + is_max_and_min_equal)
+                scaleUintToFloat(hpv, typed_min, typed_max)
               );

This change avoids potential floating-point precision errors and aligns the approach with the int32 case.

frontend/javascripts/oxalis/model/bucket_data_handling/texture_bucket_manager.ts (1)

51-71: Improve type safety in maybePadRgbData.

The function uses @ts-ignore comments with a brief explanation about BigInt. Consider adding runtime type checks or using a more specific type than TypedArray to avoid potential issues.

Consider this safer implementation:

-function maybePadRgbData(src: TypedArray, elementClass: ElementClass) {
+function maybePadRgbData(src: Uint8Array | Uint16Array | Uint32Array, elementClass: ElementClass) {
   if (elementClass !== "uint24") {
     return src;
   }
 
   let idx = 0;
   let srcIdx = 0;
   while (srcIdx < 3 * constants.BUCKET_SIZE) {
-    // @ts-ignore BigInt is not a problem as this code here only handles uint24 data
     tmpPaddingBuffer[idx++] = src[srcIdx++];
-    // @ts-ignore BigInt is not a problem as this code here only handles uint24 data
     tmpPaddingBuffer[idx++] = src[srcIdx++];
-    // @ts-ignore BigInt is not a problem as this code here only handles uint24 data
     tmpPaddingBuffer[idx++] = src[srcIdx++];
     tmpPaddingBuffer[idx++] = 255;
   }
 
   return tmpPaddingBuffer;
 }
frontend/javascripts/oxalis/shaders/utils.glsl.ts (1)

383-394: Consider using a more robust approach for large interval handling.

The current approach for handling large intervals uses a fixed multiplier (0.25) which might still lead to precision issues for extremely large intervals.

Consider using logarithmic scaling or interval subdivision for more robust handling of large ranges:

-      float mul = 0.25;
-      float nom = mul * x - mul * a;
-      float denom = mul * b - mul * a;
-      return nom / denom;
+      // Use log scale for extremely large intervals
+      float logScale = log(abs(b - a));
+      float scaledX = x / exp(logScale);
+      float scaledA = a / exp(logScale);
+      float scaledB = b / exp(logScale);
+      return (scaledX - scaledA) / (scaledB - scaledA);
frontend/javascripts/oxalis/shaders/texture_access.glsl.ts (1)

43-71: LGTM! The texture access logic is correctly updated.

The changes correctly handle different texture layer counts and element classes, particularly for int16 types. The normalization logic is well-structured and maintainable.

Consider extracting the normalization logic into a separate function for better reusability and maintainability:

+function getNormalizationFactor(textureLayerInfo) {
+  if (textureLayerInfo.isColor && !elementClass.endsWith("int8")) {
+    return 1;
+  } else if (
+    textureLayerInfo.isSigned && !elementClass.endsWith("int32") && !elementClass.endsWith("int64")
+  ) {
+    return 127;
+  } else {
+    return 255;
+  }
+}

-float dtype_normalizer = <%=
-  formatNumberAsGLSLFloat((() => {
-    if (textureLayerInfo.isColor && !elementClass.endsWith("int8")) {
-      return 1;
-    } else if (
-      textureLayerInfo.isSigned && !elementClass.endsWith("int32") && !elementClass.endsWith("int64")
-    ) {
-      return 127;
-    } else {
-      return 255;
-    }
-  })())
-%>;
+float dtype_normalizer = <%= formatNumberAsGLSLFloat(getNormalizationFactor(textureLayerInfo)) %>;
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f1035db and 13b70a4.

⛔ Files ignored due to path filters (39)
  • frontend/javascripts/test/screenshots/dtype_float32_color_zoomed_in.png is excluded by !**/*.png
  • frontend/javascripts/test/screenshots/dtype_float32_color_zoomed_out.png is excluded by !**/*.png
  • frontend/javascripts/test/screenshots/dtype_int16_color_zoomed_in.png is excluded by !**/*.png
  • frontend/javascripts/test/screenshots/dtype_int16_color_zoomed_out.png is excluded by !**/*.png
  • frontend/javascripts/test/screenshots/dtype_int16_segmentation_zoomed_in.png is excluded by !**/*.png
  • frontend/javascripts/test/screenshots/dtype_int16_segmentation_zoomed_in_selective_segment.png is excluded by !**/*.png
  • frontend/javascripts/test/screenshots/dtype_int16_segmentation_zoomed_out.png is excluded by !**/*.png
  • frontend/javascripts/test/screenshots/dtype_int32_color_zoomed_in.png is excluded by !**/*.png
  • frontend/javascripts/test/screenshots/dtype_int32_color_zoomed_out.png is excluded by !**/*.png
  • frontend/javascripts/test/screenshots/dtype_int32_segmentation_zoomed_in.png is excluded by !**/*.png
  • frontend/javascripts/test/screenshots/dtype_int32_segmentation_zoomed_in_selective_segment.png is excluded by !**/*.png
  • frontend/javascripts/test/screenshots/dtype_int32_segmentation_zoomed_out.png is excluded by !**/*.png
  • frontend/javascripts/test/screenshots/dtype_int64_segmentation_zoomed_in.png is excluded by !**/*.png
  • frontend/javascripts/test/screenshots/dtype_int64_segmentation_zoomed_in_selective_segment.png is excluded by !**/*.png
  • frontend/javascripts/test/screenshots/dtype_int64_segmentation_zoomed_out.png is excluded by !**/*.png
  • frontend/javascripts/test/screenshots/dtype_int8_color_zoomed_in.png is excluded by !**/*.png
  • frontend/javascripts/test/screenshots/dtype_int8_color_zoomed_out.png is excluded by !**/*.png
  • frontend/javascripts/test/screenshots/dtype_int8_segmentation_zoomed_in.png is excluded by !**/*.png
  • frontend/javascripts/test/screenshots/dtype_int8_segmentation_zoomed_in_selective_segment.png is excluded by !**/*.png
  • frontend/javascripts/test/screenshots/dtype_int8_segmentation_zoomed_out.png is excluded by !**/*.png
  • frontend/javascripts/test/screenshots/dtype_uint16_color_zoomed_in.png is excluded by !**/*.png
  • frontend/javascripts/test/screenshots/dtype_uint16_color_zoomed_out.png is excluded by !**/*.png
  • frontend/javascripts/test/screenshots/dtype_uint16_segmentation_zoomed_in.png is excluded by !**/*.png
  • frontend/javascripts/test/screenshots/dtype_uint16_segmentation_zoomed_in_selective_segment.png is excluded by !**/*.png
  • frontend/javascripts/test/screenshots/dtype_uint16_segmentation_zoomed_out.png is excluded by !**/*.png
  • frontend/javascripts/test/screenshots/dtype_uint32_color_zoomed_in.png is excluded by !**/*.png
  • frontend/javascripts/test/screenshots/dtype_uint32_color_zoomed_out.png is excluded by !**/*.png
  • frontend/javascripts/test/screenshots/dtype_uint32_segmentation_zoomed_in.png is excluded by !**/*.png
  • frontend/javascripts/test/screenshots/dtype_uint32_segmentation_zoomed_in_selective_segment.png is excluded by !**/*.png
  • frontend/javascripts/test/screenshots/dtype_uint32_segmentation_zoomed_out.png is excluded by !**/*.png
  • frontend/javascripts/test/screenshots/dtype_uint64_segmentation_zoomed_in.png is excluded by !**/*.png
  • frontend/javascripts/test/screenshots/dtype_uint64_segmentation_zoomed_in_selective_segment.png is excluded by !**/*.png
  • frontend/javascripts/test/screenshots/dtype_uint64_segmentation_zoomed_out.png is excluded by !**/*.png
  • frontend/javascripts/test/screenshots/dtype_uint8_color_zoomed_in.png is excluded by !**/*.png
  • frontend/javascripts/test/screenshots/dtype_uint8_color_zoomed_out.png is excluded by !**/*.png
  • frontend/javascripts/test/screenshots/dtype_uint8_segmentation_zoomed_in.png is excluded by !**/*.png
  • frontend/javascripts/test/screenshots/dtype_uint8_segmentation_zoomed_in_selective_segment.png is excluded by !**/*.png
  • frontend/javascripts/test/screenshots/dtype_uint8_segmentation_zoomed_out.png is excluded by !**/*.png
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (45)
  • biome.json (1 hunks)
  • frontend/javascripts/libs/UpdatableTexture.ts (2 hunks)
  • frontend/javascripts/libs/cuckoo/abstract_cuckoo_table.ts (1 hunks)
  • frontend/javascripts/libs/utils.ts (3 hunks)
  • frontend/javascripts/messages.tsx (0 hunks)
  • frontend/javascripts/oxalis/api/cross_origin_api.ts (0 hunks)
  • frontend/javascripts/oxalis/constants.ts (1 hunks)
  • frontend/javascripts/oxalis/geometries/materials/plane_material_factory.ts (7 hunks)
  • frontend/javascripts/oxalis/geometries/materials/plane_material_factory_helpers.ts (2 hunks)
  • frontend/javascripts/oxalis/model.ts (0 hunks)
  • frontend/javascripts/oxalis/model/accessors/dataset_accessor.ts (4 hunks)
  • frontend/javascripts/oxalis/model/bucket_data_handling/bucket.ts (4 hunks)
  • frontend/javascripts/oxalis/model/bucket_data_handling/data_rendering_logic.tsx (5 hunks)
  • frontend/javascripts/oxalis/model/bucket_data_handling/layer_rendering_manager.ts (1 hunks)
  • frontend/javascripts/oxalis/model/bucket_data_handling/texture_bucket_manager.ts (6 hunks)
  • frontend/javascripts/oxalis/model/helpers/shader_editor.ts (1 hunks)
  • frontend/javascripts/oxalis/model/reducers/skeletontracing_reducer.ts (0 hunks)
  • frontend/javascripts/oxalis/model/reducers/ui_reducer.ts (1 hunks)
  • frontend/javascripts/oxalis/model/sagas/proofread_saga.ts (1 hunks)
  • frontend/javascripts/oxalis/model/sagas/quick_select_heuristic_saga.ts (1 hunks)
  • frontend/javascripts/oxalis/model/sagas/saga_helpers.ts (1 hunks)
  • frontend/javascripts/oxalis/model/sagas/volume/floodfill_saga.tsx (1 hunks)
  • frontend/javascripts/oxalis/model_initialization.ts (0 hunks)
  • frontend/javascripts/oxalis/shaders/main_data_shaders.glsl.ts (13 hunks)
  • frontend/javascripts/oxalis/shaders/segmentation.glsl.ts (7 hunks)
  • frontend/javascripts/oxalis/shaders/texture_access.glsl.ts (3 hunks)
  • frontend/javascripts/oxalis/shaders/utils.glsl.ts (3 hunks)
  • frontend/javascripts/oxalis/view/action-bar/toolbar_view.tsx (0 hunks)
  • frontend/javascripts/oxalis/view/context_menu.tsx (1 hunks)
  • frontend/javascripts/oxalis/view/left-border-tabs/histogram_view.tsx (4 hunks)
  • frontend/javascripts/oxalis/view/left-border-tabs/layer_settings_tab.tsx (1 hunks)
  • frontend/javascripts/test/model/binary/data_rendering_logic.spec.ts (0 hunks)
  • frontend/javascripts/test/model/texture_bucket_manager.spec.ts (2 hunks)
  • frontend/javascripts/test/puppeteer/dataset_rendering.screenshot.ts (8 hunks)
  • frontend/javascripts/test/puppeteer/dataset_rendering.wkorg_screenshot.ts (2 hunks)
  • frontend/javascripts/test/puppeteer/dataset_rendering_helpers.ts (13 hunks)
  • frontend/javascripts/test/puppeteer/dtype_datasets_rendering.screenshot.ts (1 hunks)
  • frontend/javascripts/test/puppeteer/screenshot_helpers.ts (4 hunks)
  • frontend/javascripts/test/shaders/shader_syntax.spec.ts (6 hunks)
  • frontend/javascripts/types/globals.d.ts (1 hunks)
  • package.json (1 hunks)
  • tools/test.sh (1 hunks)
  • webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/AdHocMeshService.scala (1 hunks)
  • webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/DataConverter.scala (1 hunks)
  • webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/FindDataService.scala (3 hunks)
💤 Files with no reviewable changes (7)
  • frontend/javascripts/oxalis/model.ts
  • frontend/javascripts/oxalis/api/cross_origin_api.ts
  • frontend/javascripts/oxalis/view/action-bar/toolbar_view.tsx
  • frontend/javascripts/messages.tsx
  • frontend/javascripts/oxalis/model/reducers/skeletontracing_reducer.ts
  • frontend/javascripts/oxalis/model_initialization.ts
  • frontend/javascripts/test/model/binary/data_rendering_logic.spec.ts
✅ Files skipped from review due to trivial changes (2)
  • frontend/javascripts/oxalis/model/sagas/saga_helpers.ts
  • frontend/javascripts/oxalis/model/reducers/ui_reducer.ts
🧰 Additional context used
📓 Learnings (1)
frontend/javascripts/oxalis/geometries/materials/plane_material_factory.ts (1)
Learnt from: dieknolle3333
PR: scalableminds/webknossos#8168
File: frontend/javascripts/oxalis/geometries/materials/plane_material_factory.ts:568-585
Timestamp: 2024-11-22T17:18:43.411Z
Learning: In the file `frontend/javascripts/oxalis/geometries/materials/plane_material_factory.ts`, the uniform name `allResolutions` should remain unchanged to maintain consistency with the shader code.
🪛 Shellcheck (0.10.0)
tools/test.sh

[warning] 113-113: Quote this to prevent word splitting.

(SC2046)

🔇 Additional comments (50)
frontend/javascripts/oxalis/geometries/materials/plane_material_factory_helpers.ts (2)

5-12: LGTM! Good improvement in the function signature.

The change from deriving format based on channel count to explicit format specification is a good practice. It makes the texture format handling more flexible and reduces potential runtime errors.


5-35: Verify that all callers are updated with the new parameters.

Let's ensure all callers of createUpdatableTexture are updated to provide the required format parameter.

✅ Verification successful

🏁 Script executed:

#!/bin/bash
# Search for all calls to createUpdatableTexture
ast-grep --pattern 'createUpdatableTexture($$$)'

Length of output: 2605


Callers of createUpdatableTexture are updated correctly.

  • In frontend/javascripts/libs/cuckoo/abstract_cuckoo_table.ts, one call provides all six parameters (including the optional internal format) while another intentionally relies on the optional parameter.
  • In frontend/javascripts/oxalis/model/bucket_data_handling/texture_bucket_manager.ts, the full parameter set is provided.

All usages align with the updated signature.

webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/FindDataService.scala (3)

142-142: LGTM: Method signature updated to support int8 data type.

The addition of Byte to the union type aligns with the PR objective to support int8 data type for color and segmentation layers.


166-167: LGTM: Variable name change improves clarity.

The rename from intData to uintData better reflects the unsigned nature of the data being handled.


193-194: Verify the behavior of toMaybeUnsigned for all supported data types.

The change from toUnsigned to toMaybeUnsigned suggests a more flexible conversion approach. Please ensure that:

  1. The conversion works correctly for both signed and unsigned types
  2. The behavior is consistent across all supported data types (uint32, int8, int16, int32, int64)
✅ Verification successful

🏁 Script executed:

#!/bin/bash
# Search for tests covering toMaybeUnsigned conversion
rg -l "toMaybeUnsigned.*test" .

# Search for all usages of toMaybeUnsigned
ast-grep --pattern 'toMaybeUnsigned($_)'

Length of output: 667


Let's search for the definition of toMaybeUnsigned to review its implementation and verify its support for both signed and unsigned types.


🏁 Script executed:

#!/bin/bash
# Locate the definition of toMaybeUnsigned in the repository
rg -n "def toMaybeUnsigned" .

Length of output: 216


🏁 Script executed:

#!/bin/bash
# Extract the implementation of toMaybeUnsigned along with surrounding context from DataConverter.scala
sed -n '30,150p' webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/DataConverter.scala

Length of output: 3755


Conversion behavior verified: The implementation of toMaybeUnsigned correctly handles all supported data types. Specifically:

  • Byte arrays: Returned as-is, which is appropriate since they represent int8.
  • Short arrays: Each element is converted to UShort, supporting int16 unsigned conversion.
  • Int arrays: Each element is converted to UInt, matching the expected treatment for int32.
  • Long arrays: Each element is converted to ULong, aligning with int64 unsigned conversion.
  • Float arrays: Returned as-is because there is no unsigned float type.

The method’s behavior is consistent with the conversion requirements for both signed and unsigned types.

frontend/javascripts/test/puppeteer/dataset_rendering.wkorg_screenshot.ts (1)

19-22: Approved: Variable renaming enhances clarity.

Renaming BASE_PATH to SCREENSHOTS_BASE_PATH improves code readability and accurately reflects its purpose.

frontend/javascripts/test/puppeteer/screenshot_helpers.ts (3)

6-9: LGTM! Path construction for screenshots base path.

The constant is well-defined using path.join for cross-platform compatibility.


55-67: LGTM! Robust error handling in deleteScreenshotIfExists.

The function properly handles file deletion with Promise-based fs operations and silently catches errors.


105-109: LGTM! Enhanced screenshot comparison with dimension checks.

The added dimension validation prevents misleading comparisons of differently sized screenshots.

frontend/javascripts/test/puppeteer/dataset_rendering.screenshot.ts (3)

4-9: LGTM! Well-organized imports.

The imports are properly grouped and named exports are used consistently.


35-35: LGTM! Simplified URL initialization.

Using the new helper function improves code maintainability and consistency.


136-139: LGTM! Improved parameter organization.

Grouping related parameters into an options object enhances readability and maintainability.

frontend/javascripts/oxalis/shaders/main_data_shaders.glsl.ts (8)

9-9: Importing ElementClass

The addition of ElementClass import from "types/api_flow_types" is appropriate for defining type information used later in the code.


34-34: Including utility functions in imports

The inclusion of glslTypeForElementClass and scaleToFloat enhances the shader utility functions, ensuring correct type handling and value scaling.

Also applies to: 38-38


42-42: Exporting Params type

Exporting the Params type promotes reusability, allowing it to be imported and utilized in other modules.


47-57: Extending textureLayerInfos structure

The expansion of textureLayerInfos with properties like isSigned, glslPrefix, elementClass, unsanitizedName, and isColor is essential for supporting additional data types and enhancing texture layer information.


93-94: Dynamic GLSL uniform types for min and max values

Using glslTypeForElementClass to determine the GLSL type for <%= name %>_min and <%= name %>_max ensures that shaders handle various data types correctly.


106-107: Updating uniform declarations to uint

Changing uniforms such as activeCellIdHigh, activeCellIdLow, hoveredSegmentIdLow, hoveredSegmentIdHigh, hoveredUnmappedSegmentIdLow, and hoveredUnmappedSegmentIdHigh to uint improves type safety when dealing with segmentation IDs.

Also applies to: 144-147


199-199: Including scaleToFloat and glslTypeForElementClass in shader compilation

Adding scaleToFloat and glslTypeForElementClass to the compiled shader functions ensures proper scaling and type handling within the shader logic.

Also applies to: 411-411


217-255: Handling high and low components of IDs

Implementing high and low components for IDs enhances support for 64-bit segmentation IDs in shaders. The use of appropriate conversion functions based on elementClass ensures accurate ID processing.

frontend/javascripts/types/globals.d.ts (2)

1-1: Importing ApiType

The addition of import { ApiType } from "oxalis/api/api_loader" allows access to the type definitions of the API, ensuring type safety.


9-9: Extending Window.webknossos with apiReady

Adding apiReady: ApiType["apiReady"] to the webknossos object enhances the global Window interface with the apiReady property, providing better type definitions and ensuring that the API readiness state is correctly typed.

frontend/javascripts/test/model/texture_bucket_manager.spec.ts (3)

63-64: Updating TextureBucketManager constructor parameters

The removal of the fourth parameter in new TextureBucketManager(2048, 1, "uint8") and adjusting the parameters accordingly reflects changes in the TextureBucketManager constructor. Ensure that the constructor signature matches the updated implementation.


64-64: Updating setupDataTextures method call

The removal of the first parameter in tbm.setupDataTextures(new CuckooTableVec5(CUCKOO_TEXTURE_WIDTH), LAYER_INDEX); aligns with changes to the method's signature. Confirm that setupDataTextures no longer requires the first parameter and functions as expected.


77-78: Adjusting constructor and method calls for TextureBucketManager

Consistently updating the constructor call to new TextureBucketManager(2048, 2, "uint8") and corresponding setupDataTextures method aligns with the refactored TextureBucketManager. This ensures that the test cases are compatible with the updated class definition.

frontend/javascripts/libs/UpdatableTexture.ts (1)

1-1: LGTM! The changes improve type flexibility.

The switch to TypedArray type enhances flexibility and aligns with the PR's objective to support more data types.

Also applies to: 79-79

frontend/javascripts/oxalis/model/bucket_data_handling/layer_rendering_manager.ts (1)

153-165: LGTM! The changes simplify texture setup.

The removal of the bytes parameter and reliance on elementClass for type information improves code maintainability.

frontend/javascripts/oxalis/model/bucket_data_handling/texture_bucket_manager.ts (2)

96-106: LGTM! Constructor simplification improves code clarity.

The removal of the bytes parameter and use of getDtypeConfigForElementClass for determining packingDegree improves code maintainability.


261-276: LGTM! Improved texture setup with better type handling.

The changes to setupDataTextures improve type safety by using getDtypeConfigForElementClass to determine texture properties.

frontend/javascripts/libs/cuckoo/abstract_cuckoo_table.ts (1)

50-57: LGTM! Good refactoring of texture initialization.

The changes improve code maintainability by consolidating the internal format handling into the texture creation call.

frontend/javascripts/oxalis/model/sagas/volume/floodfill_saga.tsx (1)

30-30: LGTM! Clean removal of unused import.

The removal of the unused take import improves code cleanliness.

frontend/javascripts/oxalis/view/left-border-tabs/histogram_view.tsx (1)

24-24: LGTM! Good enhancement of histogram flexibility.

The addition of supportFractionals and maybeCeilFn provides a clean way to handle both fractional and integer-only cases.

Also applies to: 278-278, 290-290

frontend/javascripts/oxalis/shaders/utils.glsl.ts (1)

358-396: LGTM! Well-implemented scaling functions with good edge case handling.

The implementation includes:

  • Safe type conversions using uint
  • Edge case handling for zero range
  • Clear comments explaining the logic
frontend/javascripts/oxalis/constants.ts (1)

352-365: LGTM! The type definitions are correctly updated to support int64.

The changes correctly add BigInt64Array to the TypedArray type and update TypedArrayWithoutBigInt to exclude both BigUint64Array and BigInt64Array. This aligns with the PR objectives to support more data types.

Let's verify that these changes are consistent with the TypeScript documentation:

✅ Verification successful

🌐 Web query:

What is the difference between BigInt64Array and BigUint64Array in TypeScript?

💡 Result:

The `BigInt64Array` and `BigUint64Array` in TypeScript/JavaScript both handle 64-bit integer data but differ in signedness and value ranges:

## Key Differences

| Feature               | **BigInt64Array**                          | **BigUint64Array**                         |
|-----------------------|--------------------------------------------|--------------------------------------------|
| **Signedness**        | Signed integers (positive/negative values)| Unsigned integers (non-negative values)    |
| **Value Range**       | -9,223,372,036,854,775,808 to<br>9,223,372,036,854,775,807 | 0 to<br>18,446,744,073,709,551,615 |
| **Use Case**          | Applications requiring negative numbers    | Scenarios needing large non-negative values|

Both inherit from `TypedArray` and share these properties:
- Store elements as `bigint` values (e.g., `42n`)
- Fixed size: `BYTES_PER_ELEMENT = 8`
- Initialized with `0n` by default
- Support identical construction methods:
  ```typescript
  // Common initialization patterns
  new BigInt64Array(2); // Signed
  new BigUint64Array([1n, 2n]); // Unsigned
  ```

## Example Behavior
```typescript
// Signed array allows negative values
const signedArr = new BigInt64Array([-100n]);
console.log(signedArr[0]); // -100n [1][9]

// Unsigned array rejects negatives
const unsignedArr = new BigUint64Array(1);
unsignedArr[0] = -100n; // Throws RangeError [5][7]
```

## When to Use
- **BigInt64Array**: Financial calculations requiring debt/credit distinctions or systems using signed 64-bit IDs (e.g., some databases)[9].
- **BigUint64Array**: Cryptographic hashes, IPv6 addresses, or hardware registers using unsigned values[4][10].

These arrays enable precise handling of large integers while maintaining platform byte order compatibility[1][5]. For byte-order control or mixed-type operations, use `DataView` instead[1][5].

Citations:

- 1: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt64Array
- 2: https://udn.realityripple.com/docs/Web/JavaScript/Reference/Global_Objects/BigInt64Array
- 3: https://udn.realityripple.com/docs/Web/JavaScript/Reference/Global_Objects/BigUint64Array/BigUint64Array
- 4: https://dev.to/dhrn/day26-typed-arrays-5522
- 5: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigUint64Array
- 6: https://www.npmjs.com/package/bigint-conversion
- 7: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigUint64Array/BigUint64Array
- 8: https://www.typescriptlang.org/docs/handbook/basic-types.html
- 9: https://v8.dev/features/bigint
- 10: https://discourse.threejs.org/t/bufferattribute-is-it-possible-to-create-a-bufferattribute-from-a-biguint64array/25093

Verification of Updated TypedArray Definitions

The changes correctly include both BigUint64Array and BigInt64Array in the TypedArray union and update TypedArrayWithoutBigInt to exclude them. The web query confirms that the key difference is in their signedness, which aligns with the PR's objective to support 64-bit arrays.

  • The updated types are consistent with the TypeScript documentation regarding the handling of large integers.
frontend/javascripts/oxalis/model/accessors/dataset_accessor.ts (2)

197-203: LGTM! The value range determination is correctly delegated.

The function now correctly delegates the value range determination to getSupportedValueRangeForElementClass, which improves maintainability by centralizing this logic.


378-396: LGTM! The element class support is correctly updated.

The changes correctly limit uint64 and int64 support to segmentation layers only, with a clear comment about the JavaScript Number type limitation.

Let's verify the JavaScript Number type limitation mentioned in the comment:

✅ Verification successful

🌐 Web query:

What is the maximum safe integer in JavaScript?

💡 Result:

The maximum safe integer in JavaScript is **9,007,199,254,740,991**, represented by the `Number.MAX_SAFE_INTEGER` constant[1][2][5]. This value equals \(2^{53} - 1\), derived from JavaScript’s use of 64-bit double-precision floating-point numbers (IEEE 754 standard)[2][5][6].

### Key Details
- **Definition**: A "safe integer" is one that can be:
  - Exactly represented in IEEE 754 format
  - Correctly compared with other integers (e.g., `x + 1 ≠ x`)[5][6]
- **Limitations**:
  - Beyond this range (`±9,007,199,254,740,991`), arithmetic operations lose precision:
    ```javascript
    console.log(Number.MAX_SAFE_INTEGER + 1 === Number.MAX_SAFE_INTEGER + 2); // true[3][6]
    ```
  - Bitwise/shift operators use 32-bit integers (max safe: \(2^{31} - 1\))[2].
- **Verification**:
  Use `Number.isSafeInteger()` to check validity:
  ```javascript
  console.log(Number.isSafeInteger(9007199254740991)); // true
  console.log(Number.isSafeInteger(9007199254740992)); // false[4][6]
  ```

### Practical Considerations
- For integers exceeding this range:
  - Use `BigInt` type (e.g., `12345678901234567890n`)[3][5].
- Related constants:
  - `Number.MIN_SAFE_INTEGER`: \(-9,007,199,254,740,991\)[2][4]
  - `Number.MAX_VALUE`: Largest representable number (~\(1.79 \times 10^{308}\))[2][4].

This limit ensures reliable integer operations in most applications while guiding developers to specialized types like `BigInt` for extreme values[3][5].

Citations:

- 1: https://www.w3schools.com/jsref/jsref_max_safe_integer.asp
- 2: https://www.tutorialspoint.com/what-is-javascript-s-highest-integer-value-that-a-number-can-go-to-without-losing-precision
- 3: https://www.programiz.com/javascript/library/number/max_safe_integer
- 4: https://community.latenode.com/t/what-is-the-maximum-integer-javascript-can-accurately-represent/611
- 5: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER?retiredLocale=vi
- 6: https://forum.freecodecamp.org/t/explanation-of-safe-integers/322832

Element class support and JS Number type limitation verified.
The maximum safe integer in JavaScript is indeed 2⁵³ – 1 (9007199254740991), which confirms that the adjustment in the code—restricting uint64 and int64 support to segmentation layers only (due to potential precision issues with numbers beyond this threshold)—is correctly implemented.

frontend/javascripts/oxalis/model/bucket_data_handling/bucket.ts (3)

28-39: LGTM! The type definitions are correctly updated.

The BucketDataArray type is correctly updated to include Int8Array, Int16Array, Int32Array, and BigInt64Array, with a clear comment about adapting the type for new element classes.


79-88: LGTM! The constructor types are correctly updated.

The TypedArrayConstructor type is correctly updated to include the corresponding constructors for the new array types.


614-619: LGTM! The data cloning is correctly handled.

The changes correctly clone the data before triggering the unmergedBucketDataLoaded event, as the subsequent merge operation is done in-place.

Let's verify that the cloning is necessary by checking for any references to this event:

✅ Verification successful

🏁 Script executed:

#!/bin/bash
# Description: Search for references to unmergedBucketDataLoaded event
# to verify that the cloning is necessary.

rg -A 5 "unmergedBucketDataLoaded"

Length of output: 1630


Verification Successful: Data cloning is necessary and correctly implemented.
The search output confirms that the unmergedBucketDataLoaded event is used in a one-time listener to handle the unmerged bucket data, and that the event is triggered with a clone (dataClone). This ensures that the subsequent in-place merge operation on data does not affect the event handler’s data.

frontend/javascripts/oxalis/model/sagas/quick_select_heuristic_saga.ts (1)

224-226: LGTM: Extended error handling for signed 64-bit arrays.

The addition of BigInt64Array check aligns with the PR objective of supporting more dtypes while maintaining the restriction that color input layers must not be 64-bit.

frontend/javascripts/libs/utils.ts (2)

884-901: LGTM: Improved sign handling in base-256 conversion.

The changes correctly handle signed numbers by:

  1. Capturing the sign before conversion
  2. Using Math.abs() for consistent handling
  3. Applying the sign to each element of the result array

921-934: LGTM: Enhanced sign handling for both number and bigint types.

The changes correctly handle sign for both types:

  1. Detects sign for both number and bigint inputs
  2. Applies sign consistently to both high and low parts
frontend/javascripts/oxalis/geometries/materials/plane_material_factory.ts (3)

232-249: LGTM: Simplified uniform value handling.

Changed from Vector4 to simple number values, which is cleaner and more efficient.


955-958: LGTM: Improved active cell ID handling.

Correctly handles negative IDs and splits them into high and low parts.


970-971: LGTM: Simplified intensity range assignment.

Direct assignment of intensity range values makes the code cleaner and more maintainable.

frontend/javascripts/oxalis/model/sagas/proofread_saga.ts (1)

69-69: LGTM!

The removal of the unused take import is a good cleanup, as the code only uses other effects like takeEvery, takeEveryUnlessBusy, and takeWithBatchActionSupport.

frontend/javascripts/oxalis/view/left-border-tabs/layer_settings_tab.tsx (1)

590-591: LGTM!

The changes enhance the histogram's functionality by adding support for fractional data types. The elementClass is correctly retrieved and used to determine if fractional support is needed based on the data type being "float" or "double".

Also applies to: 594-594

frontend/javascripts/oxalis/view/context_menu.tsx (1)

453-453: Improved segment detection to support negative IDs.

The change from > 0 to !== 0 allows the context menu to handle negative segment IDs, which is essential for supporting the new signed integer data types (int8, int16, int32, int64). This aligns with the PR's objective to enhance dtype support.

biome.json (1)

67-67: LGTM: Added linter rule to enforce clean imports.

The new noUnusedImports rule will help maintain code quality by ensuring all imports are actually used. This is particularly beneficial as new data types are being added to the codebase.

package.json (1)

63-63: LGTM: Added semaphore-promise for concurrent resource management.

The addition of semaphore-promise will help manage concurrent access to resources, which could be beneficial for handling the new data types.

✅ Verification successful

🌐 Web query:

What is the latest version of semaphore-promise and are there any breaking changes or known issues?

💡 Result:

The latest version of `semaphore-promise` is **1.4.2**, released on July 9, 2023[1].  

**Key details about recent updates:**  
- No breaking changes are mentioned in recent changelogs[1][10]  
- Recent updates focus on TypeScript improvements and debug logging capabilities[1][10]  
- Added default export support (since v1.3.0) without breaking existing imports[1]  
- Default constructor now uses 1 semaphore instead of requiring explicit count[1]  

**Known considerations:**  
- Requires proper `release()` calls to avoid deadlocks[2][9]  
- Timeout handling in promise-based workflows might require additional error boundaries (as seen in QUnit integration challenges)[3]  
- Debugging capabilities were added in v1.4.0 to help identify semaphore imbalances[1][10]  

The library maintains backward compatibility while adding features like TypeScript support and debug logging over its last several updates[1][10]. No critical issues specific to the package itself are documented in the sources provided - most potential pitfalls relate to general semaphore usage patterns rather than implementation flaws[3][9].

Citations:

- 1: https://www.npmjs.com/package/semaphore-promise
- 2: https://www.npmjs.com/package/async-mutex/v/0.2.2
- 3: https://github.com/qunitjs/qunit/issues/1375
- 4: https://github.com/semaphoreui/semaphore/releases
- 5: https://www.npmjs.com/package/@chriscdn/promise-semaphore
- 6: https://github.com/chriscdn/promise-semaphore
- 7: https://jackpordi.com/posts/locks-in-js-because-why-not
- 8: https://techdocs.f5.com/kb/en-us/products/big-ip_ltm/releasenotes/related/relnote-supplement-bigip-17-1-1-4.html
- 9: https://github.com/samccone/promise-semaphore
- 10: https://github.com/Granjow/semaphore-promise

Semaphore-promise version 1.4.2 verified and approved.

The latest version on npm is indeed 1.4.2, and recent changelogs confirm no breaking changes. The addition looks good as long as the team remains mindful to correctly handle semaphore releases to avoid potential deadlocks.

Comment on lines +169 to +189
dragIcon.addEventListener("mousedown", (e) => {
isDragging = true;
offsetX = e.clientX - outerContainer.offsetLeft;
offsetY = e.clientY - outerContainer.offsetTop;
dragIcon.style.cursor = "grabbing";
});

window.addEventListener("mousemove", (e) => {
if (!isDragging) return;

outerContainer.style.left = `${e.clientX - offsetX}px`;
outerContainer.style.top = `${e.clientY - offsetY}px`;
});

window.addEventListener("mouseup", () => {
if (isDragging) {
isDragging = false;
dragIcon.style.cursor = "grab";
}
});

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Ensure event listeners are properly removed to prevent memory leaks

When adding event listeners to window and other objects, it's important to remove them when they're no longer needed. Consider removing these listeners when the shader editor is closed or destroyed to prevent potential memory leaks.

Comment on lines +109 to 110
window.materials[identifier][shaderType] = input.ace?.getValue() || input.value;
window.materials[identifier].needsUpdate = true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add null checks for 'input.ace' to prevent runtime errors

Accessing input.ace without ensuring it is initialized can lead to runtime errors if Ace Editor fails to load or initialize. Adding null checks or defaulting to a safe operation can improve the robustness of the code.

Apply this diff to add null checks:

 // Within the overrideShader function
-function overrideShader() {
-  window.materials[identifier][shaderType] = input.ace?.getValue() || input.value;
+function overrideShader() {
+  const shaderCode = input.ace ? input.ace.getValue() : input.value;
+  window.materials[identifier][shaderType] = shaderCode;

 // Within the loadAce().then(...) block
 loadAce().then((transform) => {
   transform(input);

+  if (input.ace) {
     input.ace.commands.addCommand({
       name: "Update Shader",
       bindKey: {
         win: "Ctrl-Enter",
         mac: "Command-Enter",
       },
       exec: overrideShader,
     });
+  }
 });

Ensure that input.ace is properly initialized before using it in the code.

Also applies to: 193-203

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/DataConverter.scala (1)

50-60: LGTM! Consider adding documentation for the new parameter.

The changes to toMaybeUnsigned improve type handling by supporting both signed and unsigned conversions. The implementation is correct and type-safe.

Consider adding ScalaDoc to document the isSigned parameter's purpose and impact:

+  /**
+   * Converts numeric arrays to unsigned types if needed.
+   * @param data The input array to convert
+   * @param isSigned If true, preserves the original signed values; if false, converts to unsigned
+   * @return The converted array
+   */
   def toMaybeUnsigned(
webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/FindDataService.scala (1)

168-182: Simplify histogram index adjustments for signed types.

The current implementation uses different approaches for adjusting histogram indices across signed types. Consider unifying the approach.

Apply this diff to unify the index adjustments:

   case byteData: Array[Byte] => {
-    byteData.foreach(el => counts(el.toInt + 128) += 1)
+    byteData.foreach(el => counts((el + 128).toInt) += 1)
     extrema = (-128, 128)
   }
   case shortData: Array[Short] => {
-    shortData.foreach(el => counts((el / 256.toShort + 128).toInt) += 1)
+    shortData.foreach(el => counts((el / 256 + 128).toInt) += 1)
   }
   case intData: Array[Int] =>
-    intData.foreach(el => counts((el / 16777216 + 128).toInt) += 1)
+    intData.foreach(el => counts((el / 16777216 + 128).toInt) += 1)
webknossos-datastore/app/com/scalableminds/webknossos/datastore/models/datasource/DataLayer.scala (1)

65-78: LGTM! Consider adding documentation for the new method.

The implementation correctly determines signedness for all element classes using pattern matching.

Consider adding ScalaDoc to document the method's purpose and return value:

+  /**
+   * Determines whether an element class represents signed values.
+   * @param elementClass The element class to check
+   * @return true if the element class is signed (float, double, or signed integers),
+   *         false if unsigned
+   */
   def isSigned(elementClass: ElementClass.Value): Boolean =
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c19ef21 and 02eefac.

📒 Files selected for processing (3)
  • webknossos-datastore/app/com/scalableminds/webknossos/datastore/models/datasource/DataLayer.scala (1 hunks)
  • webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/DataConverter.scala (1 hunks)
  • webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/FindDataService.scala (3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: circleci_build
🔇 Additional comments (1)
webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/FindDataService.scala (1)

148-154: ⚠️ Potential issue

Fix off-by-one error in extrema calculation for signed types.

The current extrema calculation for signed types has an off-by-one error. For n-bit signed integers, the range should be [-2^(n-1), 2^(n-1)-1].

Apply this diff to fix the extrema calculation:

   var extrema: (Double, Double) =
     (
       if (ElementClass.isSigned(elementClass))
-        (-math.pow(2, 8 * bytesPerElement - 1), math.pow(2, 8 * bytesPerElement - 1) - 1)
+        (-math.pow(2, 8 * bytesPerElement - 1), math.pow(2, 8 * bytesPerElement - 1) - 1)
       else
-        (0, math.pow(2, 8 * bytesPerElement) - 1)
+        (0, math.pow(2, 8 * bytesPerElement) - 1)
     )

Likely invalid or redundant comment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant