Skip to content
This repository has been archived by the owner on Dec 17, 2024. It is now read-only.

Plan2D #247

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open

Plan2D #247

wants to merge 1 commit into from

Conversation

vim-sroberge
Copy link
Contributor

@vim-sroberge vim-sroberge commented Oct 7, 2024

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced Plan2D and Plans2D classes for enhanced 2D visualization within the 3D viewer.
    • Added freeze functionality to the camera, allowing for temporary halting of movement.
    • Enhanced raycasting to support detection of Plan2D objects.
    • Expanded selection capabilities to include Plan2D objects.
  • Improvements

    • Updated import statements and dependencies to support new features.
  • Bug Fixes

    • Resolved issues related to the handling of mesh attributes and types.

These updates enhance user interaction and visualization capabilities in the viewer.

Copy link

coderabbitai bot commented Oct 7, 2024

Walkthrough

The pull request introduces several updates across multiple files in the vim-webgl-viewer project. Key changes include the addition of a new dependency in package.json, enhancements to the load function in src/main.ts, and the introduction of new classes such as Plan2D and Plans2D. Additionally, existing classes and interfaces have been modified to incorporate new properties and types, improving the functionality and flexibility of the viewer's mesh handling and selection capabilities.

Changes

File Change Summary
package.json Added new dependency: "pdfjs-dist": "^4.6.82" in dependencies.
src/main.ts Updated import for THREE; modified load function to add plans to viewer's gizmos.
src/vim-loader/colorAttributes.ts Added import for SimpleMesh; updated resetMergedColor to handle SimpleMesh instances.
src/vim-loader/mesh.ts Renamed StandardSubmesh to SealedSubmesh; introduced SimpleMesh class; updated MergedSubmesh type to include new classes.
src/vim-loader/objectAttributes.ts Updated AttributeTarget type to include SimpleMesh; modified apply method for more generic handling of sub parameter.
src/vim-loader/progressive/insertableSubmesh.ts Added readonly merged property to InsertableSubmesh; removed previous getter for merged.
src/vim-loader/progressive/instancedSubmesh.ts Added readonly merged property to InstancedSubmesh; removed previous getter for merged.
src/vim-webgl-viewer/camera/camera.ts Introduced freeze property in ICamera interface and Camera class; updated movement logic to incorporate freeze state.
src/vim-webgl-viewer/gizmos/gizmos.ts Added plans property of type Plans2D to Gizmos class; updated constructor to instantiate Plans2D.
src/vim-webgl-viewer/gizmos/plan2D.ts Introduced Plan2D class with methods for managing 2D planes in 3D space.
src/vim-webgl-viewer/gizmos/plans2D.ts Introduced Plans2D class for adding plans; includes methods to handle PDF and image loading.
src/vim-webgl-viewer/raycaster.ts Updated RaycastResult to include Plan2D type; modified GetFirstHit method to detect Plan2D instances.
src/vim-webgl-viewer/selection.ts Updated SelectableObject type to include Plan2D, enhancing selection capabilities.

Possibly related PRs

  • Sroberge/2 0 5 #242: The package.json file was modified to change the version and manage dependencies, which is related to the main PR's update of adding a new dependency.
  • Sroberge/2 0 6 #244: Similar to the main PR, this PR also updates the package.json file, specifically changing the version number, indicating ongoing dependency management.

Suggested reviewers

  • mavimaec

🐇 In the code we hop and play,
New plans and meshes come our way!
With colors bright and features grand,
Our viewer's magic, oh so planned!
Let's celebrate with joy and cheer,
For changes bold are finally here! 🎉


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 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.

Copy link

@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: 14

🧹 Outside diff range and nitpick comments (8)
src/vim-loader/progressive/instancedSubmesh.ts (1)

11-11: LGTM! Consider adding a brief comment for clarity.

The addition of the readonly merged = false property is a good refactoring choice. It simplifies the code while maintaining the same behavior as the previous getter method.

Consider adding a brief comment to explain the purpose of this property, for example:

