diff --git a/CHANGELOG.md b/CHANGELOG.md index 12bdbf4..bd287f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,23 @@ All notable changes to the **VS Code Aster** extension will be documented in thi The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.7.0] - 2026-04-16 + +New viewer toolbar with bounding box, wireframe, and screenshot tools. + +### Added +- Top toolbar in the mesh viewer with three new tools: + - **Bounding box**: toggleable wireframe cube with colored axes (X red, Y green, Z blue), corner dots, a "0" origin marker, and dimension labels anchored in 3D + - **Wireframe mode**: toggle between solid surface and wireframe rendering to inspect mesh density + - **Screenshot**: left click saves the 3D view as PNG next to the source file and copies to clipboard; right click captures the full viewer including the sidebar +- Toolbar button tooltips using the same inline hover pattern as the rest of the UI +- Toolbar tab in the viewer help popup documenting the three new tools +- Updated README with diagnostics, terminal reuse, direct `.med` opening, and toolbar features + +### Fixed +- Popup z-order: help and settings popups no longer render behind the sidebar +- Sidebar tooltip z-order: filter/clear tooltips no longer hidden behind the top toolbar + ## [1.6.1] - 2026-04-15 Standalone mesh visualization: click any `.med` file to open the viewer directly, even without a `.comm`/`.export` pair. diff --git a/CITATION.cff b/CITATION.cff index 7950d6d..55948ee 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -1,4 +1,4 @@ -cff-version: 1.6.1 +cff-version: 1.7.0 title: VS Code Aster message: >- If you use this software, please cite it using the diff --git a/README.md b/README.md index 76bf4ba..8d55815 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@

Simvia Logo

- Version + Version License

