Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,17 @@ 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.9.2] - 2026-04-23

Better rendering for 1D meshes and a flatter, more readable face shading.

### Added
- **Standalone 1D edges** (edges not shared with any face, e.g. beam elements) now render by default in the object color with a thin black contour. Line style adapts to parent opacity, wireframe mode, and the face-edge visibility rules (hide / show / threshold / gradual).

### Fixed
- User-defined face groups whose name contains `all_` (e.g. `all_plates`) are no longer mistakenly treated as file-level object actors.
- Face specular highlight removed, so colors no longer shift toward white (e.g. blue → turquoise) at camera-facing angles.

## [1.9.1] - 2026-04-23

New viewer toolbar actions (auto-rotate, video recording), a reorganized settings popup with a dedicated Toolbar tab, and a round of `.export` editor fixes.
Expand Down
2 changes: 1 addition & 1 deletion CITATION.cff
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
cff-version: 1.9.1
cff-version: 1.9.2
title: VS Code Aster
message: >-
If you use this software, please cite it using the
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<p align="center"><img src="https://raw.githubusercontent.com/simvia-tech/vs-code-aster/main/media/images/simvia.png" alt="Simvia Logo" width="50%" /></p>

<p align="center">
<a href="/"><img src="https://img.shields.io/badge/version-1.9.1-blue" alt="Version" /></a>
<a href="/"><img src="https://img.shields.io/badge/version-1.9.2-blue" alt="Version" /></a>
<a href="./LICENSE"><img src="https://img.shields.io/badge/license-GPL%203.0-green" alt="License" /></a>
<a href="https://github.com/simvia-tech/vs-code-aster/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/simvia-tech/vs-code-aster/ci.yml?branch=main&label=CI" alt="CI Status" /></a>
<a href="https://github.com/simvia-tech/vs-code-aster/issues"><img src="https://img.shields.io/github/issues/simvia-tech/vs-code-aster?label=issues" alt="GitHub issues" /></a>
Expand Down
2 changes: 1 addition & 1 deletion ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.9.1)
## Current Capabilities (v1.9.2)