/** Indicates whether this submesh is merged. Always false for InstancedSubmesh. */
readonly merged = false

This would help future developers understand the intent and invariant nature of this property.

src/vim-webgl-viewer/gizmos/gizmos.ts (2)

73-73: LGTM: Plans2D initialization in constructor

The Plans2D instance is correctly initialized in the constructor.

Consider adding a brief comment explaining the purpose of Plans2D for better code documentation, similar to other properties in this class. For example:

/**
 * The 2D plans gizmo for [brief explanation of its purpose].
 */
readonly plans: Plans2D

Line range hint 82-92: Update dispose method to include new Plans2D instance

The dispose method should be updated to include cleanup for the new plans property. This ensures proper resource management when the Gizmos instance is no longer needed.

Consider updating the dispose method as follows:

dispose () {
  this.viewer.viewport.canvas.parentElement?.removeChild(this.axes.canvas)
  this._measure.clear()
  this.section.dispose()
  this.loading.dispose()
  this.orbit.dispose()
  this.rectangle.dispose()
  this.axes.dispose()
  this.plans.dispose() // Add this line
}

Note: This assumes that Plans2D has a dispose method. If it doesn't, you may need to implement one or handle cleanup differently based on the Plans2D implementation.

src/vim-loader/colorAttributes.ts (1)

104-106: LGTM: Special handling for SimpleMesh added, but documentation needed.

The addition of special handling for SimpleMesh instances is consistent with the changes described in the summary. However, it would be beneficial to add a comment explaining why SimpleMesh instances are treated differently in this context.

Consider adding a comment above the new conditional block to explain the rationale behind this special case, such as:

// SimpleMesh instances do not require color resetting as they handle colors differently
if (sub instanceof SimpleMesh) {
  return
}
src/vim-webgl-viewer/gizmos/plans2D.ts (1)

82-82: Set crossOrigin conditionally based on the image source

Setting image.crossOrigin = 'anonymous' may cause issues if the image is served from the same origin and doesn't require CORS handling. Consider setting it conditionally or providing guidance on when it should be set.

Apply this diff:

-    image.crossOrigin = 'anonymous' // Use this if loading images from a different origin
+    // Set crossOrigin only if loading images from a different origin
+    if (shouldUseCrossOrigin(imageUrl)) {
+      image.crossOrigin = 'anonymous'
+    }

And define the shouldUseCrossOrigin function based on your application's logic.

src/vim-webgl-viewer/gizmos/plan2D.ts (2)

230-234: Avoid using the non-null assertion operator to handle potential null values.

In the getBoundingBox() method, using the non-null assertion operator ! assumes that boundingBox is always defined after computation. It's safer to handle possible null or undefined values explicitly to prevent potential runtime errors.

Modify the return statement to handle undefined boundingBox:

return this.mesh.geometry.boundingBox!
+ return this.mesh.geometry.boundingBox || new THREE.Box3()

98-100: Consider making the initial gizmo mode configurable.

The gizmo is initially set to 'translate' mode. Depending on user requirements, it might be beneficial to allow the initial mode to be configurable or align with user preferences.

Provide an option to set the initial gizmo mode:

constructor (viewer: Viewer, canvas: HTMLCanvasElement, initialMode: 'translate' | 'rotate' | 'scale' = 'translate') {
  // ... existing code ...
  // Enable interaction modes
- gizmo.setMode('translate') // Can be 'translate', 'rotate', or 'scale'
+ gizmo.setMode(initialMode)

This change allows the initial gizmo mode to be specified when creating an instance of Plan2D.

src/vim-webgl-viewer/camera/camera.ts (1)

118-121: Combine sentences in the comment for clarity

The comment for freeze can be made clearer by combining the sentences into one.

Apply this diff to improve the comment:

  /**
-   * Immediately stops the camera movement. And prevents any further movement until set to false.
+   * Immediately stops the camera movement and prevents any further movement until set to false.
   */
  freeze: boolean
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 9b6fab2 and 25c0fcd.

📒 Files selected for processing (13)
  • package.json (1 hunks)
  • src/main.ts (2 hunks)
  • src/vim-loader/colorAttributes.ts (2 hunks)
  • src/vim-loader/mesh.ts (4 hunks)
  • src/vim-loader/objectAttributes.ts (2 hunks)
  • src/vim-loader/progressive/insertableSubmesh.ts (1 hunks)
  • src/vim-loader/progressive/instancedSubmesh.ts (1 hunks)
  • src/vim-webgl-viewer/camera/camera.ts (4 hunks)
  • src/vim-webgl-viewer/gizmos/gizmos.ts (3 hunks)
  • src/vim-webgl-viewer/gizmos/plan2D.ts (1 hunks)
  • src/vim-webgl-viewer/gizmos/plans2D.ts (1 hunks)
  • src/vim-webgl-viewer/raycaster.ts (2 hunks)
  • src/vim-webgl-viewer/selection.ts (1 hunks)
🧰 Additional context used
🔇 Additional comments (23)
src/vim-loader/progressive/insertableSubmesh.ts (1)

12-12: Approve the addition of the merged property with suggestions.

The addition of the readonly merged = true property looks good. However, I have a few suggestions to ensure its effectiveness and maintainability:

  1. Consider adding a comment to document the purpose of this property. It would help other developers understand why it's always true and how it's used.

  2. Verify that all usages of the previous merged getter have been updated to use this property instead. This change from a getter to a property might affect how the class is used elsewhere in the codebase.

  3. Given that this property is always true, consider if it's being used for type checking or interface implementation. If not, it might be more appropriate to use a type assertion or implement an interface explicitly, rather than having a constant property.

To help verify the usage and impact of this change, you can run the following script:

This script will help identify where the merged property is being used and if there are any interfaces that might require it.

✅ Verification successful

No usages of merged found in the codebase.

The readonly merged = true property has been successfully added to InsertableSubmesh. No references or dependencies on this property were detected in the current codebase, indicating that its addition does not impact existing functionality.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for usages of InsertableSubmesh.merged in the codebase

# Search for usages of InsertableSubmesh.merged
echo "Usages of InsertableSubmesh.merged:"
rg --type typescript "InsertableSubmesh.*\.merged"

# Search for potential interface definitions that might require this property
echo "\nPotential interfaces requiring 'merged' property:"
rg --type typescript "interface.*\{[^}]*merged\s*:[^}]*\}"

Length of output: 383


Script:

#!/bin/bash
# Description: Check for usages of InsertableSubmesh.merged in TypeScript files

# Search for usages of InsertableSubmesh.merged
echo "Usages of InsertableSubmesh.merged:"
rg -g "*.ts" -g "*.tsx" "InsertableSubmesh\..*\.merged"

# Search for potential interface definitions that might require this property
echo "\nPotential interfaces requiring 'merged' property:"
rg -g "*.ts" -g "*.tsx" "interface\s+\w+\s*\{[^}]*merged\s*:\s*[^}]*\}"

Length of output: 331

package.json (1)

56-56: New dependency added: pdfjs-dist

The addition of pdfjs-dist as a dependency is noted. This likely introduces PDF-related functionality to the project.

Please ensure the following:

  1. Verify that PDF functionality aligns with the project scope and requirements.

  2. Check for any potential conflicts with existing dependencies.

  3. Review known vulnerabilities for pdfjs-dist@4.6.82:

  4. Update project documentation to reflect this new capability.

Would you like assistance in updating the project documentation or creating a GitHub issue to track these tasks?

src/main.ts (2)

1-2: LGTM: Import statement change for THREE.js

The change to import the entire THREE.js module as a namespace is a good practice. It provides more flexibility and is consistent with common usage patterns of the THREE.js library.


Line range hint 1-43: Overall changes look good, consider updating documentation

The changes to the load function enhance the viewer's capabilities by adding support for plans. The modifications integrate well with the existing code structure and are consistent with the PR objectives.

To ensure maintainability and ease of use:

  1. Consider updating any relevant documentation to reflect these new capabilities.
  2. If there are unit tests for the load function, they should be updated to cover the new functionality.

To ensure that documentation is up-to-date, let's check for any README files or documentation in the project:

This will help identify any documentation that might need to be updated to reflect the new plan functionality.

src/vim-webgl-viewer/gizmos/gizmos.ts (2)

10-10: LGTM: New import statement for Plans2D

The import statement for Plans2D is correctly formatted and necessary for the new property being added to the Gizmos class.


57-58: LGTM: New Plans2D property

The plans property is correctly declared as readonly and of type Plans2D. Its public accessibility and placement within the class are consistent with the existing structure.

src/vim-loader/objectAttributes.ts (3)

6-6: LGTM: Import statement updated correctly.

The addition of SimpleMesh to the import statement is consistent with its usage in the AttributeTarget type definition. This change enhances the module's capability to work with different mesh types.


54-54: LGTM: apply method updated for flexibility.

The removal of the type assertion for sub as MergedSubmesh allows for more generic handling of the sub variable, which is consistent with the expanded AttributeTarget type. This change improves the flexibility of the ObjectAttribute class in dealing with different types of mesh attributes.

To ensure that this change doesn't introduce any compatibility issues, please verify that the applyMerged method can handle all possible types defined in AttributeTarget. Run the following script to check for potential issues:

#!/bin/bash
# Description: Check compatibility of applyMerged method with AttributeTarget types

# Test 1: Check the applyMerged method implementation
echo "Implementation of applyMerged method:"
ast-grep --lang typescript --pattern $'class ObjectAttribute {
  $$$
  private applyMerged(sub: $_, number: number) {
    $$$
  }
  $$$
}'

