Skip to content
Open
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
10 changes: 1 addition & 9 deletions core/opengate_core/opengate_lib/GateGenericSource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -683,15 +683,7 @@ void GateGenericSource::InitializeVisualization(py::dict puser_info) {
if (G4Threading::IsWorkerThread())
return;
auto user_info = py::dict(puser_info["visualization"]);
py::object color = user_info["color"];
if (py::isinstance<py::str>(color)) {
std::string color_str = color.cast<std::string>();
G4Colour::GetColour(color_str, fVisColour);
} else {
std::vector<G4double> rgba = color.cast<std::vector<G4double>>();
fVisColour = G4Colour(rgba[0], rgba[1], rgba[2], rgba[3]);
}

fVisColour = DictGetColour(user_info, "color");
fVisSize = DictGetDouble(user_info, "size");
fVisCount = DictGetInt(user_info, "count");
}
Expand Down
38 changes: 37 additions & 1 deletion core/opengate_core/opengate_lib/GateHelpersDict.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,42 @@ DictGetVecG4RotationMatrix(py::dict &user_info, const std::string &key) {
return l;
}

namespace {
G4Colour GetColour(const py::handle &color) {
if (py::isinstance<py::str>(color)) {
G4Colour colour;
const std::string color_str = color.cast<std::string>();
G4Colour::GetColour(color_str, colour);
return colour;
}

const auto rgba = color.cast<std::vector<G4double>>();

if (rgba.size() == 3)
return {rgba[0], rgba[1], rgba[2], 1.0};
return {rgba[0], rgba[1], rgba[2], rgba[3]};
}
} // namespace

G4Colour DictGetColour(py::dict &user_info, const std::string &key) {
return GetColour(user_info[key.c_str()]);
}

std::vector<G4Colour> DictGetVecColour(py::dict &user_info,
const std::string &key) {
std::vector<G4Colour> l;
auto color_list = py::list(user_info[key.c_str()]);
for (const auto color : color_list) {
l.push_back(GetColour(color));
}
return l;
}

std::vector<G4Colour> DictGetColourVec(py::dict &user_info,
const std::string &key) {
return DictGetVecColour(user_info, key);
}

bool IsIn(const std::string &s, std::vector<std::string> &v) {
for (const auto &x : v)
if (x == s)
Expand Down Expand Up @@ -318,4 +354,4 @@ std::string ParamAt(const std::map<std::string, std::string> &param,
Fatal("Cannot find this key in the param list");
}
return param.at(key);
}
}
6 changes: 6 additions & 0 deletions core/opengate_core/opengate_lib/GateHelpersDict.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#ifndef OPENGATE_CORE_OPENGATEHELPERSDICT_H
#define OPENGATE_CORE_OPENGATEHELPERSDICT_H

#include <G4Colour.hh>
#include <G4DataVector.hh>
#include <G4RotationMatrix.hh>
#include <G4ThreeVector.hh>
Expand Down Expand Up @@ -62,6 +63,11 @@ DictGetVecG4RotationMatrix(py::dict &user_info, const std::string &key);
std::vector<G4ThreeVector> DictGetVecG4ThreeVector(py::dict &user_info,
const std::string &key);

G4Colour DictGetColour(py::dict &user_info, const std::string &key);

std::vector<G4Colour> DictGetVecColour(py::dict &user_info,
const std::string &key);

bool IsIn(const std::string &s, std::vector<std::string> &v);

std::map<std::string, std::string> DictToMap(const py::dict &user_info);
Expand Down
5 changes: 4 additions & 1 deletion docs/source/user_guide/user_guide_reference_volumes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ Common parameters are:
respect to the mother volume. We advocate the use of
``scipy.spatial.transform.Rotation`` to manage the rotation matrix.
- ``color``: a list of 4 values (Red, Green, Blue, Opacity) between 0
and 1, e.g. ``[1, 0, 0, 0.5]``. Only used when visualization is on.
and 1, e.g. ``[1, 0, 0, 0.5]`` or a string representing a Geant4 color name,
including ``"white"``, ``"grey"``, ``"gray"``,
``"black"``, ``"brown"``, ``"red"``, ``"green"``, ``"blue"``, ``"cyan"``,
``"magenta"``, or ``"yellow"``. Only used when visualization is on.
- ``style``: forced visualization style for this volume. Can be ``'solid'``,
or ``'wireframe'``. Otherwise or not set, the volume will be displayed
according to global visualization style. Only available with qt.
Expand Down
4 changes: 3 additions & 1 deletion opengate/geometry/volumes.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

