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
21 changes: 21 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
name: Feature request
about: Suggest new features
title: ''
labels: feature
assignees: jgray-19

---

**Feature requested**
What is DECTSim missing, and why would it be useful.

**Area**
Provided a short indication of where this feature would sit e.g. GUI-image display, simutlation-scatter_correction

**Suggested behaviour**
Detail of the proposed feature.

**Additional context**
Add any other context about the problem here.

8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@
*.slx.r*
*.mdl.r*

#Example binaries
gui/PhantomExample*.mat
gui/PhantomExample*.png
gui/SourceExample*.mat

# MATLAB Compiler output
build/

# Derived content-obscured files
*.p

Expand Down
34 changes: 34 additions & 0 deletions APPLICATION_LICENSE.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
DECTSim Application License

The DECTSim source code is separately available under the BSD 3-Clause License in the accompanying LICENSE file.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

MATHWORKS COMPONENTS

DECTSim includes MATLAB Runtime and other components owned by The MathWorks, Inc. or its licensors. You may use those components only as part of DECTSim and only to run DECTSim. You may not:

- use MATLAB Runtime separately from DECTSim
- use it with another application
- provide it as a service
- redistribute it separately
- claim any right to use or deploy MATLAB or any other MathWorks product.

Use of MATLAB Runtime is also subject to the MATLAB Runtime License, which is incorporated into this licence by reference. It is available at: [MATLAB Runtime installation directory]\R2026a\matlabruntime_license_agreement.pdf and agreement to this is required during DECTSim install.

You must not remove or alter any copyright, trademark, logo, proprietary-rights, disclaimer, or warning notice contained in DECTSim or its MathWorks components.

DECTSim and its MathWorks components are provided “as is,” without warranties of any kind. No warranty is provided for MATLAB Runtime or any other MathWorks program. MathWorks, Inc. and its licensors are excluded from all liability for damages, remedies, claims, losses, or expenses arising from the use of DECTSim or any MathWorks component.




19 changes: 19 additions & 0 deletions NOTICE.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
DECTSim Notices
===============

DECTSim
Copyright (c) 2023, Joshua Gray and Sofia Pearson.

DECTSim source code is distributed under the BSD 3-Clause License.
See the accompanying LICENSE file for the complete terms.

MATLAB®. © 1984 - 2026 The MathWorks, Inc.

The application was created using MATLAB Compiler and requires
MATLAB Runtime.

MATLAB and MATLAB Runtime are products of The MathWorks, Inc.

Use of DECTSim and the MATLAB Runtime components is subject to
the accompanying APPLICATION_LICENSE.txt and the applicable
MATLAB Runtime License.
126 changes: 126 additions & 0 deletions build_windows.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
function build_windows()
%BUILD_WINDOWS Build and package DECTSim as a Windows standalone application.
%
% Requirements:
% - Windows
% - MATLAB R2026a or newer
% - MATLAB Compiler
% - Image Processing Toolbox

rootDir = fileparts(mfilename("fullpath"));
guiDir = fullfile(rootDir, "gui");
srcDir = fullfile(rootDir, "src");
buildRoot = fullfile(rootDir, "build");
applicationOutput = fullfile(buildRoot, "application");
installerOutput = fullfile(buildRoot, "installer");

assert(ispc, ...
"DECTSim:UnsupportedPlatform", ...
"A Windows executable must be built on Windows.");

assert(~isempty(which("compiler.build.standaloneWindowsApplication")), ...
"DECTSim:MissingCompiler", ...
["MATLAB Compiler is not available. " ...
"Install and license MATLAB Compiler before building."]);

assert(~isempty(which("fan2para")) && ~isempty(which("iradon")), ...
"DECTSim:MissingImageProcessingToolbox", ...
["Image Processing Toolbox is not available. " ...
"DECTSim requires it for reconstruction."]);

assert(isfile(fullfile(guiDir, "gui.m")), ...
"DECTSim:MissingGUI", ...
"Could not find gui/gui.m.");

assert(isfolder(srcDir), ...
"DECTSim:MissingSource", ...
"Could not find the src directory.");

% Make all DECTSim classes and functions visible during dependency analysis.
originalPath = path;
pathCleanup = onCleanup(@() path(originalPath));

addpath(guiDir);
addpath(genpath(srcDir));

requiredDataNames = [
"PhantomExample1.mat"
"PhantomExample1.png"
"PhantomExample2.mat"
"PhantomExample1.png"
"PhantomExample3.mat"
"PhantomExample1.png"
"PhantomExample4.mat"
"PhantomExample1.png"
"SourceExample40kvp.mat"
"SourceExample80kvp.mat"
];

requiredDataFiles = fullfile(guiDir, requiredDataNames);

% Generate the bundled example objects when they do not yet exist.
if any(~isfile(requiredDataFiles))
fprintf("Generating missing DECTSim example data...\n");
run(fullfile(guiDir, "ExampleObjects.m"));
end

missingData = requiredDataFiles(~isfile(requiredDataFiles));

if ~isempty(missingData)
error( ...
"DECTSim:MissingExampleData", ...
"The following example files were not generated:\n%s", ...
strjoin(missingData, newline));
end