- `.export` file generator
- 3D mesh viewer
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "vs-code-aster",
"displayName": "VS Code Aster",
"version": "1.9.1",
"version": "1.9.2",
"description": "VS Code extension for code_aster",
"publisher": "simvia",
"license": "GPL-3.0",
Expand Down
20 changes: 11 additions & 9 deletions webviews/viewer/src/lib/commands/VisibilityManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,16 +118,16 @@ export class VisibilityManager {
const fileGroup = this.groups[object];
if (fileGroup) {
if (nowVisible) {
fileGroup.actor.setVisibility(true);
fileGroup.setVisibility(true);
const opacity =
this.visibleGroupsByObject[object] > 0 ? GlobalSettings.Instance.groupTransparency : 1.0;
fileGroup.setOpacity(opacity);
} else {
const hiddenOpacity = GlobalSettings.Instance.hiddenObjectOpacity;
if (hiddenOpacity === 0) {
fileGroup.actor.setVisibility(false);
fileGroup.setVisibility(false);
} else {
fileGroup.actor.setVisibility(true);
fileGroup.setVisibility(true);
fileGroup.setOpacity(hiddenOpacity);
}
}
Expand Down Expand Up @@ -165,17 +165,19 @@ export class VisibilityManager {
applyEdgeGroupThickness(): void {
const thickness = GlobalSettings.Instance.edgeGroupThickness;
for (const group of Object.values(this.groups)) {
if (group.kind !== 'edge') continue;
group.actor.getProperty().setLineWidth(thickness);
if (group.kind === 'edge') {
group.actor.getProperty().setLineWidth(thickness);
}
}
VtkApp.Instance.getRenderWindow().render();
}

applyEdgeGroupDepthOffset(): void {
const enabled = GlobalSettings.Instance.edgeGroupDepthOffset;
for (const group of Object.values(this.groups)) {
if (group.kind !== 'edge') continue;
EdgeActorCreator.applyDepthOffset(group.actor.getMapper(), enabled);
if (group.kind === 'edge') {
EdgeActorCreator.applyDepthOffset(group.actor.getMapper(), enabled);
}
}
VtkApp.Instance.getRenderWindow().render();
}
Expand Down Expand Up @@ -213,9 +215,9 @@ export class VisibilityManager {
const fileGroup = this.groups[object];
if (!fileGroup) continue;
if (hiddenOpacity === 0) {
fileGroup.actor.setVisibility(false);
fileGroup.setVisibility(false);
} else {
fileGroup.actor.setVisibility(true);
fileGroup.setVisibility(true);
fileGroup.setOpacity(hiddenOpacity);
}
}
Expand Down
36 changes: 35 additions & 1 deletion webviews/viewer/src/lib/data/CreateGroups.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,31 @@ export class CreateGroups {
const nodeActorCreator = new NodeActorCreator(vertices, nodes, nodeIndexToGroup);
const edgeActorCreator = new EdgeActorCreator(vertices, edges, edgeIndexToGroup);

const edgeKey = (a: number, b: number) => (a < b ? `${a}-${b}` : `${b}-${a}`);
const faceEdgeSet = new Set<string>();
for (const cell of cells) {
for (let i = 0; i < cell.length; i++) {
faceEdgeSet.add(edgeKey(cell[i], cell[(i + 1) % cell.length]));
}
}
const standaloneByFile: Record<string, number[]> = {};
for (let i = 0; i < edges.length; i++) {
const e = edges[i];
let isStandalone = false;
for (let j = 0; j + 1 < e.length; j++) {
if (!faceEdgeSet.has(edgeKey(e[j], e[j + 1]))) {
isStandalone = true;
break;
}
}
if (!isStandalone) continue;
const egId = edgeIndexToGroup[i];
if (egId < 0) continue;
const fileGroup = edgeGroups[egId]?.split('::')[0];
if (!fileGroup) continue;
(standaloneByFile[fileGroup] ||= []).push(i);
}

const groupKeys = Object.keys(groupHierarchy);
const yield_ = () => new Promise<void>((r) => setTimeout(r, 0));

Expand All @@ -70,7 +95,7 @@ export class CreateGroups {
colorIndex: fileColorIndex,
isObjectActor: fileIsObj,
cellCount: fileCellCount,
} = faceActorCreator.create(fileGroup, groupId);
} = faceActorCreator.create(fileGroup, groupId, true);

const groupInstance = new Group(
actor,
Expand All @@ -84,6 +109,15 @@ export class CreateGroups {
);
this.groups[fileGroup] = groupInstance;

const standaloneIdx = standaloneByFile[fileGroup];
if (standaloneIdx && standaloneIdx.length > 0) {
const result = edgeActorCreator.createStandalone(standaloneIdx, objColor);
if (result) {
groupInstance.standaloneEdgesActor = result.actor;
groupInstance.standaloneEdgesContourActor = result.contourActor;
}
}

const size = this.computeSize(actor);

for (const volumeGroup of groupHierarchy[fileGroup].volumes) {
Expand Down
49 changes: 48 additions & 1 deletion webviews/viewer/src/lib/data/Group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,14 @@ export class Group {
colorIndex: number | null;
isObjectActor: boolean;
cellCount: number | null;
standaloneEdgesActor: any = null;
standaloneEdgesContourActor: any = null;
private _edgeT?: number;
private _visible = true;
private _opacity = 1;
private _wireframe = false;
private _edgeVisible = true;
private _edgeFade = 1;

constructor(
actor: any,
Expand Down Expand Up @@ -44,6 +51,9 @@ export class Group {
: GlobalSettings.Instance.meshGroupColors;
const color = colors[this.colorIndex % colors.length];
this.actor.getProperty().setColor(color);
if (this.standaloneEdgesActor) {
this.standaloneEdgesActor.getProperty().setColor(color[0], color[1], color[2]);
}
this._applyEdgeColor();
}

Expand All @@ -54,11 +64,17 @@ export class Group {

if (mode === 'hide') {
prop.setEdgeVisibility(false);
this._edgeVisible = false;
this._edgeFade = 0;
this._updateStandaloneEdges();
return;
}
if (mode === 'show') {
prop.setEdgeVisibility(true);
this._applyFlatEdgeColor(prop);
this._edgeVisible = true;
this._edgeFade = 1;
this._updateStandaloneEdges();
return;
}

Expand All @@ -68,14 +84,21 @@ export class Group {
GlobalSettings.Instance.edgeThresholdMultiplier;

if (mode === 'threshold') {
prop.setEdgeVisibility(currentDistance < threshold);
const visible = currentDistance < threshold;
prop.setEdgeVisibility(visible);
this._applyFlatEdgeColor(prop);
this._edgeVisible = visible;
this._edgeFade = visible ? 1 : 0;
this._updateStandaloneEdges();
return;
}

prop.setEdgeVisibility(true);
this._edgeT = Math.min(1, Math.max(0, threshold / currentDistance));
this._applyEdgeColor();
this._edgeVisible = true;
this._edgeFade = this._edgeT;
this._updateStandaloneEdges();
}

private _applyFlatEdgeColor(prop: any): void {
Expand Down Expand Up @@ -103,10 +126,34 @@ export class Group {
}

setVisibility(visible: boolean): void {
this._visible = visible;
this.actor.setVisibility(visible);
this._updateStandaloneEdges();
}

setOpacity(opacity: number): void {
this._opacity = opacity;
this.actor.getProperty().setOpacity(opacity);
this._updateStandaloneEdges();
}

setWireframeMode(wireframe: boolean): void {
this._wireframe = wireframe;
this._updateStandaloneEdges();
}

private _updateStandaloneEdges(): void {
if (!this.standaloneEdgesActor) return;
const transparent = this._opacity < 1;
const thin = transparent || this._wireframe;
const lineOpacity = transparent ? this._opacity * 0.4 : this._opacity;
this.standaloneEdgesActor.setVisibility(this._visible);
this.standaloneEdgesActor.getProperty().setOpacity(lineOpacity);
this.standaloneEdgesActor.getProperty().setLineWidth(thin ? 1 : 2);
if (this.standaloneEdgesContourActor) {
const contourVisible = this._visible && !thin && this._edgeVisible;
this.standaloneEdgesContourActor.setVisibility(contourVisible);
this.standaloneEdgesContourActor.getProperty().setOpacity(lineOpacity * this._edgeFade);
}
}
}
55 changes: 55 additions & 0 deletions webviews/viewer/src/lib/data/create/EdgeActorCreator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,61 @@ export class EdgeActorCreator {
return { actor, colorIndex, cellCount };
}

createStandalone(
edgeIndices: number[],
color: number[]
): { actor: any; contourActor: any; cellCount: number } | null {
if (edgeIndices.length === 0) return null;

const pd = vtkPolyData.newInstance();
const pts = vtkPoints.newInstance();
const coords = new Float32Array(this.vertices.length * 3);
this.vertices.forEach((v, i) => {
coords[3 * i] = v.x;
coords[3 * i + 1] = v.y;
coords[3 * i + 2] = v.z;
});
pts.setData(coords, 3);
pd.setPoints(pts);

const lineArray = vtkCellArray.newInstance({
values: Uint32Array.from(
edgeIndices.flatMap((i) => {
const e = this.edges[i];
return [e.length, ...e];
})
),
});
pd.setLines(lineArray);

const contourMapper = vtkMapper.newInstance();
contourMapper.setInputData(pd);
contourMapper.setResolveCoincidentTopologyToPolygonOffset();
contourMapper.setRelativeCoincidentTopologyLineOffsetParameters(2, 2);
const contourActor = vtkActor.newInstance();
contourActor.setMapper(contourMapper);
const contourProp = contourActor.getProperty();
contourProp.setColor(0, 0, 0);
contourProp.setLineWidth(4);
contourProp.setLighting(false);
VtkApp.Instance.getRenderer().addActor(contourActor);

const mapper = vtkMapper.newInstance();
mapper.setInputData(pd);
EdgeActorCreator.applyDepthOffset(mapper, false);

const actor = vtkActor.newInstance();
actor.setMapper(mapper);

const prop = actor.getProperty();
prop.setColor(color[0], color[1], color[2]);
prop.setLineWidth(2);
prop.setLighting(false);

VtkApp.Instance.getRenderer().addActor(actor);
return { actor, contourActor, cellCount: edgeIndices.length };
}

static applyDepthOffset(mapper: any, enabled: boolean): void {
if (enabled) {
mapper.setResolveCoincidentTopologyToPolygonOffset();
Expand Down
16 changes: 8 additions & 8 deletions webviews/viewer/src/lib/data/create/FaceActorCreator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,23 @@ export class FaceActorCreator {
}

create(
groupName: string,
groupId: number
_groupName: string,
groupId: number,
isObject = false
): { actor: any; colorIndex: number; isObjectActor: boolean; cellCount: number } {
const { polyData, cellCount } = this.prepare(groupId);

const actor = vtkActor.newInstance();
const mapper = vtkMapper.newInstance();

mapper.setInputData(polyData);
if (!groupName.includes('all_')) {
if (!isObject) {
mapper.setResolveCoincidentTopologyToPolygonOffset();
mapper.setRelativeCoincidentTopologyPolygonOffsetParameters(-2, -2);
}
actor.setMapper(mapper);

const { colorIndex, isObjectActor } = this.setProperty(actor, groupName, cellCount);
const { colorIndex, isObjectActor } = this.setProperty(actor, isObject, cellCount);
VtkApp.Instance.getRenderer().addActor(actor);

return { actor, colorIndex, isObjectActor, cellCount };
Expand Down Expand Up @@ -78,14 +79,14 @@ export class FaceActorCreator {

private setProperty(
actor: any,
groupName: string,
isObject: boolean,
_cellCount: number
): { colorIndex: number; isObjectActor: boolean } {
const prop = actor.getProperty();

let colorIndex: number;
let isObjectActor: boolean;
if (groupName.includes('all_')) {
if (isObject) {
isObjectActor = true;
colorIndex = GlobalSettings.Instance.objIndex;
prop.setColor(GlobalSettings.Instance.getColorForObject());
Expand All @@ -103,8 +104,7 @@ export class FaceActorCreator {
prop.setLineWidth(0.3);
prop.setInterpolationToPhong();
prop.setAmbient(GlobalSettings.Instance.ambientIntensity);
prop.setSpecular(GlobalSettings.Instance.specular);
prop.setSpecularPower(GlobalSettings.Instance.specularPower);
prop.setSpecular(0);

return { colorIndex, isObjectActor };
}
Expand Down
Loading
Loading