# Test 2: Look for any type checks or assertions within applyMerged
echo "Type checks or assertions in applyMerged:"
rg --type typescript "applyMerged.*\{" -A 10 | rg "(instanceof|as |typeof)"

# Test 3: Check for usage of properties specific to MergedSubmesh
echo "Usage of MergedSubmesh-specific properties:"
rg --type typescript "applyMerged.*\{" -A 20 | rg "(meshStart|meshEnd)"

8-8: LGTM: AttributeTarget type updated correctly.

The addition of SimpleMesh to the AttributeTarget type definition expands the possible types that can be used for mesh attributes, which is consistent with the updated import statement.

To ensure that this change doesn't introduce any unintended side effects, please verify the behavior of methods that use the AttributeTarget type, particularly the apply method. Run the following script to check for any potential issues:

src/vim-loader/colorAttributes.ts (2)

6-6: LGTM: Import of SimpleMesh added correctly.

The addition of SimpleMesh to the import statement is consistent with the changes described in the summary and follows the existing import style.


Line range hint 1-193: Consider reviewing the entire ColorAttribute class for consistent SimpleMesh handling.

With the introduction of special handling for SimpleMesh instances in the resetMergedColor method, it's important to ensure that other methods in the ColorAttribute class handle SimpleMesh instances consistently. This may include methods like applyMergedColor, applyInstancedColor, and others.

To assist with this review, you can run the following script to identify other methods in the ColorAttribute class that might need updates:

Please review the output of this script and consider whether any other methods need to be updated to handle SimpleMesh instances consistently.

src/vim-webgl-viewer/selection.ts (2)

10-10: LGTM: Import statement for Plan2D is correct.

The import statement for Plan2D is correctly added and is necessary for the subsequent type definition change.


Line range hint 1-248: Summary: Plan2D integration looks good. Ensure comprehensive testing.

The changes to include Plan2D as a selectable object type have been implemented correctly. The Selection class implementation appears to handle objects generically, which should accommodate the new Plan2D type without further modifications.

To ensure the integration is complete and correct:

  1. Verify that all Selection class methods work as expected with Plan2D objects.
  2. Add unit tests specifically for selecting, deselecting, and manipulating Plan2D objects.
  3. Update any relevant documentation to reflect the new selectable object type.

