Skip to content

npxpy.preset.Preset

A class to represent a preset with various parameters related to writing and hatching settings.

Attributes:

Name Type Description
id str

Unique identifier for the preset.

name str

Name of the preset.

valid_objectives List[str]

Valid objectives for the preset.

valid_resins List[str]

Valid resins for the preset.

valid_substrates List[str]

Valid substrates for the preset.

writing_speed float

Writing speed.

writing_power float

Writing power.

slicing_spacing float

Slicing spacing.

hatching_spacing float

Hatching spacing.

hatching_angle float

Hatching angle.

hatching_angle_increment float

Hatching angle increment.

hatching_offset float

Hatching offset.

hatching_offset_increment float

Hatching offset increment.

hatching_back_n_forth bool

Whether hatching is back and forth.

mesh_z_offset float

Mesh Z offset.

Source code in npxpy/preset.py
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
class Preset:
    """
    A class to represent a preset with various parameters related to writing
    and hatching settings.

    Attributes:
        id (str): Unique identifier for the preset.
        name (str): Name of the preset.
        valid_objectives (List[str]): Valid objectives for the preset.
        valid_resins (List[str]): Valid resins for the preset.
        valid_substrates (List[str]): Valid substrates for the preset.
        writing_speed (float): Writing speed.
        writing_power (float): Writing power.
        slicing_spacing (float): Slicing spacing.
        hatching_spacing (float): Hatching spacing.
        hatching_angle (float): Hatching angle.
        hatching_angle_increment (float): Hatching angle increment.
        hatching_offset (float): Hatching offset.
        hatching_offset_increment (float): Hatching offset increment.
        hatching_back_n_forth (bool): Whether hatching is back and forth.
        mesh_z_offset (float): Mesh Z offset.
    """

    def __init__(
        self,
        name: str = "25x_IP-n162_default",
        valid_objectives: List[str] = None,
        valid_resins: List[str] = None,
        valid_substrates: List[str] = None,
        writing_speed: float = 250000.0,
        writing_power: float = 50.0,
        slicing_spacing: float = 0.8,
        hatching_spacing: float = 0.3,
        hatching_angle: float = 0.0,
        hatching_angle_increment: float = 0.0,
        hatching_offset: float = 0.0,
        hatching_offset_increment: float = 0.0,
        hatching_back_n_forth: bool = True,
        mesh_z_offset: float = 0.0,
    ):

        # Default lists for valid_objectives, valid_resins, valid_substrates
        self._valid_objectives = None
        self._valid_resins = None
        self._valid_substrates = None

        # Set attributes via setters
        self.name = name
        self.valid_objectives = (
            valid_objectives if valid_objectives else ["25x"]
        )
        self.valid_resins = valid_resins if valid_resins else ["IP-n162"]
        self.valid_substrates = valid_substrates if valid_substrates else ["*"]
        self.writing_speed = writing_speed
        self.writing_power = writing_power
        self.slicing_spacing = slicing_spacing
        self.hatching_spacing = hatching_spacing
        self.hatching_angle = hatching_angle
        self.hatching_angle_increment = hatching_angle_increment
        self.hatching_offset = hatching_offset
        self.hatching_offset_increment = hatching_offset_increment
        self.hatching_back_n_forth = hatching_back_n_forth
        self.mesh_z_offset = mesh_z_offset
        self.grayscale_multilayer_enabled = False
        self.grayscale_layer_profile_nr_layers = 6
        self.grayscale_writing_power_minimum = 0.0
        self.grayscale_exponent = 1.0
        self.id = str(uuid.uuid4())

    # Setters and validation logic for all attributes
    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, value: str):
        value = str(value)
        if not isinstance(value, str) or not value.strip():
            raise ValueError("name must be a non-empty string.")
        self._name = value

    @property
    def valid_objectives(self):
        return self._valid_objectives

    @valid_objectives.setter
    def valid_objectives(self, value):
        valid_objectives_set = {"25x", "63x", "*"}
        if not set(value).issubset(valid_objectives_set):
            raise ValueError(f"Invalid valid_objectives: {value}")
        self._valid_objectives = value

    @property
    def valid_resins(self):
        return self._valid_resins

    @valid_resins.setter
    def valid_resins(self, value):
        valid_resins_set = {
            "IP-PDMS",
            "IPX-S",
            "IP-L",
            "IP-n162",
            "IP-Dip2",
            "IP-Dip",
            "IP-S",
            "IP-Visio",
            "*",
        }
        if not set(value).issubset(valid_resins_set):
            raise ValueError(f"Invalid valid_resins: {value}")
        self._valid_resins = value

    @property
    def valid_substrates(self):
        return self._valid_substrates

    @valid_substrates.setter
    def valid_substrates(self, value):
        valid_substrates_set = {"*", "FuSi", "Si"}
        if not set(value).issubset(valid_substrates_set):
            raise ValueError(f"Invalid valid_substrates: {value}")
        self._valid_substrates = value

    @property
    def writing_speed(self):
        return self._writing_speed

    @writing_speed.setter
    def writing_speed(self, value):
        value = float(value)  # Type coercion
        if value <= 0:
            raise ValueError(
                f"writing_speed must be greater than 0. Got {value}"
            )
        self._writing_speed = value

    @property
    def writing_power(self):
        return self._writing_power

    @writing_power.setter
    def writing_power(self, value):
        value = float(value)  # Type coercion
        if value < 0:
            raise ValueError(
                f"writing_power must be greater or equal to 0. Got {value}"
            )
        self._writing_power = value

    @property
    def slicing_spacing(self):
        return self._slicing_spacing

    @slicing_spacing.setter
    def slicing_spacing(self, value):
        value = float(value)
        if value <= 0:
            raise ValueError(
                f"slicing_spacing must be greater than 0. Got {value}"
            )
        self._slicing_spacing = value

    @property
    def hatching_spacing(self):
        return self._hatching_spacing

    @hatching_spacing.setter
    def hatching_spacing(self, value):
        value = float(value)
        if value <= 0:
            raise ValueError(
                f"hatching_spacing must be greater than 0. Got {value}"
            )
        self._hatching_spacing = value

    @property
    def hatching_angle(self):
        return self._hatching_angle

    @hatching_angle.setter
    def hatching_angle(self, value):
        self._hatching_angle = float(value)

    @property
    def hatching_angle_increment(self):
        return self._hatching_angle_increment

    @hatching_angle_increment.setter
    def hatching_angle_increment(self, value):
        self._hatching_angle_increment = float(value)

    @property
    def hatching_offset(self):
        return self._hatching_offset

    @hatching_offset.setter
    def hatching_offset(self, value):
        self._hatching_offset = float(value)

    @property
    def hatching_offset_increment(self):
        return self._hatching_offset_increment

    @hatching_offset_increment.setter
    def hatching_offset_increment(self, value):
        self._hatching_offset_increment = float(value)

    @property
    def hatching_back_n_forth(self):
        return self._hatching_back_n_forth

    @hatching_back_n_forth.setter
    def hatching_back_n_forth(self, value):
        if not isinstance(value, bool):
            raise ValueError(
                f"hatching_back_n_forth must be a boolean. Got {type(value).__name__}"
            )
        self._hatching_back_n_forth = value

    @property
    def mesh_z_offset(self):
        return self._mesh_z_offset

    @mesh_z_offset.setter
    def mesh_z_offset(self, value):
        self._mesh_z_offset = float(value)

    @property
    def grayscale_layer_profile_nr_layers(self):
        return self._grayscale_layer_profile_nr_layers

    @grayscale_layer_profile_nr_layers.setter
    def grayscale_layer_profile_nr_layers(self, value):
        value = float(value)
        if value < 0:
            raise ValueError(
                f"grayscale_layer_profile_nr_layers must be greater or equal to 0. Got {value}"
            )
        self._grayscale_layer_profile_nr_layers = value

    @property
    def grayscale_writing_power_minimum(self):
        return self._grayscale_writing_power_minimum

    @grayscale_writing_power_minimum.setter
    def grayscale_writing_power_minimum(self, value):
        value = float(value)
        if value < 0:
            raise ValueError(
                f"grayscale_writing_power_minimum must be greater or equal to 0. Got {value}"
            )
        self._grayscale_writing_power_minimum = value

    @property
    def grayscale_exponent(self):
        return self._grayscale_exponent

    @grayscale_exponent.setter
    def grayscale_exponent(self, value):
        value = float(value)
        if value <= 0:
            raise ValueError(
                f"grayscale_exponent must be greater than 0. Got {value}"
            )
        self._grayscale_exponent = value

    def set_grayscale_multilayer(
        self,
        grayscale_layer_profile_nr_layers: float = 6.0,
        grayscale_writing_power_minimum: float = 0.0,
        grayscale_exponent: float = 1.0,
    ) -> "Preset":
        """
        Enable grayscale multilayer and set the related attributes.
        grayscale_layer_profile_nr_layers (float): Number of layers for
            grayscale layer profile.
        grayscale_writing_power_minimum (float): Minimum writing power for
            grayscale.
        grayscale_exponent (float): Grayscale exponent.
        """
        self.grayscale_layer_profile_nr_layers = (
            grayscale_layer_profile_nr_layers
        )
        self.grayscale_writing_power_minimum = grayscale_writing_power_minimum
        self.grayscale_exponent = grayscale_exponent
        self.grayscale_multilayer_enabled = True
        return self

    def duplicate(self) -> "Preset":
        """
        Create a duplicate of the current preset instance.

        Returns:
            Preset: A duplicate of the current preset instance.
        """
        duplicate = copy.copy(self)
        duplicate.id = str(uuid.uuid4())
        return duplicate

    @classmethod
    def load_single(cls, file_path: str, fresh_id: bool = True) -> "Preset":
        """
        Load a single preset from a valid .toml file containing
        preset data only.

        Parameters:
            file_path (str): The path to the .toml file.
            fresh_id (bool): Whether to assign a fresh ID to the loaded preset.

        Returns:
            Preset: The loaded preset instance.

        Raises:
            FileNotFoundError: If the file at file_path does not exist.
            toml.TomlDecodeError: If there is an error decoding the TOML file.
        """
        if not os.path.isfile(file_path):
            raise FileNotFoundError(f"File not found: {file_path}")

        with open(file_path, "r") as toml_file:
            data = toml.load(toml_file)

        # Create a new Preset instance using the setters
        try:
            instance = cls(
                name=data.get(
                    "name", os.path.splitext(os.path.basename(file_path))[0]
                ),
                valid_objectives=data.get("valid_objectives", ["25x"]),
                valid_resins=data.get("valid_resins", ["IP-n162"]),
                valid_substrates=data.get("valid_substrates", ["*"]),
                writing_speed=data.get("writing_speed", 250000.0),
                writing_power=data.get("writing_power", 50.0),
                slicing_spacing=data.get("slicing_spacing", 0.8),
                hatching_spacing=data.get("hatching_spacing", 0.3),
                hatching_angle=data.get("hatching_angle", 0.0),
                hatching_angle_increment=data.get(
                    "hatching_angle_increment", 0.0
                ),
                hatching_offset=data.get("hatching_offset", 0.0),
                hatching_offset_increment=data.get(
                    "hatching_offset_increment", 0.0
                ),
                hatching_back_n_forth=data.get("hatching_back_n_forth", True),
                mesh_z_offset=data.get("mesh_z_offset", 0.0),
            )

            instance.grayscale_multilayer_enabled = data.get(
                "grayscale_multilayer_enabled", False
            )
            instance.grayscale_layer_profile_nr_layers = data.get(
                "grayscale_layer_profile_nr_layers", 6
            )
            instance.grayscale_writing_power_minimum = data.get(
                "grayscale_writing_power_minimum", 0.0
            )
            instance.grayscale_exponent = data.get("grayscale_exponent", 1.0)

        except Exception as e:
            raise ValueError(
                f"Error creating Preset from file {file_path}: {e}"
            )

        # Optionally assign a new ID if fresh_id is True
        if not fresh_id:
            instance.id = data.get("id", instance.id)

        return instance

    @classmethod
    def load_multiple(
        cls,
        directory_path: str,
        print_names: bool = False,
        fresh_id: bool = True,
    ) -> List["Preset"]:
        """
        Load multiple presets from a directory containing .toml files.

        Parameters:
            directory_path (str): The path to the directory containing .toml files.
            print_names (bool): If True, print the names of the files in the order they are loaded.
            fresh_id (bool): Whether to assign fresh IDs to the loaded presets.

        Returns:
            List[Preset]: A list of loaded preset instances.

        Raises:
            FileNotFoundError: If the directory_path does not exist.
        """
        if not os.path.isdir(directory_path):
            raise FileNotFoundError(f"Directory not found: {directory_path}")

        presets = []
        for file_name in sorted(os.listdir(directory_path)):
            if file_name.endswith(".toml"):
                file_path = os.path.join(directory_path, file_name)
                preset = cls.load_single(file_path, fresh_id)
                presets.append(preset)
                if print_names:
                    print(file_name)
        return presets

    def to_dict(self) -> Dict[str, Any]:
        """
        Convert the preset to a dictionary format.

        Returns:
            Dict[str, Any]: Dictionary representation of the preset, including
                            attributes starting with '_', but excluding attributes with '__'.
                            Leading '_' is removed from keys in the resulting dictionary.
        """
        preset_dict = {}

        for attr_name, attr_value in self.__dict__.items():
            # Skip attributes that start with two underscores
            # Ensures functional backend implementations via self.__interals
            if attr_name.startswith("__"):
                continue

            # Remove leading single underscore if present
            if attr_name.startswith("_"):
                key = attr_name[1:]  # Remove the leading '_'
            else:
                key = attr_name

            preset_dict[key] = attr_value

        return preset_dict

    def export(self, file_path: str = None) -> None:
        """
        Export the preset to a file that can be loaded by nanoPrintX and/or npxpy.

        Parameters:
            file_path (str): The path to the .toml file to be created. If not provided,
                             defaults to the current directory with the preset's name.

        Raises:
            IOError: If there is an error writing to the file.
        """
        if file_path is None:
            file_path = f"{self.name}.toml"
        elif not file_path.endswith(".toml"):
            file_path += ".toml"

        data = self.to_dict()

        with open(file_path, "w") as toml_file:
            toml.dump(data, toml_file)

