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
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/humitos/mirrors-autoflake
rev: v1.1
- repo: https://github.com/PyCQA/autoflake
rev: v2.2.1
hooks:
- id: autoflake
args: ['-i', '--remove-all-unused-imports']
Expand Down
88 changes: 88 additions & 0 deletions ansys_optical_automation/application/Deactivate FOP.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# ----------------------------------------------------------------------------------------
# Script Description:
#
# This script mimics the behavior of the legacy Deactivate SOP feature that was
# available in Speos for Catia. Its main function is to replace the surface optical
# property (SOP) of selected materials with a generic polished surface type.
#
# The idea is that when users want to "disconnect", the script will:
# • Check if the selected material has a surfacic optical property.
# • Change its SOP type to "OpticalPolished".
# • Rename the material by appending " --- POLISHED --- " to indicate the override.
#
# Steps:
# 1. Loop through all selected materials from the active selection.
# 2. Skip any materials already marked as polished.
# 3. For each valid material:
# - Check if it exists in the current Speos material list.
# - If its optical property type is "Surfacic", convert it to "OpticalPolished".
# - Rename it to reflect the change.
# - If not surfacic, show an informative message.
# ----------------------------------------------------------------------------------------
import ctypes

# MessageBox flags
MB_OK = 0x00000000
MB_ICONINFORMATION = 0x00000040
MB_TOPMOST = 0x00040000


# Get handle to foreground window
def get_foreground_hwnd():
"""
Get the handle (HWND) of the current foreground window.

This function uses the Windows API to retrieve the window handle
of the application that currently has the user's focus.

Returns
-------
int
Handle (HWND) of the foreground window.
"""
return ctypes.windll.user32.GetForegroundWindow()


# Function to show a topmost message box
def show_message(message, title="Message"):
"""
Display a topmost message box with an information icon.

This function shows a Windows message box that stays on top of all windows.
It retrieves the handle of the current foreground window to anchor the message box.

Parameters
----------
message : str
The message text to display.
title : str, optional
The title of the message box. Default is "Message".

Returns
-------
int
The result code from the MessageBoxW function (usually MB_OK).
"""
hwnd = get_foreground_hwnd()
ctypes.windll.user32.MessageBoxW(hwnd, message, title, MB_OK | MB_ICONINFORMATION | MB_TOPMOST)


material_selections = Selection.GetActive().Items

for material in material_selections:
try:
# Skip if material name already contains "--- POLISHED ---"
if "--- POLISHED ---" in material.Name:
continue

material__ = SpeosSim.Material.Find(material.Name)
if material__ is None:
show_message("Property " + str(material.Name) + " not found in the material list", "Material Not Found")
else:
if material__.OpticalPropertiesType == SpeosSim.Material.EnumOpticalPropertiesType.Surfacic:
material__.SOPType = SpeosSim.Material.EnumSOPType.OpticalPolished
material__.Name = material__.Name + " --- POLISHED --- "
else:
show_message("Property " + str(material.Name) + " is not an FOP!", "Wrong Type")
except Exception as error:
show_message("Error processing material " + str(material.Name) + ": " + str(error), "Error")
35 changes: 35 additions & 0 deletions ansys_optical_automation/application/GPU Compute All.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# ----------------------------------------------------------------------------------------
# Script Description:
#
# This script creates and configures a Speos Direct Simulation automatically importing
# all elements and enabling GPU-based computation.
#
# Main Objectives:
# • Create a new Direct Simulation.
# • Assign it a default name ("Complete_Simulation") or a timestamped fallback if the
# default name is already in use or causes a conflict.
# • Automatically select all geometries, light sources, and sensors present in the model.
# • Set a predefined number of rays (1e7) for the simulation.
# • Launch the simulation using **GPU Compute** to accelerate performance.
# ----------------------------------------------------------------------------------------

from System import DateTime