Run the following script to identify potential areas that might need additional testing:

This script will help identify any type-specific handling in the Selection class methods, which might need to be updated or tested for Plan2D objects.

src/vim-loader/mesh.ts (4)

129-129: Ensure correct handling of 'SealedSubmesh' in 'getSubmeshFromFace'

The method getSubmeshFromFace now returns SealedSubmesh. Verify that any code that calls this method and expects a StandardSubmesh is updated to handle SealedSubmesh.


137-137: Update code to handle 'SealedSubmesh' array from 'getSubmeshes'

The getSubmeshes method now returns an array of SealedSubmesh instances. Ensure that all usages of this method are compatible with SealedSubmesh and that any type assertions or expectations are updated accordingly.


167-168: Review changes to 'MergedSubmesh' and 'Submesh' types

The MergedSubmesh type now includes SealedSubmesh, InsertableSubmesh, and SimpleMesh. The Submesh type includes MergedSubmesh and InstancedSubmesh. Ensure that all code that uses these types is updated to handle the new type structure.

Consider searching for usages of these types:

#!/bin/bash
# Description: Find all usages of `MergedSubmesh` and `Submesh` to verify compatibility.

# Test: Search for usages of `MergedSubmesh` and `Submesh` in TypeScript files.
rg 'MergedSubmesh|Submesh' --type ts

118-118: Verify compatibility of 'SealedSubmesh' with 'StandardSubmesh'

The method getSubMesh now returns SealedSubmesh instead of StandardSubmesh. Ensure that SealedSubmesh provides all the methods and properties expected by code that uses getSubMesh.

You can run the following script to find all usages of getSubMesh and check for any type incompatibilities:

src/vim-webgl-viewer/raycaster.ts (4)

14-14: Importing Plan2D

The Plan2D class is now imported, enabling the raycasting functionality to include Plan2D objects.


50-59: Enhance raycasting to detect Plan2D objects

The GetFirstHit method now includes logic to detect Plan2D instances in raycasting results. This broadens the interactable objects within the scene.


45-47: Ensure consistent usage of GetFirstHit

The method GetFirstVimHit has been replaced with GetFirstHit. Verify that all references to GetFirstVimHit have been updated throughout the codebase to prevent any broken references.

Run the following script to check for any remaining references to GetFirstVimHit:

#!/bin/bash
# Description: Search for any occurrences of 'GetFirstVimHit' that may have been missed.

rg 'GetFirstVimHit'

30-30: Update object property to include Plan2D

The object property in RaycastResult now includes Plan2D as a possible type. Ensure that all usages of RaycastResult.object handle Plan2D instances correctly to prevent any runtime errors.

Run the following script to find all references to RaycastResult.object and check for compatibility with Plan2D:

src/vim-webgl-viewer/camera/camera.ts (2)

160-160: LGTM!

The private field _freeze is appropriately initialized.


213-220: Verify if events should be dispatched when freeze is modified

Consider whether the freeze property change should trigger any events, such as _onValueChanged or _onMoved, to notify other components of the state change.

Comment on lines +40 to +43
const plan = await viewer.gizmos.plans.addPlan('https://vimdevelopment01storage.blob.core.windows.net/samples/floor_plan.png')
const plan2 = await viewer.gizmos.plans.addPlan('https://vimdevelopment01storage.blob.core.windows.net/samples/floor_plan.pdf')
plan.color = new THREE.Color(0x00ff00)

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider parameterizing plan URLs and colors

The addition of plans to the viewer's gizmos enhances its capabilities. However, there are a few points to consider:

  1. The URLs for the plans are hardcoded. Consider parameterizing these for better maintainability.
  2. Only the first plan's color is set. Is this intentional? If not, consider setting colors for both plans or using a consistent approach.

Here's a suggestion to improve this section:

const planUrls = [
  'https://vimdevelopment01storage.blob.core.windows.net/samples/floor_plan.png',
  'https://vimdevelopment01storage.blob.core.windows.net/samples/floor_plan.pdf'
];

const planColors = [
  new THREE.Color(0x00ff00),
  new THREE.Color(0x0000ff)  // Example second color
];

for (let i = 0; i < planUrls.length; i++) {
  const plan = await viewer.gizmos.plans.addPlan(planUrls[i]);
  plan.color = planColors[i];
}

This approach allows for easier management of multiple plans and their colors.


export type SelectableObject = Object3D | GizmoMarker
export type SelectableObject = Object3D | GizmoMarker | Plan2D
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

Potential Issue: Selection class may not handle Plan2D objects

The SelectableObject type has been updated to include Plan2D, but there are no corresponding implementations in the Selection class methods to handle Plan2D objects. This omission could lead to unexpected behavior or runtime errors when Plan2D objects are selected.

  • src/vim-webgl-viewer/selection.ts
    • Line 12: export type SelectableObject = Object3D | GizmoMarker | Plan2D
🔗 Analysis chain

LGTM: SelectableObject type updated correctly. Verify class implementation.

The SelectableObject type has been correctly updated to include Plan2D. This change expands the selection capabilities as intended.

To ensure the Selection class correctly handles the new Plan2D type, please run the following verification script:

This script will help identify any potential issues or missing implementations related to the Plan2D type in the Selection class methods.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that the Selection class methods handle Plan2D objects correctly.

# Test: Check if Plan2D is used in the Selection class methods
rg --type typescript -g 'src/vim-webgl-viewer/selection.ts' -e 'Plan2D' -C 5

# Test: Verify that no type errors are introduced by this change
ast-grep --lang typescript --pattern 'class Selection {
  $$$
  $_($$_: $$$Plan2D$$$) {
    $$$
  }
  $$$
}'

Length of output: 484


Script:

#!/bin/bash
# Description: Verify that the Selection class methods handle Plan2D objects correctly.

# Test 1: Check if Plan2D is used in the Selection class methods
rg --type typescript -g 'src/vim-webgl-viewer/selection.ts' -e 'Plan2D' -C 5

# Test 2: Verify that no type errors are introduced by this change
ast-grep --lang typescript --pattern '
class Selection {
  $$$
  $_($$_: Plan2D) {
    $$$
  }
  $$$
}
'

Length of output: 216


Script:

#!/bin/bash
# Description: Verify that the Selection class methods handle Plan2D objects correctly.

# Test 1: Check if Plan2D is used in the Selection class methods
rg -g 'src/vim-webgl-viewer/selection.ts' -e 'Plan2D' -C 5

# Test 2: Verify that no type errors are introduced by this change
ast-grep --lang typescript --pattern 'class Selection { 
  $$$ 
  $_($$_: Plan2D) { 
    $$$ 
  } 
  $$$ 
}'

Length of output: 1073