@@ -135,7 +135,11 @@ There are two ways to open the form : 1. Open a `.export` file. 2. Click on the "play" icon `Run with code_aster` in the top-right corner of the file. -It will open a terminal and execute the following command : `cave run [file].export`. +It will open a terminal and execute the following command : `cave run [file].export`. Subsequent runs reuse the same terminal. + +**Diagnostics** + +Warnings (``), errors (``, ``), Python tracebacks, and fatal errors from code_aster automatically appear in the VS Code **Problems panel** after a run — no `F mess` entry required in the `.export` file. Diagnostics are attached to the originating `.comm` line when possible and cleared between runs. **Personnalize alias to run code-aster** @@ -182,21 +186,24 @@ The visualizer is an integrated 3D viewer that lets you display and explore your It’s powered by **VTK.js**, and supports both mesh visualization and node-based groups. -#### Opening the visualizer +![Mesh viewer screenshot](media/images/viewer-screenshot.png) -The visualizer is very easy to open : +#### Opening the visualizer -1. Open a `.comm` file. -2. Click on the "eye" icon `Open visualizer` in the top-right corner of the file. +There are two ways to open the visualizer : -The visualizer is now open ! +- **From a `.comm` file** : click on the "eye" icon `Open visualizer` in the top-right corner of the file. +- **From a `.med` file** : click any `.med`, `.mmed`, or `.rmed` file in the explorer — it opens directly in the viewer, no `.comm` file needed. Files with non-standard MED extensions (e.g. `.71`) are auto-detected and can be registered in one click. #### Features - Load geometry files (`.med`) directly into the viewer - Highlight face and node groups using the sidebar - Highlight groups quickly by selecting their names from your command file (`.comm`) -- Control the camera with by rotating or panning it +- Control the camera by rotating or panning it +- **Bounding box** : toggle a wireframe cube with colored axes (X red, Y green, Z blue), corner dots, and dimension labels to quickly read the characteristic size of the structure +- **Wireframe mode** : switch between solid surface and wireframe rendering to inspect mesh density +- **Screenshot** : save the current 3D view as a PNG file next to your mesh and copy it to the clipboard #### Usage tips @@ -211,6 +218,9 @@ The visualizer is now open ! - Hold `Shift` + `Left click` and move your mouse to pan the camera - Use the `Mouse wheel` to zoom in and out - Click on the `X`, `Y`, and `Z` buttons at the bottom of the sidebar to quickly align the camera along an axis +- Toolbar : + - The top toolbar provides quick access to the bounding box, wireframe, and screenshot features + - Right-click the screenshot button to capture the full viewer including the sidebar - File management : - Mesh files (`.*med` files) are converted to `.obj` files, which are stored in a hidden folder called `.visu_data/` in your workspace diff --git a/ROADMAP.md b/ROADMAP.md index fbfaa89..3308232 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -4,7 +4,7 @@ The extension aims to reduce friction between modeling, validation, execution, and analysis by bringing **code_aster** native workflows into the editor. -## Current Capabilities (v1.6.1) +## Current Capabilities (v1.7.0) - `.export` file generator - 3D mesh viewer diff --git a/media/images/viewer-screenshot.png b/media/images/viewer-screenshot.png new file mode 100644 index 0000000..95999ef Binary files /dev/null and b/media/images/viewer-screenshot.png differ diff --git a/package-lock.json b/package-lock.json index 1b1a5ff..72ffd93 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "vs-code-aster", - "version": "1.6.1", + "version": "1.7.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "vs-code-aster", - "version": "1.6.1", + "version": "1.7.0", "license": "GPL-3.0", "dependencies": { "@tailwindcss/cli": "^4.1.17", @@ -23,6 +23,7 @@ "@vscode/test-electron": "^2.5.2", "esbuild": "^0.25.5", "eslint": "^9.25.1", + "html-to-image": "^1.11.13", "husky": "^9.1.7", "npm-run-all": "^4.1.5", "prettier": "^3.8.1", @@ -3433,6 +3434,13 @@ "dev": true, "license": "ISC" }, + "node_modules/html-to-image": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/html-to-image/-/html-to-image-1.11.13.tgz", + "integrity": "sha512-cuOPoI7WApyhBElTTb9oqsawRvZ0rHhaHwghRLlTuffoD1B2aDemlCruLeZrUIIdvG7gs9xeELEPm6PhuASqrg==", + "dev": true, + "license": "MIT" + }, "node_modules/http-proxy-agent": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", diff --git a/package.json b/package.json index deb8c15..1c82f2c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vs-code-aster", "displayName": "VS Code Aster", - "version": "1.6.1", + "version": "1.7.0", "description": "VS Code extension for code_aster", "publisher": "simvia", "license": "GPL-3.0", @@ -138,9 +138,15 @@ "viewType": "vs-code-aster.medViewer", "displayName": "MED Mesh Viewer", "selector": [ - { "filenamePattern": "*.med" }, - { "filenamePattern": "*.mmed" }, - { "filenamePattern": "*.rmed" } + { + "filenamePattern": "*.med" + }, + { + "filenamePattern": "*.mmed" + }, + { + "filenamePattern": "*.rmed" + } ], "priority": "default" } @@ -274,6 +280,18 @@ "default": true, "markdownDescription": "Show the orientation axes widget in the bottom-right corner of the viewer." }, + "vs-code-aster.viewer.showBoundingBox": { + "order": 17, + "type": "boolean", + "default": false, + "markdownDescription": "Show a bounding box with X/Y/Z tick marks and values around the loaded meshes in the viewer." + }, + "vs-code-aster.viewer.showWireframe": { + "order": 18, + "type": "boolean", + "default": false, + "markdownDescription": "Render meshes in wireframe mode instead of solid surfaces." + }, "vs-code-aster.enableTelemetry": { "order": 100, "type": "boolean", @@ -306,6 +324,7 @@ "@vscode/test-electron": "^2.5.2", "esbuild": "^0.25.5", "eslint": "^9.25.1", + "html-to-image": "^1.11.13", "husky": "^9.1.7", "npm-run-all": "^4.1.5", "prettier": "^3.8.1", diff --git a/src/MedEditorProvider.ts b/src/MedEditorProvider.ts index 4f072d5..fe026db 100644 --- a/src/MedEditorProvider.ts +++ b/src/MedEditorProvider.ts @@ -76,6 +76,7 @@ export class MedEditorProvider implements vscode.CustomReadonlyEditorProvider { this.activeViewers.delete(medPath); diff --git a/src/VisuManager.ts b/src/VisuManager.ts index 94a0cba..9aad1b9 100644 --- a/src/VisuManager.ts +++ b/src/VisuManager.ts @@ -128,6 +128,7 @@ export class VisuManager { commName ); + visu.sourceDir = path.dirname(commUri.fsPath); this.views.set(key, { commUri, objUris, visu }); // Send telemetry once per opening of this .comm (non-blocking). diff --git a/src/WebviewVisu.ts b/src/WebviewVisu.ts index 4322eb0..b7c28fe 100644 --- a/src/WebviewVisu.ts +++ b/src/WebviewVisu.ts @@ -14,6 +14,7 @@ export class WebviewVisu implements vscode.Disposable { private readyReceived = false; private deferredInit?: { fileContexts: string[]; objFilenames: string[] }; + public sourceDir?: string; public get webview(): vscode.Webview { return this.panel.webview; @@ -119,6 +120,8 @@ export class WebviewVisu implements vscode.Disposable { 'edgeMode', 'groupTransparency', 'showOrientationWidget', + 'showBoundingBox', + 'showWireframe', ]; for (const key of settingKeys) { if (e.settings[key] !== undefined) { @@ -126,6 +129,15 @@ export class WebviewVisu implements vscode.Disposable { } } break; + case 'saveScreenshot': { + if (this.sourceDir) { + const base64 = (e.dataUrl as string).replace(/^data:image\/png;base64,/, ''); + const buffer = Buffer.from(base64, 'base64'); + const filePath = path.join(this.sourceDir, e.filename as string); + fs.writeFileSync(filePath, buffer); + } + break; + } case 'debugPanel': // Log debug messages from the webview console.log('[WebviewVisu] Message received from webview:', e.text); @@ -178,6 +190,8 @@ export class WebviewVisu implements vscode.Disposable { edgeMode: config.get('viewer.edgeMode', 'threshold'), groupTransparency: config.get('viewer.groupTransparency', 0.2), showOrientationWidget: config.get('viewer.showOrientationWidget', true), + showBoundingBox: config.get('viewer.showBoundingBox', false), + showWireframe: config.get('viewer.showWireframe', false), }; this.panel.webview.postMessage({ type: 'init', diff --git a/webviews/viewer/index.html b/webviews/viewer/index.html index 318a9bc..8782a87 100644 --- a/webviews/viewer/index.html +++ b/webviews/viewer/index.html @@ -6,7 +6,7 @@ MeshViewer - +
diff --git a/webviews/viewer/src/components/layout/App.svelte b/webviews/viewer/src/components/layout/App.svelte index e689b8d..3d03e61 100644 --- a/webviews/viewer/src/components/layout/App.svelte +++ b/webviews/viewer/src/components/layout/App.svelte @@ -2,7 +2,9 @@ import { groupHierarchy } from '../../lib/state'; import Sidebar from './Sidebar.svelte'; import TopActions from './TopActions.svelte'; + import TopToolbar from './TopToolbar.svelte'; import ZoomWidget from '../viewer/ZoomWidget.svelte'; + import BoundingBoxLabels from '../viewer/BoundingBoxLabels.svelte'; import Popup from '../popups/Popup.svelte'; import HelpPopup from '../popups/HelpPopup.svelte'; import SettingsPopup from '../popups/SettingsPopup.svelte'; @@ -35,7 +37,11 @@ }} /> + + + + {/if} {#if openPopup} diff --git a/webviews/viewer/src/components/layout/Sidebar.svelte b/webviews/viewer/src/components/layout/Sidebar.svelte index a07090e..670e236 100644 --- a/webviews/viewer/src/components/layout/Sidebar.svelte +++ b/webviews/viewer/src/components/layout/Sidebar.svelte @@ -13,7 +13,7 @@ }); -
+
{#each Object.entries($groupHierarchy) as [key, data]} + import BoundingBoxButton from '../viewer/BoundingBoxButton.svelte'; + import WireframeButton from '../viewer/WireframeButton.svelte'; + import ScreenshotButton from '../viewer/ScreenshotButton.svelte'; + + +
+ +
+ +
+ +
diff --git a/webviews/viewer/src/components/popups/HelpPopup.svelte b/webviews/viewer/src/components/popups/HelpPopup.svelte index 59d87b8..fc42a72 100644 --- a/webviews/viewer/src/components/popups/HelpPopup.svelte +++ b/webviews/viewer/src/components/popups/HelpPopup.svelte @@ -3,6 +3,7 @@