duplicate()

Create a duplicate of the current preset instance.

Returns:

Name Type Description
Preset Preset

A duplicate of the current preset instance.

Source code in npxpy/preset.py
309
310
311
312
313
314
315
316
317
318
def duplicate(self) -> "Preset":
    """
    Create a duplicate of the current preset instance.

    Returns:
        Preset: A duplicate of the current preset instance.
    """
    duplicate = copy.copy(self)
    duplicate.id = str(uuid.uuid4())
    return duplicate

export(file_path=None)

Export the preset to a file that can be loaded by nanoPrintX and/or npxpy.

Parameters:

Name Type Description Default
file_path str

The path to the .toml file to be created. If not provided, defaults to the current directory with the preset's name.

None

Raises:

Type Description
IOError

If there is an error writing to the file.

Source code in npxpy/preset.py
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
def export(self, file_path: str = None) -> None:
    """
    Export the preset to a file that can be loaded by nanoPrintX and/or npxpy.

    Parameters:
        file_path (str): The path to the .toml file to be created. If not provided,
                         defaults to the current directory with the preset's name.

    Raises:
        IOError: If there is an error writing to the file.
    """
    if file_path is None:
        file_path = f"{self.name}.toml"
    elif not file_path.endswith(".toml"):
        file_path += ".toml"

    data = self.to_dict()

    with open(file_path, "w") as toml_file:
        toml.dump(data, toml_file)