# Create a new Speos Direct Simulation
direct = SpeosSim.SimulationDirect.Create()
try:
direct.Name = "Complete_Simulation"
except Exception as e:
print(e) # Print the actual exception
# Get current date and time
now = DateTime.Now
timestamp = now.ToString("yyyyMMdd_HHmmss") # Format: 20250527_141530
# Assign a unique name using timestamp
direct.Name = "Complete_Simulation_" + timestamp
# Select all available geometries, sources, and sensors
direct.Geometries.SelectAll()
direct.Sources.SelectAll()
direct.Sensors.SelectAll()
# Set simulation parameters
direct.NbRays = 1e7
direct.GpuCompute()
58 changes: 58 additions & 0 deletions ansys_optical_automation/application/Reactivate Library FOP.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# ----------------------------------------------------------------------------------------
# Script Description:
#
# This script restores the original surface optical properties (SOP) of Speos materials
# that were previously "disconnected" with "Deactivate SOP".
#
# This restoration script reactivates the previously disabled SOPs by:
#
# • Checking if the selected material is of type "Surfacic"
# • Verifying if its SOP type is currently set to `OpticalPolished`
# • Reverting the SOP type back to `Library`, which re-enables the linked surface property
# • Cleaning up the material name by removing the `"--- POLISHED ---"` suffix (if present)
#
# If any material is not of type Surfacic, or is not marked as OpticalPolished,
# an informative message box will notify the user.
# ----------------------------------------------------------------------------------------
import ctypes

# MessageBox flags
MB_OK = 0x00000000
MB_ICONINFORMATION = 0x00000040
MB_TOPMOST = 0x00040000


# Get handle to foreground window
def get_foreground_hwnd():
return ctypes.windll.user32.GetForegroundWindow()


# Function to show a topmost message box
def show_message(message, title="Message"):
hwnd = get_foreground_hwnd()
ctypes.windll.user32.MessageBoxW(hwnd, message, title, MB_OK | MB_ICONINFORMATION | MB_TOPMOST)


material_selections = Selection.GetActive().Items

for material in material_selections:
try:
material__ = SpeosSim.Material.Find(material.Name)
if material__ is None:
show_message("Property " + str(material.Name) + " not found in the material list", "Material Not Found")
else:
if material__.OpticalPropertiesType == SpeosSim.Material.EnumOpticalPropertiesType.Surfacic:
if material__.SOPType == SpeosSim.Material.EnumSOPType.OpticalPolished:
material__.SOPType = SpeosSim.Material.EnumSOPType.Library
if "--- POLISHED ---" in material__.Name:
material__.Name = material__.Name.replace("--- POLISHED ---", "")
material__.Compute()
# No popup here — silent success
else:
show_message(
"Material " + str(material__.Name) + " is not defined as OpticalPolished", "Not OpticalPolished"
)
else:
show_message("Property " + str(material.Name) + " is not a Surfacic material", "Wrong Type")
except Exception as error:
show_message("Error processing material " + str(material.Name) + ": " + str(error), "Error")
80 changes: 80 additions & 0 deletions ansys_optical_automation/application/Reverse Surface Source.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# ----------------------------------------------------------------------------------------
# Script Description:
#
# This script allows users to **invert the orientation of the emissive faces**
# associated with selected Speos surface light sources.
#
# This is especially useful after using the **"Create Sources from Data"** tool,
# where light sources are created based on geometry name matching, but their
# right **emissive face orientation cannot be predicted** reliably during import.
# ----------------------------------------------------------------------------------------
import ctypes

# MessageBox flags
MB_OK = 0x00000000
MB_ICONERROR = 0x00000010
MB_TOPMOST = 0x00040000


# Get handle to foreground window
def get_foreground_hwnd():
"""
Get the handle (HWND) of the current foreground window.

This function uses the Windows API to retrieve the window handle
of the application that currently has the user's focus.

Returns
-------
int
Handle (HWND) of the foreground window.
"""
return ctypes.windll.user32.GetForegroundWindow()


# Show error message box
def show_error(message, title="Error"):
"""
Display a topmost error message box with an error icon.

This function shows a Windows message box with an error icon that stays
on top of all other windows. It uses the current foreground window as parent.

Parameters
----------
message : str
The message text to display.
title : str, optional
The title of the message box window. Default is "Error".

Returns
-------
int
The result code from the MessageBoxW function (usually MB_OK).
"""
hwnd = get_foreground_hwnd()
ctypes.windll.user32.MessageBoxW(hwnd, message, title, MB_OK | MB_ICONERROR | MB_TOPMOST)


# Get selected items
source_selections = Selection.GetActive().Items