% Start each build with clean output directories.
if isfolder(buildRoot)
rmdir(buildRoot, "s");
end

mkdir(applicationOutput);
mkdir(installerOutput);

additionalApplicationFiles = [
fullfile(guiDir, "graphics")
fullfile(guiDir, "40kvp.spk")
fullfile(guiDir, "80kvp.spk")
requiredDataFiles(:)
srcDir
];

fprintf("Building the standalone Windows application...\n");

buildResults = compiler.build.standaloneWindowsApplication( ...
fullfile(guiDir, "gui.m"), ...
"ExecutableName", "DECTSim", ...
"ExecutableVersion", "1.0.0.0", ...
"OutputDir", applicationOutput, ...
"AdditionalFiles", additionalApplicationFiles, ...
"AutoDetectDataFiles", "on", ...
"Verbose", "on");

fprintf("Creating the Windows installer...\n");

compiler.package.installer( ...
buildResults, ...
"ApplicationName", "DECTSim", ...
"InstallerName", "DECTSimInstaller", ...
"Version", "1.0.0", ...
"Summary", ...
"Dual-energy computed tomography simulation application.", ...
"OutputDir", installerOutput, ...
"RuntimeDelivery", "web", ...
"AdditionalFiles", [...
fullfile(rootDir, "LICENSE")
fullfile(rootDir, "NOTICE.txt")
fullfile(rootDir, "APPLICATION_LICENSE.txt")
],"Verbose", "on");

fprintf("\nDECTSim build completed successfully.\n");
fprintf("Application output:\n %s\n", applicationOutput);
fprintf("Installer output:\n %s\n", installerOutput);

% Keep the onCleanup object alive until the function completes.
clear pathCleanup
end
74 changes: 74 additions & 0 deletions docs/source/dev_guide/save_phantom_preview.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
Save Phantom Preview
=============

This allows a preview image to be generated when you create a custom phantom. The preview image can then be displayed on the GUI when your phantom is selected.

save_phantom_preview
------------

Purpose
~~~~~~~

The ``save_phantom_preview`` function generates a transparent PNG preview of a voxel phantom.

The function selects the central slice in the z direction and uses :func:`voxel_array.get_object_idxs` to determine which voxel object occupies each position in the slice. The resulting object indices are converted to grayscale values.

Voxels whose object index is equal to :attr:`voxel_array.nobj` represent the world material and are made fully transparent. All voxels belonging to objects within the phantom are made fully opaque.

Arguments
~~~~~~~~~

.. attribute:: phantom
(:class:`voxel_array`) The voxel array from which the preview image is generated.

.. attribute:: output_file
(:class:`string`) The path at which the preview image is saved. The output image is written in PNG format.

Returns

.. attribute:: preview
(:class:`double`) A two-dimensional grayscale image containing the central axial slice of the phantom. The values are normalised to the range ``[0, 1]``.

.. attribute:: alpha
(:class:`double`) A two-dimensional alpha channel with the same dimensions as ``preview``. A value of ``0`` represents a fully transparent background pixel, while a value of ``1`` represents a fully opaque phantom pixel.

Functions
~~~~~~~~~

.. function:: save_phantom_preview(phantom, output_file)

Generates and saves a transparent preview image from the central axial slice of a voxel phantom.

The number of voxels in each dimension is calculated from :attr:`voxel_array.num_planes`. Because ``num_planes`` contains the voxel boundary planes, the number of voxel cells is given by ``num_planes - 1``.

The function constructs the voxel indices for the central x-y slice and passes them to :func:`voxel_array.get_object_idxs`. This returns the index of the voxel object occupying each position.

The object indices are reshaped into a two-dimensional image and normalised to grayscale. Positions containing the world material, identified by :attr:`voxel_array.nobj`, are assigned an alpha value of ``0`` and are therefore transparent in the saved PNG.

:param phantom: The voxel array from which the preview is generated.
:type phantom: :class:`voxel_array`
:param output_file: The path at which the PNG preview is saved.
:type output_file: :class:`string`

:returns: A grayscale preview image and its corresponding alpha channel.
:rtype: tuple(:class:`double`, :class:`double`)


Properties
~~~~~~~~~~
All of these properties are immutable, therefore they cannot be changed after the object is created.

.. attribute:: is_in_object

(:class:`function`) A function that takes a set of points ``(x, y, z)`` and returns a list of logical values indicating if the point is inside the object or not.

.. attribute:: material

(:class:`material_attenuation`) The material of the object.

.. attribute:: get_mu
:noindex:

(:class:`function`) A function that takes an energy and returns the linear attenuation coefficient of the material at that energy.


2 changes: 2 additions & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Welcome to DECTSim's documentation!
:caption: User Guide:
:glob:

user_guide/first_run.md
user_guide/gui.md
user_guide/first_sim.md

Expand All @@ -25,6 +26,7 @@ Welcome to DECTSim's documentation!
dev_guide/detector.rst
dev_guide/materials.rst
dev_guide/voxel_shapes.rst
dev_guide/save_phantom_preview.rst
dev_guide/voxel_array.rst
dev_guide/ray_tracing.rst
dev_guide/sensors.rst
Expand Down
Loading