load_multiple(directory_path, print_names=False, fresh_id=True) classmethod

Load multiple presets from a directory containing .toml files.

Parameters:

Name Type Description Default
directory_path str

The path to the directory containing .toml files.

required
print_names bool

If True, print the names of the files in the order they are loaded.

False
fresh_id bool

Whether to assign fresh IDs to the loaded presets.

True

Returns:

Type Description
List[Preset]

List[Preset]: A list of loaded preset instances.

Raises:

Type Description
FileNotFoundError

If the directory_path does not exist.

Source code in npxpy/preset.py
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
@classmethod
def load_multiple(
    cls,
    directory_path: str,
    print_names: bool = False,
    fresh_id: bool = True,
) -> List["Preset"]:
    """
    Load multiple presets from a directory containing .toml files.

    Parameters:
        directory_path (str): The path to the directory containing .toml files.
        print_names (bool): If True, print the names of the files in the order they are loaded.
        fresh_id (bool): Whether to assign fresh IDs to the loaded presets.

    Returns:
        List[Preset]: A list of loaded preset instances.

    Raises:
        FileNotFoundError: If the directory_path does not exist.
    """
    if not os.path.isdir(directory_path):
        raise FileNotFoundError(f"Directory not found: {directory_path}")

    presets = []
    for file_name in sorted(os.listdir(directory_path)):
        if file_name.endswith(".toml"):
            file_path = os.path.join(directory_path, file_name)
            preset = cls.load_single(file_path, fresh_id)
            presets.append(preset)
            if print_names:
                print(file_name)
    return presets