from ..base import DynamicGateObject, process_cls
from . import solids
from ..utility import ensure_filename_is_str
from ..utility import ensure_filename_is_str, validate_color
from ..exception import fatal, warning
from ..image import write_itk_image
from ..image import update_image_py_to_cpp
Expand Down Expand Up @@ -164,6 +164,7 @@ class VolumeBase(DynamicGateObject, NodeMixin):
"doc": (
"4 component vector defining the volume's color in visual rendering. "
"The first 3 entries are RBG, the 4th is visible/invisible (1 or 0). "
"Can also use Geant4 color name strings, e.g. 'red', 'blue', 'cyan' etc. "
)
},
),
Expand Down Expand Up @@ -464,6 +465,7 @@ def construct_logical_volume(self):
self.g4_vis_attributes.SetForceWireframe(True)
elif self.style == "solid":
self.g4_vis_attributes.SetForceSolid(True)
self.color = validate_color(self.color, f"Color error for volume {self.name}: ")
self.g4_vis_attributes.SetColor(*self.color)
self.g4_vis_attributes.SetVisibility(bool(self.color[3]))
self.g4_logical_volume.SetVisAttributes(self.g4_vis_attributes)
Expand Down
43 changes: 2 additions & 41 deletions opengate/sources/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from ..base import UserInfoValidatorBase, process_cls
from ..exception import fatal, warning
from ..logger import logger
from ..utility import g4_units
from ..utility import g4_units, validate_color
from .base import SourceBase
from .utility import (
all_beta_plus_radionuclides,
Expand Down Expand Up @@ -270,45 +270,6 @@ class VisualizationValidator(UserInfoValidatorBase):

__schema__ = set(visualization_parameters().keys())

def validate_color(self, color, prefix=""):
valid_color_str = [
"white",
"grey",
"gray",
"black",
"brown",
"red",
"green",
"blue",
"cyan",
"magenta",
"yellow",
]

if isinstance(color, str) and not color in valid_color_str:
fatal(
f"{prefix}Invalid color name '{color}'. Valid color name options are: {valid_color_str}."
)
if isinstance(color, list):
if len(color) > 4 or len(color) < 3:
fatal(
f"{prefix}Color list must have 3 (RGB) or 4 (RGBA) elements. Got {len(color)}."
)
if len(color) == 3:
color.append(1.0) # Add alpha value of 1.0 if only RGB is provided
logger.debug(
f"{prefix}Alpha value of 1.0 is added to the color list since only RGB values are provided."
)
for i, c in enumerate(color):
if not isinstance(c, (int, float, np.number)):
fatal(
f"{prefix}All elements of color list must be numbers. Element {i} is not."
)
if c < 0 or c > 1:
fatal(
f"{prefix}All elements of color list must be in the range [0, 1]. Element {i} is {c}."
)

def validate(self, parent_obj, attr_name: str, parent_context: str = None):
context_name = super().validate(parent_obj, attr_name, parent_context)
b = getattr(parent_obj, attr_name)
Expand All @@ -326,7 +287,7 @@ def validate(self, parent_obj, attr_name: str, parent_context: str = None):
f"For source {parent_obj.name}, visualization size must be in the range (0, 20). Got {b.size}. Using 3 instead."
)
b.size = 3
self.validate_color(b.color, f"For visualization of source {parent_obj.name}: ")
validate_color(b.color, f"For visualization of source {parent_obj.name}: ")
return context_name


Expand Down
2 changes: 1 addition & 1 deletion opengate/tests/src/geometry/test009_voxels_visu.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
patient.image = paths.data / "patient-4mm.mhd"
patient.mother = "fake"
patient.material = "G4_AIR" # material used by default
patient.color = [1, 0, 1, 1]
patient.color = "magenta"
patient.voxel_materials = [
[-2000, -900, "G4_AIR"],
[-900, -100, "Lung"],
Expand Down
40 changes: 40 additions & 0 deletions opengate/utility.py
Original file line number Diff line number Diff line change
Expand Up @@ -608,3 +608,43 @@ def get_basename_and_extension(filename):
extensions.append(ext)
extensions.reverse()
return os.path.basename(base), "".join(extensions)


def validate_color(color, error_prefix=""):
valid_color_dict = {
"white": [1, 1, 1, 1],
"grey": [0.5, 0.5, 0.5, 1],
"gray": [0.5, 0.5, 0.5, 1],
"black": [0, 0, 0, 1],
"brown": [0.45, 0.25, 0, 1],
"red": [1, 0, 0, 1],
"green": [0, 1, 0, 1],
"blue": [0, 0, 1, 1],
"cyan": [0, 1, 1, 1],
"magenta": [1, 0, 1, 1],
"yellow": [1, 1, 0, 1],
}
if isinstance(color, str) and not color in valid_color_dict.keys():
fatal(
f"{error_prefix}Invalid color name '{color}'. Valid color name options are: {valid_color_dict.keys()}."
)
if isinstance(color, str):
color = valid_color_dict[color]
return color
if isinstance(color, list):
if len(color) > 4 or len(color) < 3:
fatal(
f"{error_prefix}Color list must have 3 (RGB) or 4 (RGBA) elements. Got {len(color)}."
)
for i, c in enumerate(color):
if not isinstance(c, (int, float, np.number)):
fatal(
f"{error_prefix}All elements of color list must be numbers. Element {i} is not."
)
if c < 0 or c > 1:
fatal(
f"{error_prefix}All elements of color list must be in the range [0, 1]. Element {i} is {c}."
)
if len(color) == 3:
color.append(1)
return color
Loading