# Iterate over each selected item
for selected in source_selections:
try:
# Try to find the Speos light source by name
led_to_reverse = SpeosSim.SourceSurface.Find(selected.Name)
if led_to_reverse is None:
show_error("Source '" + selected.Name + "' not found in the Speos light list.", "Not Found")
continue

# Reverse the direction of each emissive face
count = 0
for face in led_to_reverse.EmissiveFaces:
face.ReverseDirection = not face.ReverseDirection
count += 1

print("✓ Direction reversed for " + str(count) + " face(s) in source: " + selected.Name)

except Exception as e:
show_error("Error processing source '" + selected.Name + "': " + str(e), "Exception")
106 changes: 106 additions & 0 deletions ansys_optical_automation/application/Setup Blue FOP.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# ----------------------------------------------------------------------------------------
# Script Description:
#
# This script applies surface optical properties (SOP) to blue-colored faces within
# model geometries, based on keys defined in 'FOP.json'.
#
# JSON 'FOP.json' (in OpticalLibraries) format:
# "KeyInBodyName": {
# "FOP": "relative/or/absolute/path/to/.unpolished"
# }
# ----------------------------------------------------------------------------------------
# -*- coding: utf-8 -*-
import json
import os
import re


def get_optical_lib_dir():
ansys_dir = os.environ.get("ANSYS252_DIR")
if not ansys_dir:
raise EnvironmentError("Environment variable 'ANSYS252_DIR' is not defined.")
v252_root = os.path.dirname(ansys_dir) # ...\v252
return os.path.join(v252_root, "Optical Products", "OpticalLibraries")


OPTICAL_LIB_DIR = get_optical_lib_dir()


def resolve_optical_path(path_str):
if not path_str:
return None
if os.path.isabs(path_str):
return path_str
return os.path.join(OPTICAL_LIB_DIR, path_str)


# Load FOP materials from JSON
fop_json_path = os.path.join(OPTICAL_LIB_DIR, "FOP.json")
if not os.path.isfile(fop_json_path):
raise IOError("FOP definition file not found: " + fop_json_path)

with open(fop_json_path, "r") as f:
FOP = json.load(f)

keys = FOP.keys()


# Function to check if a color is blue (with tolerance)
def is_blue_color(color, tolerance=50):
"""
Determines whether a given color is considered blue based on RGB components.
"""
return color.B > 100 and color.B - color.R > tolerance and color.B - color.G > tolerance


# Dictionary to group blue faces by geometry name
blue_faces_by_geometry = {}

# Loop through all bodies in the model
for body in GetRootPart().GetAllBodies():
body_name = body.GetName()
matched_key = None
for key in keys:
if key in body_name:
matched_key = key
break

if matched_key:
blue_faces = []
for face in body.Faces:
face_selection = FaceSelection.Create(face)
face_color = ColorHelper.GetColor(face_selection)
if is_blue_color(face_color):
blue_faces.append(face)

if blue_faces:
blue_faces_by_geometry[body_name] = {"key": matched_key, "faces": blue_faces}

# Create and assign materials only to blue faces
for geometry_name, data in blue_faces_by_geometry.items():
key = data["key"]
faces = data["faces"]

# Clean geometry name: remove FOP and key, then strip special characters
cleaned_name = geometry_name.replace("FOP", "")
cleaned_name = cleaned_name.replace(key, "")
cleaned_name = re.sub(r"[^A-Za-z0-9_]", "", cleaned_name)

# Create a new Speos material
material = SpeosSim.Material.Create()
material.Name = "FOP_" + cleaned_name
material.OpticalPropertiesType = SpeosSim.Material.EnumOpticalPropertiesType.Surfacic
material.SOPType = SpeosSim.Material.EnumSOPType.Library

fop_rel = FOP[key].get("FOP")
fop_full = resolve_optical_path(fop_rel)
if fop_full:
material.SOPLibrary = fop_full
else:
print("⚠ No valid FOP path for key '{0}'".format(key))

# Create a face selection and assign it to the material
oriented_faces = FaceSelection.Create(faces)
material.OrientedFaces.Set(oriented_faces.Items)

print("Created material '{0}' with {1} blue face(s).".format(material.Name, len(faces)))
Loading
Loading