load_single(file_path, fresh_id=True) classmethod

Load a single preset from a valid .toml file containing preset data only.

Parameters:

Name Type Description Default
file_path str

The path to the .toml file.

required
fresh_id bool

Whether to assign a fresh ID to the loaded preset.

True

Returns:

Name Type Description
Preset Preset

The loaded preset instance.

Raises:

Type Description
FileNotFoundError

If the file at file_path does not exist.

TomlDecodeError

If there is an error decoding the TOML file.

Source code in npxpy/preset.py
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
@classmethod
def load_single(cls, file_path: str, fresh_id: bool = True) -> "Preset":
    """
    Load a single preset from a valid .toml file containing
    preset data only.

    Parameters:
        file_path (str): The path to the .toml file.
        fresh_id (bool): Whether to assign a fresh ID to the loaded preset.

    Returns:
        Preset: The loaded preset instance.

    Raises:
        FileNotFoundError: If the file at file_path does not exist.
        toml.TomlDecodeError: If there is an error decoding the TOML file.
    """
    if not os.path.isfile(file_path):
        raise FileNotFoundError(f"File not found: {file_path}")

    with open(file_path, "r") as toml_file:
        data = toml.load(toml_file)

    # Create a new Preset instance using the setters
    try:
        instance = cls(
            name=data.get(
                "name", os.path.splitext(os.path.basename(file_path))[0]
            ),
            valid_objectives=data.get("valid_objectives", ["25x"]),
            valid_resins=data.get("valid_resins", ["IP-n162"]),
            valid_substrates=data.get("valid_substrates", ["*"]),
            writing_speed=data.get("writing_speed", 250000.0),
            writing_power=data.get("writing_power", 50.0),
            slicing_spacing=data.get("slicing_spacing", 0.8),
            hatching_spacing=data.get("hatching_spacing", 0.3),
            hatching_angle=data.get("hatching_angle", 0.0),
            hatching_angle_increment=data.get(
                "hatching_angle_increment", 0.0
            ),
            hatching_offset=data.get("hatching_offset", 0.0),
            hatching_offset_increment=data.get(
                "hatching_offset_increment", 0.0
            ),
            hatching_back_n_forth=data.get("hatching_back_n_forth", True),
            mesh_z_offset=data.get("mesh_z_offset", 0.0),
        )

        instance.grayscale_multilayer_enabled = data.get(
            "grayscale_multilayer_enabled", False
        )
        instance.grayscale_layer_profile_nr_layers = data.get(
            "grayscale_layer_profile_nr_layers", 6
        )
        instance.grayscale_writing_power_minimum = data.get(
            "grayscale_writing_power_minimum", 0.0
        )
        instance.grayscale_exponent = data.get("grayscale_exponent", 1.0)

    except Exception as e:
        raise ValueError(
            f"Error creating Preset from file {file_path}: {e}"
        )

    # Optionally assign a new ID if fresh_id is True
    if not fresh_id:
        instance.id = data.get("id", instance.id)

    return instance