Comment on lines +19 to +35
async addPlan (url: string): Promise<Plan2D> {
let canvas: HTMLCanvasElement

// Determine the file type based on the file extension
const extension = url.split('.').pop()?.toLowerCase()
if (extension === 'pdf') {
canvas = await loadPdfAsImage(url)
} else if (['png', 'jpg', 'jpeg', 'gif', 'bmp'].includes(extension!)) {
canvas = await loadImageAsCanvas(url)
} else {
throw new Error('Unsupported file type')
}

const plan = new Plan2D(this._viewer, canvas)
this._viewer.renderer.add(plan.mesh)
return plan
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Validate input URLs to enhance security

Loading resources from external URLs without validation can introduce security vulnerabilities such as Cross-Site Scripting (XSS) or injection attacks. Consider validating the input URL to ensure it conforms to expected formats or domains, or sanitize it before usage.

Apply this diff to add basic URL validation:

async addPlan (url: string): Promise<Plan2D> {
+  if (!isValidUrl(url)) {
+    throw new Error('Invalid URL')
+  }

   let canvas: HTMLCanvasElement

   // Determine the file type based on the file extension

And define the isValidUrl function:

function isValidUrl(url: string): boolean {
  try {
    new URL(url)
    return true
  } catch (e) {
    return false
  }
}

Further enhancements could include checking against a whitelist of allowed domains.

import * as PDF from 'pdfjs-dist'
import { Plan2D } from './plan2D'

PDF.GlobalWorkerOptions.workerSrc = 'node_modules/pdfjs-dist/build/pdf.worker.mjs'
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Make the PDF worker source path dynamic

Setting a hardcoded path for PDF.GlobalWorkerOptions.workerSrc may lead to issues in different deployment environments where the path to pdf.worker.mjs might vary. Consider using a dynamic import or configuring the worker source path based on the environment to ensure compatibility.

Apply this diff to use a dynamic import:

-PDF.GlobalWorkerOptions.workerSrc = 'node_modules/pdfjs-dist/build/pdf.worker.mjs'
+import pdfjsWorker from 'pdfjs-dist/build/pdf.worker.entry'
+PDF.GlobalWorkerOptions.workerSrc = pdfjsWorker

Ensure that your build system supports loading worker files correctly.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
PDF.GlobalWorkerOptions.workerSrc = 'node_modules/pdfjs-dist/build/pdf.worker.mjs'
import pdfjsWorker from 'pdfjs-dist/build/pdf.worker.entry'
PDF.GlobalWorkerOptions.workerSrc = pdfjsWorker

const canvas = document.createElement('canvas')
canvas.width = image.width
canvas.height = image.height
const context = canvas.getContext('2d')!
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Handle potential null return from getContext('2d')

Similar to the previous comment, ensure that the canvas.getContext('2d') call handles the possibility of returning null.

Apply this diff:

-const context = canvas.getContext('2d')!
+const context = canvas.getContext('2d')
+if (!context) {
+  reject(new Error('2D rendering context is not available'))
+  return
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const context = canvas.getContext('2d')!
const context = canvas.getContext('2d')
if (!context) {
reject(new Error('2D rendering context is not available'))
return
}

Comment on lines +41 to +43
// Create a plane geometry (width and height based on canvas dimensions)
this._geometry = new THREE.PlaneGeometry(100, 100)

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Initialize plane geometry dimensions based on canvas size instead of scaling the mesh.

Currently, you're creating a PlaneGeometry with fixed dimensions and then scaling the mesh to match the canvas size. This can be simplified by directly initializing the geometry with the canvas dimensions, improving code clarity and potentially performance.

Apply this diff to modify the geometry initialization and remove the scaling:

// Create a plane geometry with dimensions based on canvas size
- this._geometry = new THREE.PlaneGeometry(100, 100)
+ const width = canvas.width * 0.001
+ const height = canvas.height * 0.001
+ this._geometry = new THREE.PlaneGeometry(width, height)

- frameMesh.scale.set(canvas.width, canvas.height, 1).multiplyScalar(0.001)

Also applies to: 50-50

Comment on lines +116 to +117
window.addEventListener('keydown', onKey)
this._subs.push(() => window.removeEventListener('keydown', onKey))
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid attaching global 'keydown' event listener to the window object and prevent default browser actions.

Attaching a 'keydown' event listener to the global window object can lead to unintended side effects and potential conflicts with other components. Additionally, handling keys like 't', 'r', and 's' without preventing default browser behavior might interfere with the application's functionality.

Solution: Attach the event listener to the viewer's canvas and prevent default actions.

Apply this diff to attach the event listener to the canvas and prevent default behavior:

const onKey = (event: KeyboardEvent): void => {
  if (!this.selected) return
+ event.preventDefault()
  switch (event.key) {
    case 't': // 't' for translate
      gizmo.setMode('translate')
      break
    case 'r': // 'r' for rotate
      gizmo.setMode('rotate')
      break
    case 's': // 's' for scale
      gizmo.setMode('scale')
      break
  }
}

- window.addEventListener('keydown', onKey)
- this._subs.push(() => window.removeEventListener('keydown', onKey))
+ this._viewer.viewport.canvas.tabIndex = 0 // Ensure the canvas can receive focus
+ this._viewer.viewport.canvas.addEventListener('keydown', onKey)
+ this._subs.push(() => this._viewer.viewport.canvas.removeEventListener('keydown', onKey))

Additional Note: Setting tabIndex = 0 on the canvas element allows it to receive keyboard events when focused. Ensure that the canvas is focused appropriately within your application.

Also applies to: 103-115

Comment on lines +213 to +215
get size (): THREE.Vector2 {
return new THREE.Vector2(this.mesh.scale.x, this.mesh.scale.y)
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Optimize 'size' getter and setter to reuse a Vector2 instance and improve performance.

Creating a new THREE.Vector2 instance every time the size getter is called can lead to unnecessary memory allocations. Reusing a single instance improves performance, especially if size is accessed frequently.

Introduce a private _size property and update it accordingly:

+ private readonly _size: THREE.Vector2 = new THREE.Vector2()

get size (): THREE.Vector2 {
-   return new THREE.Vector2(this.mesh.scale.x, this.mesh.scale.y)
+   return this._size.set(this.mesh.scale.x, this.mesh.scale.y)
}

set size (value: THREE.Vector2) {
  this.mesh.scale.set(value.x, value.y, 1)
  this.mesh.geometry.computeBoundingBox()
  this._viewer.renderer.needsUpdate = true
+ this._size.copy(value)
}

Also applies to: 220-224

Comment on lines +227 to +231
return this._force
? new THREE.Vector3(1, 1, 1)
: this._freeze
? new THREE.Vector3(0, 0, 0)
: this._allowedMovement
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve readability by replacing nested ternary operators with if-else statements

Nested ternary operators can be hard to read and maintain. Consider refactoring the getter to use if-else statements for better clarity.

Apply this diff to improve readability:

  get allowedMovement () {
-    return this._force
-      ? new THREE.Vector3(1, 1, 1)
-      : this._freeze
-        ? new THREE.Vector3(0, 0, 0)
-        : this._allowedMovement
+    if (this._force) {
+      return new THREE.Vector3(1, 1, 1)
+    } else if (this._freeze) {
+      return new THREE.Vector3(0, 0, 0)
+    } else {
+      return this._allowedMovement
+    }
  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return this._force
? new THREE.Vector3(1, 1, 1)
: this._freeze
? new THREE.Vector3(0, 0, 0)
: this._allowedMovement
get allowedMovement () {
if (this._force) {
return new THREE.Vector3(1, 1, 1)
} else if (this._freeze) {
return new THREE.Vector3(0, 0, 0)
} else {
return this._allowedMovement
}
}

Comment on lines +246 to +250
return this._force
? new THREE.Vector2(1, 1)
: this._freeze
? new THREE.Vector2(0, 0)
: this._allowedRotation
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve readability by replacing nested ternary operators with if-else statements

Nested ternary operators can be hard to read and maintain. Consider refactoring the getter to use if-else statements for better clarity.

Apply this diff to improve readability:

  get allowedRotation () {
-    return this._force
-      ? new THREE.Vector2(1, 1)
-      : this._freeze
-        ? new THREE.Vector2(0, 0)
-        : this._allowedRotation
+    if (this._force) {
+      return new THREE.Vector2(1, 1)
+    } else if (this._freeze) {
+      return new THREE.Vector2(0, 0)
+    } else {
+      return this._allowedRotation
+    }
  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return this._force
? new THREE.Vector2(1, 1)
: this._freeze
? new THREE.Vector2(0, 0)
: this._allowedRotation
get allowedRotation () {
if (this._force) {
return new THREE.Vector2(1, 1)
} else if (this._freeze) {
return new THREE.Vector2(0, 0)
} else {
return this._allowedRotation
}
}

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

Successfully merging this pull request may close these issues.

1 participant