set_grayscale_multilayer(grayscale_layer_profile_nr_layers=6.0, grayscale_writing_power_minimum=0.0, grayscale_exponent=1.0)

Enable grayscale multilayer and set the related attributes. grayscale_layer_profile_nr_layers (float): Number of layers for grayscale layer profile. grayscale_writing_power_minimum (float): Minimum writing power for grayscale. grayscale_exponent (float): Grayscale exponent.

Source code in npxpy/preset.py
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
def set_grayscale_multilayer(
    self,
    grayscale_layer_profile_nr_layers: float = 6.0,
    grayscale_writing_power_minimum: float = 0.0,
    grayscale_exponent: float = 1.0,
) -> "Preset":
    """
    Enable grayscale multilayer and set the related attributes.
    grayscale_layer_profile_nr_layers (float): Number of layers for
        grayscale layer profile.
    grayscale_writing_power_minimum (float): Minimum writing power for
        grayscale.
    grayscale_exponent (float): Grayscale exponent.
    """
    self.grayscale_layer_profile_nr_layers = (
        grayscale_layer_profile_nr_layers
    )
    self.grayscale_writing_power_minimum = grayscale_writing_power_minimum
    self.grayscale_exponent = grayscale_exponent
    self.grayscale_multilayer_enabled = True
    return self

to_dict()

Convert the preset to a dictionary format.

Returns:

Type Description
Dict[str, Any]

Dict[str, Any]: Dictionary representation of the preset, including attributes starting with '', but excluding attributes with '__'. Leading '' is removed from keys in the resulting dictionary.

Source code in npxpy/preset.py
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
def to_dict(self) -> Dict[str, Any]:
    """
    Convert the preset to a dictionary format.

    Returns:
        Dict[str, Any]: Dictionary representation of the preset, including
                        attributes starting with '_', but excluding attributes with '__'.
                        Leading '_' is removed from keys in the resulting dictionary.
    """
    preset_dict = {}

    for attr_name, attr_value in self.__dict__.items():
        # Skip attributes that start with two underscores
        # Ensures functional backend implementations via self.__interals
        if attr_name.startswith("__"):
            continue

        # Remove leading single underscore if present
        if attr_name.startswith("_"):
            key = attr_name[1:]  # Remove the leading '_'
        else:
            key = attr_name

        preset_dict[key] = attr_value

    return preset_dict