"""GUI-enabled vector properties with Wolf_Param dialog and textures."""
from __future__ import annotations
import logging
import wx
from pathlib import Path
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from ._vector import vector
from ..PyTranslate import _
from ..wolf_texture import genericImagetexture
from ..color_constants import getRGBfromI, getIfromRGB
from ..PyParams import Wolf_Param, Type_Param, new_json
from ._models import vectorpropertiesModel, VectorOGLRenderer
from .polygon_pbr_material import (
PolygonPBRMaterial,
get_builtin_polygon_pbr_presets,
get_official_polygon_pbr_preset_names,
)
[docs]
class vectorproperties(vectorpropertiesModel):
"""GUI-enabled vector properties with Wolf_Param dialog and image textures.
Inherits all data storage, I/O, and color helpers from
:class:`vectorpropertiesModel`. Adds ``Wolf_Param`` property
dialogs, ``genericImagetexture`` management, and wxPython
interaction.
"""
[docs]
myprops: Wolf_Param = None
def __init__(self, lines: list[str] = None, parent: "vector" = None) -> None:
"""Initialise GUI-enabled vector properties.
:param lines: Raw text lines to parse (from a .vec file).
:param parent: The parent ``vector`` owning these properties.
"""
if lines is None:
lines = []
super().__init__(lines=lines, parent=parent)
# GUI-specific attribute: texture holder for attached images
[docs]
self.textureimage: genericImagetexture = None
@property
[docs]
def image_path(self) -> Path:
"""Path to the attached image file."""
return super().image_path
@image_path.setter
def image_path(self, value: Path):
"""Set the attached image path, unloading the old one if changed."""
if self.attachedimage != value:
self.unload_image()
self.attachedimage = Path(value)
[docs]
def load_unload_image(self):
""" Load an attached image """
if self.imagevisible:
if self.attachedimage is not None:
self.attachedimage = Path(self.attachedimage)
if self.attachedimage.exists():
(xmin,xmax),(ymin,ymax) = self.parent.get_bounds_xx_yy()
if self.textureimage is not None:
if self.textureimage.imageFile == self.attachedimage:
self.textureimage.drawing_scale = self.image_scale
self.textureimage.offset = (self.image_relative_posx, self.image_relative_posy)
self.textureimage.xmin = xmin
self.textureimage.xmax = xmax
self.textureimage.ymin = ymin
self.textureimage.ymax = ymax
return
else:
self.textureimage.unload()
self.textureimage = genericImagetexture(which = 'attachedfile',
label=self.parent.myname,
mapviewer= self.parent.get_mapviewer(),
imageFile=self.attachedimage,
xmin=xmin,
xmax=xmax,
ymin=ymin,
ymax=ymax,
drawing_scale= self.image_scale,
offset = (self.image_relative_posx, self.image_relative_posy))
else:
logging.warning('Image not found : {}'.format(self.attachedimage))
else:
self.unload_image()
[docs]
def _get_prop(self, props: Wolf_Param, section: str, key: str, default=None):
"""Get a property value from ``Wolf_Param`` with a fallback default."""
try:
value = props[(section, key)]
return default if value is None else value
except Exception:
return default
[docs]
def _parse_width_profile(self, raw) -> list | None:
"""Convert a width profile string to a list or ``None``.
Delegates to the base-class implementation on
:class:`~wolfhece.pyvertexvectors._models.vectorpropertiesModel`.
See that method for the supported dense and sparse formats.
"""
return super()._parse_width_profile(raw)
[docs]
def _convert_alignment_to_int(self, align: str, default: int = 1) -> int:
"""Convert alignment text to an integer enum used by Wolf_Param."""
if not isinstance(align, str):
return default
align = align.lower()
if align == 'left':
return 1
if align == 'center':
return 2
if align == 'right':
return 3
return default
[docs]
def _convert_int_to_alignment(self, value: int, default: str = 'left') -> str:
"""Convert integer enum value to alignment text."""
if value == 1:
return 'left'
if value == 2:
return 'center'
if value == 3:
return 'right'
return default
[docs]
def _is_shader_rendering_active(self) -> bool:
"""Return True when the parent zone currently renders vectors with shaders."""
zone = getattr(self.parent, 'parentzone', None)
if zone is None:
return False
return getattr(zone, 'rendering_machine', VectorOGLRenderer.LIST) == VectorOGLRenderer.SHADER
[docs]
def _remove_shader_ui_params(self):
"""Remove shader-only parameters from the properties UI dictionaries."""
if self.myprops is None:
return
to_remove = {
'Draw': [
'Width in pixels', 'Width profile',
'Join style', 'Join size', 'Join size mode',
'Fill color', 'Contour enabled', 'Contour color', 'Contour width',
],
'Draw Glow': [
'Glow enabled', 'Glow width', 'Glow color', 'Glow alpha',
],
'Fill material': [
'Mode', 'Preset', 'Albedo texture', 'Normal texture', 'ORM texture', 'Emissive texture',
'Base color factor', 'Metallic factor', 'Roughness factor', 'Normal scale',
'Occlusion strength', 'Emissive factor', 'UV scale X [m/repeat]',
'UV scale Y [m/repeat]', 'UV offset X [m]', 'UV offset Y [m]',
'Cushion strength',
],
'Animation': [
'Animation mode', 'Animation speed',
],
'Fill animation': [
'Mode', 'Speed',
],
'Line style': [
'Dash enabled', 'Dash length', 'Gap length',
],
'Legend': [
'Glow enabled', 'Glow width', 'Glow color', 'Glow alpha',
'Animation mode', 'Animation speed', 'Smoothing', 'Alignment', 'Line spacing',
],
'Text along': [
'Enabled', 'Text', 'Offset', 'Perpendicular offset', 'Alignment', 'Font size', 'Height', 'Priority',
],
'Tracking label': [
'Enabled', 'Snap radius', 'Font size', 'Height', 'Priority',
],
}
for curdict in (self.myprops.myparams, self.myprops.myparams_default):
for group, keys in to_remove.items():
if group not in curdict:
continue
for key in keys:
curdict[group].pop(key, None)
if len(curdict[group]) == 0:
curdict.pop(group, None)
[docs]
def _ensure_props_schema_matches_renderer(self):
"""Rebuild the parameter schema when shader/list rendering mode changes."""
if self.myprops is None:
return
shader_mode = self._is_shader_rendering_active()
if self._shader_ui_mode is None:
self._shader_ui_mode = shader_mode
return
if self._shader_ui_mode != shader_mode:
# Rebuild defaults/active dictionaries to restore or remove shader keys cleanly.
self.myprops.Clear()
self._defaultprop()
self._shader_ui_mode = shader_mode
[docs]
def unload_image(self):
""" Unload the attached image """
if self.textureimage is not None:
self.textureimage.unload()
self.textureimage = None
[docs]
def fill_property(self, props:Wolf_Param = None, updateOGL:bool = True):
"""Apply values from the properties dialog.
Called when the user presses *Apply* in the properties UI.
:param props: Property container; defaults to ``self.myprops``.
:param updateOGL: If True, refresh the OpenGL display list.
"""
import logging
logging.debug('VectorProperties.fill_property() called for: %s', self.parent.myname)
if props is None:
props = self.myprops
old_filled = self.filled
old_width_profile = self.width_profile
old_join = (self.join_style, self.join_size, self.join_size_mode)
self.color = getIfromRGB(self._get_prop(props, 'Draw', 'Color', getRGBfromI(self.color)))
self.width = self._get_prop(props, 'Draw', 'Width', self.width)
# self.style = props[('Draw','Style')]
old_closed = self.closed
self.closed = self._get_prop(props, 'Draw', 'Closed', self.closed)
if old_closed != self.closed:
self.parent._on_vertices_changed()
self.filled = self._get_prop(props, 'Draw', 'Filled', self.filled)
raw_fc = self._get_prop(props, 'Draw', 'Fill color', (-1, -1, -1))
main_rgb = getRGBfromI(self.color)
if raw_fc == (-1, -1, -1) or raw_fc[0] == -1:
self.fill_color = None
elif tuple(raw_fc[:3]) == tuple(main_rgb[:3]):
# Keep inheritance semantics when the user leaves fill colour equal to main colour.
self.fill_color = None
else:
self.fill_color = getIfromRGB(raw_fc)
self.contour_enabled = self._get_prop(props, 'Draw', 'Contour enabled', self.contour_enabled)
raw_cc = self._get_prop(props, 'Draw', 'Contour color', (-1, -1, -1))
if raw_cc == (-1, -1, -1) or raw_cc[0] == -1:
self.contour_color = None
elif tuple(raw_cc[:3]) == tuple(main_rgb[:3]):
# Keep inheritance semantics when contour colour matches main colour.
self.contour_color = None
else:
self.contour_color = getIfromRGB(raw_cc)
self.contour_width = self._get_prop(props, 'Draw', 'Contour width', self.contour_width)
self.transparent = self._get_prop(props, 'Draw', 'Transparent', self.transparent)
self.alpha = self._get_prop(props, 'Draw', 'Alpha', self.alpha)
# self.flash = props[('Draw','Flash')]
self.plot_indices = self._get_prop(props, 'Draw', 'Plot indices if active', self.plot_indices)
self.plot_lengths = self._get_prop(props, 'Draw', 'Plot lengths if active', self.plot_lengths)
self.dash_enabled = self._get_prop(props, 'Line style', 'Dash enabled', self.dash_enabled)
self.dash_length = self._get_prop(props, 'Line style', 'Dash length', self.dash_length)
self.gap_length = self._get_prop(props, 'Line style', 'Gap length', self.gap_length)
self.glow_enabled = self._get_prop(props, 'Draw Glow', 'Glow enabled', self.glow_enabled)
self.glow_width = self._get_prop(props, 'Draw Glow', 'Glow width', self.glow_width)
glow_rgb = self._get_prop(props, 'Draw Glow', 'Glow color',
tuple(int(c * 255) for c in self.glow_color[:3]))
glow_a = self._get_prop(props, 'Draw Glow', 'Glow alpha', self.glow_color[3])
self.glow_color = (glow_rgb[0] / 255., glow_rgb[1] / 255., glow_rgb[2] / 255., float(glow_a))
self.anim_mode = self._get_prop(props, 'Animation', 'Animation mode', self.anim_mode)
self.anim_speed = self._get_prop(props, 'Animation', 'Animation speed', self.anim_speed)
self.fill_anim_mode = self._get_prop(props, 'Fill animation', 'Mode', self.fill_anim_mode)
self.fill_anim_speed = self._get_prop(props, 'Fill animation', 'Speed', self.fill_anim_speed)
self.fill_anim_center_index = self._get_prop(props, 'Fill animation', 'Center vertex index', self.fill_anim_center_index)
self.fill_anim_start_angle = self._get_prop(props, 'Fill animation', 'Start angle [deg]', self.fill_anim_start_angle)
self.fill_anim_center_preview = self._get_prop(props, 'Fill animation', 'Show center preview', self.fill_anim_center_preview)
try:
material_mode = int(self._get_prop(props, 'Fill material', 'Mode', 0) or 0)
logging.debug('fill_property: material_mode=%d', material_mode)
material = getattr(self, 'fill_pbr_material', None)
logging.debug('fill_property: existing material=%s', material is not None)
if material is None:
material = PolygonPBRMaterial()
preset_catalog = get_builtin_polygon_pbr_presets()
preset_names = list(preset_catalog.keys())
preset_raw = int(self._get_prop(props, 'Fill material', 'Preset', -1) or -1)
logging.debug('fill_property: preset_raw=%d, total_presets=%d', preset_raw, len(preset_names))
if 1 <= preset_raw <= len(preset_names):
preset_name = preset_names[preset_raw - 1]
preset_mat = preset_catalog[preset_name]
logging.debug('fill_property: loading preset=%s', preset_name)
# Keep alpha from the currently edited material so transparency remains user-driven.
alpha_keep = material.base_color_factor[3]
material = PolygonPBRMaterial.from_dict(preset_mat.to_dict())
material.base_color_factor = (
material.base_color_factor[0],
material.base_color_factor[1],
material.base_color_factor[2],
alpha_keep,
)
material.enabled = True
material.enabled = material_mode == 1
# When a preset has just been loaded, its values must prevail
# over the UI fields which still contain stale defaults.
# Only read UI overrides when NO preset was selected (Custom).
preset_loaded = 1 <= preset_raw <= len(preset_names)
# Only override preset textures when the user provides a non-empty path.
# Empty strings from the UI must NOT wipe out preset textures.
_ui_albedo = str(self._get_prop(props, 'Fill material', 'Albedo texture', '') or '')
_ui_normal = str(self._get_prop(props, 'Fill material', 'Normal texture', '') or '')
_ui_orm = str(self._get_prop(props, 'Fill material', 'ORM texture', '') or '')
_ui_emiss = str(self._get_prop(props, 'Fill material', 'Emissive texture', '') or '')
if _ui_albedo:
material.albedo_texture = _ui_albedo
if _ui_normal:
material.normal_texture = _ui_normal
if _ui_orm:
material.orm_texture = _ui_orm
if _ui_emiss:
material.emissive_texture = _ui_emiss
if not preset_loaded:
# Custom mode: read all parameters from the UI fields.
raw_bcf = self._get_prop(
props,
'Fill material',
'Base color factor',
tuple(int(c * 255) for c in material.base_color_factor[:3]),
)
material.base_color_factor = (
float(raw_bcf[0]) / 255.,
float(raw_bcf[1]) / 255.,
float(raw_bcf[2]) / 255.,
material.base_color_factor[3],
)
material.metallic_factor = float(self._get_prop(props, 'Fill material', 'Metallic factor', material.metallic_factor))
material.roughness_factor = float(self._get_prop(props, 'Fill material', 'Roughness factor', material.roughness_factor))
material.normal_scale = float(self._get_prop(props, 'Fill material', 'Normal scale', material.normal_scale))
material.occlusion_strength = float(self._get_prop(props, 'Fill material', 'Occlusion strength', material.occlusion_strength))
raw_ef = self._get_prop(
props,
'Fill material',
'Emissive factor',
tuple(int(c * 255) for c in material.emissive_factor),
)
material.emissive_factor = (
float(raw_ef[0]) / 255.,
float(raw_ef[1]) / 255.,
float(raw_ef[2]) / 255.,
)
material.uv_scale = (
float(self._get_prop(props, 'Fill material', 'UV scale X [m/repeat]', material.uv_scale[0])),
float(self._get_prop(props, 'Fill material', 'UV scale Y [m/repeat]', material.uv_scale[1])),
)
material.uv_offset = (
float(self._get_prop(props, 'Fill material', 'UV offset X [m]', material.uv_offset[0])),
float(self._get_prop(props, 'Fill material', 'UV offset Y [m]', material.uv_offset[1])),
)
logging.debug('fill_property: material params - metallic=%.2f, roughness=%.2f (preset_loaded=%s)',
material.metallic_factor, material.roughness_factor, preset_loaded)
# Cushion is always read from the UI (independent of preset)
material.cushion_strength = float(self._get_prop(props, 'Fill material', 'Cushion strength', material.cushion_strength))
if preset_raw == -1:
material.preset_name = ''
elif 1 <= preset_raw <= len(preset_names):
material.preset_name = preset_names[preset_raw - 1]
# DEBUG: Log material state being saved
logging.debug(
'VectorProperties.fill_property: Saving material - enabled=%s, mode=%d, preset=%s, metallic=%.2f',
material.enabled, material_mode, material.preset_name, material.metallic_factor
)
self.fill_pbr_material = material
except Exception as e:
logging.error('ERROR in fill_property material handling: %s', e, exc_info=True)
raw_width_in_pixels = self._get_prop(props, 'Draw', 'Width in pixels', self.width_in_pixels)
if isinstance(raw_width_in_pixels, str):
self.width_in_pixels = raw_width_in_pixels.strip().lower() in ('1', 'true', 'yes', 'on')
else:
self.width_in_pixels = bool(raw_width_in_pixels)
self.width_profile = self._parse_width_profile(self._get_prop(props, 'Draw', 'Width profile', self.width_profile))
self.join_style = self._get_prop(props, 'Draw', 'Join style', self.join_style)
self.join_size = self._get_prop(props, 'Draw', 'Join size', self.join_size)
self.join_size_mode = self._get_prop(props, 'Draw', 'Join size mode', self.join_size_mode)
# VBO data must be rebuilt when line/fill topology or width profile changes.
# Without this explicit invalidation, editing shader-related properties in the
# dialog may appear to have no effect until another action rebuilds caches.
if (
old_filled != self.filled
or old_width_profile != self.width_profile
or old_join != (self.join_style, self.join_size, self.join_size_mode)
):
self.parent._vbo_cache = None
self.parent._vbo_vertex_count = 0
self.parent._fill_vbo_cache = None
self.parent._fill_vbo_vertex_count = 0
if hasattr(self.parent, '_vbo_fillet_key'):
self.parent._vbo_fillet_key = None
self.legendunderlined = self._get_prop(props, 'Legend', 'Underlined', self.legendunderlined)
self.legendbold = self._get_prop(props, 'Legend', 'Bold', self.legendbold)
self.legendfontname = self._convert_int2fontname(self._get_prop(props, 'Legend', 'Font name', self._convert_fontname2int(self.legendfontname)))
self.legendfontsize = self._get_prop(props, 'Legend', 'Font size', self.legendfontsize)
self.legendcolor = getIfromRGB(self._get_prop(props, 'Legend', 'Color', getRGBfromI(self.legendcolor)))
self.legenditalic = self._get_prop(props, 'Legend', 'Italic', self.legenditalic)
self.legendrelpos = self._get_prop(props, 'Legend', 'Relative position', self.legendrelpos)
text = self._get_prop(props, 'Legend', 'Text', self.legendtext)
self.legendvisible = self._get_prop(props, 'Legend', 'Visible', self.legendvisible)
self.legendlength = self._get_prop(props, 'Legend', 'Length', self.legendlength)
self.legendheight = self._get_prop(props, 'Legend', 'Height', self.legendheight)
self.legendpriority = self._get_prop(props, 'Legend', 'Priority', self.legendpriority)
self.legendorientation = self._get_prop(props, 'Legend', 'Orientation', self.legendorientation)
self.legend_glow_enabled = self._get_prop(props, 'Legend', 'Glow enabled', self.legend_glow_enabled)
self.legend_glow_width = self._get_prop(props, 'Legend', 'Glow width', self.legend_glow_width)
lg_rgb = self._get_prop(props, 'Legend', 'Glow color',
tuple(int(c * 255) for c in self.legend_glow_color[:3]))
lg_a = self._get_prop(props, 'Legend', 'Glow alpha', self.legend_glow_color[3])
self.legend_glow_color = (lg_rgb[0] / 255., lg_rgb[1] / 255., lg_rgb[2] / 255., float(lg_a))
self.legend_anim_mode = self._get_prop(props, 'Legend', 'Animation mode', self.legend_anim_mode)
self.legend_anim_speed = self._get_prop(props, 'Legend', 'Animation speed', self.legend_anim_speed)
self.legend_smoothing = self._get_prop(props, 'Legend', 'Smoothing', self.legend_smoothing)
self.legend_alignment = self._convert_int_to_alignment(
self._get_prop(props, 'Legend', 'Alignment', self._convert_alignment_to_int(self.legend_alignment)),
default=self.legend_alignment if isinstance(self.legend_alignment, str) else 'left',
)
self.legend_line_spacing = self._get_prop(props, 'Legend', 'Line spacing', self.legend_line_spacing)
self.text_along_enabled = self._get_prop(props, 'Text along', 'Enabled', self.text_along_enabled)
self.text_along_text = self._get_prop(props, 'Text along', 'Text', self.text_along_text)
self.text_along_offset = self._get_prop(props, 'Text along', 'Offset', self.text_along_offset)
self.text_along_perp = self._get_prop(props, 'Text along', 'Perpendicular offset', self.text_along_perp)
self.text_along_alignment = self._convert_int_to_alignment(
self._get_prop(props, 'Text along', 'Alignment', self._convert_alignment_to_int(self.text_along_alignment, default=2)),
default=self.text_along_alignment if isinstance(self.text_along_alignment, str) else 'center',
)
self.text_along_font_size = self._get_prop(props, 'Text along', 'Font size', self.text_along_font_size)
self.text_along_world_width = self._get_prop(props, 'Text along', 'Length', self.text_along_world_width)
self.text_along_world_height = self._get_prop(props, 'Text along', 'Height', self.text_along_world_height)
self.text_along_priority = self._get_prop(props, 'Text along', 'Priority', self.text_along_priority)
self.tracking_label_enabled = self._get_prop(props, 'Tracking label', 'Enabled', self.tracking_label_enabled)
snap_radius = self._get_prop(props, 'Tracking label', 'Snap radius', self.tracking_label_snap_radius)
if snap_radius is None or snap_radius == -1.0:
self.tracking_label_snap_radius = None
else:
self.tracking_label_snap_radius = float(snap_radius)
self.tracking_label_font_size = self._get_prop(props, 'Tracking label', 'Font size', self.tracking_label_font_size)
self.tracking_label_world_width = self._get_prop(props, 'Tracking label', 'Length', self.tracking_label_world_width)
self.tracking_label_world_height = self._get_prop(props, 'Tracking label', 'Height', self.tracking_label_world_height)
self.tracking_label_priority = self._get_prop(props, 'Tracking label', 'Priority', self.tracking_label_priority)
self.tracking_label_horizontal = self._get_prop(props, 'Tracking label', 'Horizontal', self.tracking_label_horizontal)
self.attachedimage = Path(self._get_prop(props, 'Image', 'Attached image', self.attachedimage if self.attachedimage is not None else ''))
self.imagevisible = self._get_prop(props, 'Image', 'To show', self.imagevisible)
self.image_scale = self._get_prop(props, 'Image', 'Scale', self.image_scale)
self.image_relative_posx = self._get_prop(props, 'Image', 'Relative position X', self.image_relative_posx)
self.image_relative_posy = self._get_prop(props, 'Image', 'Relative position Y', self.image_relative_posy)
self.image_attached_pointx = self._get_prop(props, 'Image', 'Attached point X', self.image_attached_pointx)
self.image_attached_pointy = self._get_prop(props, 'Image', 'Attached point Y', self.image_attached_pointy)
posx = self._get_prop(props, 'Move', 'Start X', 99999.)
posy = self._get_prop(props, 'Move', 'Start Y', 99999.)
move_step = self._get_prop(props, 'Move', 'Step [m]', 99999.)
if posx != 99999. and posy != 99999.:
self.parent._move_start = (posx,posy)
else:
self.parent._move_start = None
if move_step != 99999.:
self.parent._move_step = move_step
else:
self.parent._move_step = None
posx = self._get_prop(props, 'Rotation', 'Center X', 99999.)
posy = self._get_prop(props, 'Rotation', 'Center Y', 99999.)
rot_step = self._get_prop(props, 'Rotation', 'Step [degree]', 99999.)
if posx != 99999. and posy != 99999.:
self.parent._rotation_center = (posx,posy)
else:
self.parent._rotation_center = None
if rot_step != 99999.:
self.parent._rotation_step = rot_step
else:
self.parent._rotation_step = None
angle = self._get_prop(props, 'Rotation', 'Angle [degree]', 0.)
dx = self._get_prop(props, 'Move', 'Delta X', 0.)
dy = self._get_prop(props, 'Move', 'Delta Y', 0.)
if angle != 0. and (dx != 0. or dy != 0.):
logging.error('Both rotation and move are selected')
elif angle != 0.:
if self.parent._rotation_center is not None:
self.parent.rotate(angle, self.parent._rotation_center)
self.parent.clear_cache()
elif dx != 0. or dy != 0.:
self.parent.move(dx,dy)
self.parent.clear_cache()
# FIXME : Must be positionned here to avoid bug during update of move and rotate
# set_legend_position and set_legend_text will call update_props... It is not a good idea !!
posx = self._get_prop(props, 'Legend', 'X', self.legendx)
posy = self._get_prop(props, 'Legend', 'Y', self.legendy)
self.parent.set_legend_position(posx,posy)
self.parent.set_legend_text(text)
self.load_unload_image()
try:
if updateOGL:
self.parent.parentzone.prep_listogl()
if self.parent.parentzone.parent is None:
logging.critical('Problem with OpenGL update - parent/mapviewer is None -- Track this issue')
self.parent.parentzone.parent.mapviewer.Refresh()
except:
logging.warning('Problem with OpenGL update - vectorproperties.fill_property')
pass
[docs]
def _defaultprop(self):
"""
Create the default properties for the vector and the associated UI
"""
if self.myprops is None:
self.myprops=Wolf_Param(title='Vector Properties', w= 500, h= 800, to_read= False, ontop= False)
self.myprops.show_in_active_if_default = True
self.myprops._callbackdestroy = self.destroyprop
self.myprops._callback=self.fill_property
self.myprops.hide_selected_buttons() # only 'Apply' button
self.myprops.addparam('Geometry','Length 2D',99999.,Type_Param.Float,'',whichdict='Default')
self.myprops.addparam('Geometry','Length 3D',99999.,Type_Param.Float,'',whichdict='Default')
self.myprops.addparam('Geometry','Surface',99999.,Type_Param.Float,'',whichdict='Default')
self.myprops.addparam('Draw','Color',(0,0,0),'Color','Drawing color',whichdict='Default')
self.myprops.addparam(
'Draw', 'Width', 1, 'Integer',
_('Base line width. Unit depends on "Width in pixels":\n'
'- True: pixels (screen-space)\n'
'- False: world units (map coordinates).'),
whichdict='Default'
)
# self.myprops.addparam('Draw','Style',1,'Integer','Drawing style',whichdict='Default')
self.myprops.addparam('Draw','Closed',False,'Logical','',whichdict='Default')
self.myprops.addparam('Draw','Filled',False,'Logical','',whichdict='Default')
self.myprops.addparam('Draw','Fill color',(-1,-1,-1),'Color',_('Fill colour (defaults to main colour unless explicitly changed)'),whichdict='Default')
self.myprops.addparam('Draw','Contour enabled',False,'Logical',_('Draw a contour stroke on top of filled polygons'),whichdict='Default')
self.myprops.addparam('Draw','Contour color',(-1,-1,-1),'Color',_('Contour colour (defaults to main colour unless explicitly changed)'),whichdict='Default')
self.myprops.addparam('Draw','Contour width',1.0,Type_Param.Float,_('Contour line width (pixels or world units)'),whichdict='Default')
self.myprops.addparam('Draw','Transparent',False,'Logical','',whichdict='Default')
self.myprops.addparam('Draw','Alpha',0,'Integer','Transparency intensity (255 is opaque)',whichdict='Default')
self.myprops.addparam('Draw','Plot indices if active',False,'Logical','',whichdict='Default')
self.myprops.addparam('Draw','Plot lengths if active',False,'Logical','',whichdict='Default')
self.myprops.addparam('Line style','Dash enabled',False,'Logical','',whichdict='Default')
self.myprops.addparam('Line style','Dash length',10.0,Type_Param.Float,'',whichdict='Default')
self.myprops.addparam('Line style','Gap length',5.0,Type_Param.Float,'',whichdict='Default')
self.myprops.addparam('Draw Glow','Glow enabled',False,'Logical','',whichdict='Default')
self.myprops.addparam('Draw Glow','Glow width',0.4,Type_Param.Float,'',whichdict='Default')
self.myprops.addparam('Draw Glow','Glow color',(255, 255, 255),Type_Param.Color,_('Glow halo colour'),whichdict='Default')
self.myprops.addparam('Draw Glow','Glow alpha',0.4,Type_Param.Float,_('Glow opacity (0-1)'),whichdict='Default')
jsonstr = new_json({_('None'): 0, _('Pulse'): 1, _('Marching ants'): 2}, _('Line animation mode'))
self.myprops.addparam('Animation','Animation mode',0,'Integer','',whichdict='Default', jsonstr=jsonstr)
self.myprops.addparam('Animation','Animation speed',1.0,Type_Param.Float,'',whichdict='Default')
jsonstr_fill = new_json({
_('None'): 0,
_('Pulse'): 1,
_('Sweep'): 2,
_('Ripple'): 3,
_('Radial progressive'): 4,
_('Clockwise clock fill'): 5,
_('Counter-clockwise clock fill'): 6,
}, _('Filled polygon animation mode'))
self.myprops.addparam('Fill animation','Mode',0,'Integer','',whichdict='Default', jsonstr=jsonstr_fill)
self.myprops.addparam('Fill animation','Speed',1.0,Type_Param.Float,'',whichdict='Default')
self.myprops.addparam('Fill animation','Center vertex index',0,Type_Param.Integer,_('Vertex index used as radial/clock animation centre; invalid values fall back to vertex 0'),whichdict='Default')
self.myprops.addparam('Fill animation','Start angle [deg]',90.0,Type_Param.Float,_('Clock fill start angle in degrees; 90 = 12 o\'clock'),whichdict='Default')
self.myprops.addparam('Fill animation','Show center preview',False,'Logical',_('Temporary editor-only overlay; not saved to file'),whichdict='Default')
preset_catalog = get_builtin_polygon_pbr_presets()
official_names = set(get_official_polygon_pbr_preset_names())
preset_choices = {_('Custom'): -1}
for idx, preset_name in enumerate(preset_catalog.keys(), start=1):
label = f'[OFFICIAL] {preset_name}' if preset_name in official_names else preset_name
preset_choices[_(label)] = idx
jsonstr_material = new_json({_('Flat fill'): 0, _('PBR material'): 1}, _('Optional material shading for filled polygons'))
self.myprops.addparam('Fill material','Mode',0,'Integer','',whichdict='Default', jsonstr=jsonstr_material)
self.myprops.addparam('Fill material','Preset',-1,'Integer',_('Choose a preconfigured texture set for quick setup'),whichdict='Default', jsonstr=new_json(preset_choices, _('Built-in polygon PBR presets')))
self.myprops.addparam('Fill material','Albedo texture','',Type_Param.File,_('Optional base-color texture'),whichdict='Default')
self.myprops.addparam('Fill material','Normal texture','',Type_Param.File,_('Optional tangent-space normal map projected in XY'),whichdict='Default')
self.myprops.addparam('Fill material','ORM texture','',Type_Param.File,_('Optional packed occlusion/roughness/metallic texture (R/G/B)'),whichdict='Default')
self.myprops.addparam('Fill material','Emissive texture','',Type_Param.File,_('Optional emissive texture'),whichdict='Default')
self.myprops.addparam('Fill material','Base color factor',(255, 255, 255),Type_Param.Color,_('Base colour multiplier applied before lighting'),whichdict='Default')
self.myprops.addparam('Fill material','Metallic factor',0.0,Type_Param.Float,_('Metallicness [0..1]. 0=texture controlled, 1=fully metallic. Blends with ORM texture to enable fine control on any surface'),whichdict='Default')
self.myprops.addparam('Fill material','Roughness factor',1.0,Type_Param.Float,_('Surface roughness multiplier [0.04..1.0]. Scales texture roughness. 1=texture strength, <1=smoother, >1=rougher'),whichdict='Default')
self.myprops.addparam('Fill material','Normal scale',1.0,Type_Param.Float,_('Strength applied to the normal map XY components'),whichdict='Default')
self.myprops.addparam('Fill material','Occlusion strength',1.0,Type_Param.Float,_('Strength applied to ORM occlusion channel'),whichdict='Default')
self.myprops.addparam('Fill material','Emissive factor',(0, 0, 0),Type_Param.Color,_('Emissive colour multiplier'),whichdict='Default')
self.myprops.addparam('Fill material','UV scale X [m/repeat]',100.0,Type_Param.Float,_('World-space repeat length along X'),whichdict='Default')
self.myprops.addparam('Fill material','UV scale Y [m/repeat]',100.0,Type_Param.Float,_('World-space repeat length along Y'),whichdict='Default')
self.myprops.addparam('Fill material','UV offset X [m]',0.0,Type_Param.Float,_('World-space UV offset along X'),whichdict='Default')
self.myprops.addparam('Fill material','UV offset Y [m]',0.0,Type_Param.Float,_('World-space UV offset along Y'),whichdict='Default')
self.myprops.addparam('Fill material','Cushion strength',0.0,Type_Param.Float,_('Pillow/cushion effect [0..2]. Tilts normals near polygon edges. 0=flat, 1=moderate, 2=strong bevel'),whichdict='Default')
self.myprops.addparam('Draw','Width in pixels',True,'Logical','',whichdict='Default')
self.myprops.addparam(
'Draw', 'Width profile', '', Type_Param.String,
_('Width multipliers applied to "Width" along the polyline.\n'
'Final width unit depends on "Width in pixels":\n'
'- True: final width in pixels\n'
'- False: final width in world units\n'
'Dense (one value per vertex): "1.0,1.5,2.0"\n'
'Sparse (index:value pairs, linear interpolation): "0:1.0,10:2.0,20:0.5"\n'
'In sparse mode, unspecified vertices are interpolated linearly; '
'values beyond the first/last index are held constant.'),
whichdict='Default'
)
jsonstr_join = new_json({_('Miter'): 0, _('Bevel'): 1, _('Round'): 2, _('Fillet'): 3}, _('Join style at segment junctions'))
self.myprops.addparam('Draw','Join style',0,'Integer','',whichdict='Default', jsonstr=jsonstr_join)
self.myprops.addparam('Draw','Join size',0.5,Type_Param.Float,_('Fillet: fraction (0-1) or world distance; fan: radius multiplier'),whichdict='Default')
jsonstr_jsm = new_json({_('Fraction'): 0, _('World (m)'): 1}, _('Join size unit mode'))
self.myprops.addparam('Draw','Join size mode',0,'Integer','',whichdict='Default', jsonstr=jsonstr_jsm)
# self.myprops.addparam('Draw','Flash',False,'Logical','',whichdict='Default')
self.myprops.addparam('Legend','Visible',False,'Logical','',whichdict='Default')
comment = _('Text for the legend \n \
if :\n \
- "Not used", the value will not be replaced\n \
- "name", the value will be the name of the vector\n \
- "first z", the value will be the z coordinate of the first vertex\n \
- "length2D", the value will be the 2D length of the vector\n \
- "length3D", the value will be the 3D length of the vector\n \
- "id", the value will be the index of the vector')
self.myprops.addparam('Legend','Text','','String',comment,whichdict='Default')
#1--4--7
#| | |
#2--5--8
#| | |
#3--6--9
jsonstr = new_json({_('Left'): 2, _('Right'):8, _('Top'):4, _('Bottom'):6, _('Center'):5, _('Top left'):1, _('Top right'):7, _('Bottom left'):3, _('Bottom right'):9}, _('Relative position of the legend'))
self.myprops.addparam('Legend','Relative position',5,'Integer','',whichdict='Default', jsonstr=jsonstr)
comment = _('Position of the legend \n \
if :\n \
- "Not used", the value will be the median of the coordinates of the vertices\n \
- "median", the value will be the median of the coordinates of the vertices\n \
- "mean", the value will be the mean of the coordinates of the vertices\n \
- "min", the value will be the minimum of the coordinates of the vertices\n \
- "max", the value will be the maximum of the coordinates of the vertices\n \
- "first", the value will be the coordinate of the first vertex\n \
- "last", the value will be the coordinate of the last vertex')
self.myprops.addparam('Legend','X',0,Type_Param.String, comment,whichdict='Default')
self.myprops.addparam('Legend','Y',0,Type_Param.String, comment,whichdict='Default')
self.myprops.addparam('Legend','Bold',False,'Logical','',whichdict='Default')
self.myprops.addparam('Legend','Italic',False,'Logical','',whichdict='Default')
jsonstr = new_json({_('Arial'): 1, _('Helvetica'):2, _('Sans Serif'):3}, _('Font name for the legend -- if not found, Arial will be used -- TTF file must be in a "fonts" directory in the same directory as the package'))
self.myprops.addparam('Legend','Font name', 1 , 'Integer','',whichdict='Default', jsonstr=jsonstr)
self.myprops.addparam('Legend','Font size',10,'Integer','',whichdict='Default')
self.myprops.addparam('Legend','Color',(0,0,0),'Color','',whichdict='Default')
self.myprops.addparam('Legend','Underlined',False,'Logical','',whichdict='Default')
self.myprops.addparam('Legend','Length',0,'Float','',whichdict='Default')
self.myprops.addparam('Legend','Height',0,'Float','',whichdict='Default')
jsonstr = new_json({_('Width'): 1, _('Height'):2, _('Fontsize'):3}, _('Priority will be respected during the OpenGL plot rendering'))
self.myprops.addparam('Legend','Priority', 3 ,'Integer', whichdict='Default', jsonstr=jsonstr)
self.myprops.addparam('Legend','Orientation',0,'Float',whichdict='Default', comment=_('In degrees'))
self.myprops.addparam('Legend','Glow enabled',False,'Logical','',whichdict='Default')
self.myprops.addparam('Legend','Glow width',0.15,Type_Param.Float,'',whichdict='Default')
self.myprops.addparam('Legend','Glow color',(255, 255, 255),Type_Param.Color,_('Legend glow colour'),whichdict='Default')
self.myprops.addparam('Legend','Glow alpha',0.5,Type_Param.Float,_('Legend glow opacity (0-1)'),whichdict='Default')
jsonstr = new_json({_('None'): 0, _('Pulse'): 1, _('Wave'): 2, _('Typewriter'): 3}, _('Legend animation mode'))
self.myprops.addparam('Legend','Animation mode',0,'Integer','',whichdict='Default', jsonstr=jsonstr)
self.myprops.addparam('Legend','Animation speed',1.0,Type_Param.Float,'',whichdict='Default')
self.myprops.addparam('Legend','Smoothing',1.0,Type_Param.Float,'',whichdict='Default')
jsonstr = new_json({_('Left'): 1, _('Center'): 2, _('Right'): 3}, _('Legend text alignment'))
self.myprops.addparam('Legend','Alignment',1,Type_Param.Integer,'',whichdict='Default', jsonstr=jsonstr)
self.myprops.addparam('Legend','Line spacing',1.2,Type_Param.Float,'',whichdict='Default')
self.myprops.addparam('Image','Attached image','',Type_Param.File, '', whichdict='Default')
self.myprops.addparam('Image','To show',False,Type_Param.Logical,'',whichdict='Default')
self.myprops.addparam('Image','Scale',1.0,Type_Param.Float,'',whichdict='Default')
self.myprops.addparam('Image','Relative position X',0.0,Type_Param.Float,'',whichdict='Default')
self.myprops.addparam('Image','Relative position Y',0.0,Type_Param.Float,'',whichdict='Default')
self.myprops.addparam('Image','Attached point X',-99999.,Type_Param.Float,'',whichdict='Default')
self.myprops.addparam('Image','Attached point Y',-99999.,Type_Param.Float,'',whichdict='Default')
self.myprops.addparam('Move','Start X',99999.,Type_Param.Float,'',whichdict='Default')
self.myprops.addparam('Move','Start Y',99999.,Type_Param.Float,'',whichdict='Default')
self.myprops.addparam('Move','Step [m]',99999.,Type_Param.Float,'',whichdict='Default')
self.myprops.addparam('Move','Delta X',0.0,Type_Param.Float,'',whichdict='Default')
self.myprops.addparam('Move','Delta Y',0.0,Type_Param.Float,'',whichdict='Default')
self.myprops.addparam('Rotation','Center X',99999.,Type_Param.Float,'',whichdict='Default')
self.myprops.addparam('Rotation','Center Y',99999.,Type_Param.Float,'',whichdict='Default')
self.myprops.addparam('Rotation','Step [degree]',99999.,Type_Param.Float,'',whichdict='Default')
self.myprops.addparam('Rotation','Angle [degree]',0.0,Type_Param.Float,'',whichdict='Default')
self.myprops.addparam('Text along','Enabled',False,'Logical','',whichdict='Default')
self.myprops.addparam('Text along','Text','',Type_Param.String,'',whichdict='Default')
self.myprops.addparam('Text along','Offset',0.0,Type_Param.Float,'',whichdict='Default')
self.myprops.addparam('Text along','Perpendicular offset',0.0,Type_Param.Float,'',whichdict='Default')
jsonstr = new_json({_('Left'): 1, _('Center'): 2, _('Right'): 3}, _('Text-along alignment'))
self.myprops.addparam('Text along','Alignment',2,Type_Param.Integer,'',whichdict='Default', jsonstr=jsonstr)
self.myprops.addparam('Text along','Font size',10,'Integer',_('Font size for text-along (independent from legend)'),whichdict='Default')
self.myprops.addparam('Text along','Length',100.0,Type_Param.Float,_('World-space width for text-along'),whichdict='Default')
self.myprops.addparam('Text along','Height',100.0,Type_Param.Float,_('World-space height for text-along'),whichdict='Default')
jsonstr = new_json({_('Width'): 1, _('Height'):2, _('Fontsize'):3}, _('Size priority for text-along rendering'))
self.myprops.addparam('Text along','Priority',3,Type_Param.Integer,'',whichdict='Default', jsonstr=jsonstr)
self.myprops.addparam('Tracking label','Enabled',False,'Logical','',whichdict='Default')
self.myprops.addparam('Tracking label','Snap radius',-1.0,Type_Param.Float,_('Set to -1 to disable distance filtering'),whichdict='Default')
self.myprops.addparam('Tracking label','Font size',10,'Integer',_('Font size for tracking label (independent from legend)'),whichdict='Default')
self.myprops.addparam('Tracking label','Length',100.0,Type_Param.Float,_('World-space width for tracking label'),whichdict='Default')
self.myprops.addparam('Tracking label','Height',100.0,Type_Param.Float,_('World-space height for tracking label'),whichdict='Default')
jsonstr = new_json({_('Width'): 1, _('Height'):2, _('Fontsize'):3}, _('Size priority for tracking label rendering'))
self.myprops.addparam('Tracking label','Priority',3,Type_Param.Integer,'',whichdict='Default', jsonstr=jsonstr)
self.myprops.addparam('Tracking label','Horizontal',True,'Logical',_('Horizontal text (True) or follow segment direction (False)'),whichdict='Default')
shader_mode = self._is_shader_rendering_active()
self._shader_ui_mode = shader_mode
if not shader_mode:
self._remove_shader_ui_params()
[docs]
def destroyprop(self):
"""
Nullify the properties UI
Destroy has been called, so the UI is not visible anymore
"""
self.myprops=None
[docs]
def show(self):
"""
Show the properties
Firstly, set default values
Then, add the current properties to the UI
"""
self._defaultprop()
self._ensure_props_schema_matches_renderer()
self.update_myprops()
self.myprops.Layout()
self.myprops.SetSizeHints(500,800)
self.myprops.Show()
self.myprops.SetTitle(_('Vector properties - {}'.format(self.parent.myname)))
icon_candidates = [
Path(__file__).parent / 'apps' / 'wolf.ico',
Path(__file__).parent.parent / 'apps' / 'wolf.ico',
Path(__file__).parent.parent.parent / 'apps' / 'wolf.ico',
]
icon_path = next((p for p in icon_candidates if p.exists()), None)
if icon_path is not None:
try:
icon = wx.Icon(str(icon_path), wx.BITMAP_TYPE_ANY)
if icon.IsOk():
self.myprops.SetIcon(icon)
except Exception:
logging.debug('Unable to load icon for vector properties from %s', icon_path)
self.myprops.Center()
self.myprops.Raise()
[docs]
def show_properties(self):
""" Show the properties -- alias of show() """
self.show()
[docs]
def hide_properties(self):
""" Hide the properties """
if self.myprops is not None:
self.myprops.Hide()
[docs]
def update_myprops(self):
""" Update the properties """
if self.myprops is not None:
self._ensure_props_schema_matches_renderer()
shader_mode = self._is_shader_rendering_active()
self.parent.update_lengths()
self.myprops[( 'Geometry','Length 2D')] = self.parent.length2D if self.parent.length2D is not None else 0.
self.myprops[( 'Geometry','Length 3D')] = self.parent.length3D if self.parent.length3D is not None else 0.
self.myprops[( 'Geometry','Surface')] = self.parent.area if self.parent.area is not None else 0.
self.myprops[('Draw','Color')] = getRGBfromI(self.color)
self.myprops[('Draw','Width')] = self.width
# self.myprops[('Draw','Style')] = self.style
self.myprops[('Draw','Closed')] = self.closed
self.myprops[('Draw','Filled')] = self.filled
self.myprops[('Draw','Fill color')] = getRGBfromI(self.fill_color) if self.fill_color is not None else getRGBfromI(self.color)
self.myprops[('Draw','Contour enabled')] = self.contour_enabled
self.myprops[('Draw','Contour color')] = getRGBfromI(self.contour_color) if self.contour_color is not None else getRGBfromI(self.color)
self.myprops[('Draw','Contour width')] = self.contour_width
self.myprops[('Draw','Transparent')]= self.transparent
self.myprops[('Draw','Alpha')] = self.alpha
# self.myprops[('Draw','Flash')] = self.flash
self.myprops[('Draw','Plot indices if active')] = self.plot_indices
self.myprops[('Draw','Plot lengths if active')] = self.plot_lengths
if shader_mode:
self.myprops[('Line style','Dash enabled')] = self.dash_enabled
self.myprops[('Line style','Dash length')] = self.dash_length
self.myprops[('Line style','Gap length')] = self.gap_length
material = getattr(self, 'fill_pbr_material', PolygonPBRMaterial())
self.myprops[('Fill material','Mode')] = 1 if material.enabled else 0
preset_catalog = get_builtin_polygon_pbr_presets()
preset_names = list(preset_catalog.keys())
if material.preset_name in preset_names:
self.myprops[('Fill material','Preset')] = preset_names.index(material.preset_name) + 1
else:
self.myprops[('Fill material','Preset')] = -1
self.myprops[('Fill material','Albedo texture')] = str(material.albedo_texture)
self.myprops[('Fill material','Normal texture')] = str(material.normal_texture)
self.myprops[('Fill material','ORM texture')] = str(material.orm_texture)
self.myprops[('Fill material','Emissive texture')] = str(material.emissive_texture)
self.myprops[('Fill material','Base color factor')] = tuple(int(max(0.0, min(1.0, c)) * 255) for c in material.base_color_factor[:3])
self.myprops[('Fill material','Metallic factor')] = material.metallic_factor
self.myprops[('Fill material','Roughness factor')] = material.roughness_factor
self.myprops[('Fill material','Normal scale')] = material.normal_scale
self.myprops[('Fill material','Occlusion strength')] = material.occlusion_strength
self.myprops[('Fill material','Emissive factor')] = tuple(int(max(0.0, min(1.0, c)) * 255) for c in material.emissive_factor)
self.myprops[('Fill material','UV scale X [m/repeat]')] = material.uv_scale[0]
self.myprops[('Fill material','UV scale Y [m/repeat]')] = material.uv_scale[1]
self.myprops[('Fill material','UV offset X [m]')] = material.uv_offset[0]
self.myprops[('Fill material','UV offset Y [m]')] = material.uv_offset[1]
self.myprops[('Fill material','Cushion strength')] = material.cushion_strength
self.myprops[('Draw Glow','Glow enabled')] = self.glow_enabled
self.myprops[('Draw Glow','Glow width')] = self.glow_width
self.myprops[('Draw Glow','Glow color')] = tuple(int(c * 255) for c in self.glow_color[:3])
self.myprops[('Draw Glow','Glow alpha')] = self.glow_color[3]
self.myprops[('Animation','Animation mode')] = self.anim_mode
self.myprops[('Animation','Animation speed')] = self.anim_speed
self.myprops[('Fill animation','Mode')] = self.fill_anim_mode
self.myprops[('Fill animation','Speed')] = self.fill_anim_speed
self.myprops[('Fill animation','Center vertex index')] = self.fill_anim_center_index
self.myprops[('Fill animation','Start angle [deg]')] = self.fill_anim_start_angle
self.myprops[('Fill animation','Show center preview')] = self.fill_anim_center_preview
self.myprops[('Draw','Width in pixels')] = self.width_in_pixels
if self.width_profile is None or len(self.width_profile) == 0:
_wp_str = ''
elif isinstance(self.width_profile[0], (tuple, list)):
# Sparse format: "idx:val,idx:val,..."
_wp_str = ','.join(f'{int(idx)}:{val}' for idx, val in self.width_profile)
else:
# Dense format: "v0,v1,v2,..."
_wp_str = ','.join(str(v) for v in self.width_profile)
self.myprops[('Draw','Width profile')] = _wp_str
self.myprops[('Draw','Join style')] = self.join_style
self.myprops[('Draw','Join size')] = self.join_size
self.myprops[('Draw','Join size mode')] = self.join_size_mode
self.myprops[('Legend','Visible')] = self.legendvisible
self.myprops[('Legend','Text')] = self.legendtext
self.myprops[('Legend','Relative position')]=self.legendrelpos
self.myprops[('Legend','X')] = str(self.legendx)
self.myprops[('Legend','Y')] = str(self.legendy)
self.myprops[('Legend','Bold')] = self.legendbold
self.myprops[('Legend','Italic')] = self.legenditalic
self.myprops[('Legend','Font name')]= self._convert_fontname2int(self.legendfontname)
self.myprops[('Legend','Font size')]= self.legendfontsize
self.myprops[('Legend','Color')] = getRGBfromI(self.legendcolor)
self.myprops[('Legend','Underlined')]= self.legendunderlined
self.myprops[('Legend','Length')] = self.legendlength
self.myprops[('Legend','Height')] = self.legendheight
self.myprops[('Legend','Priority')] = self.legendpriority
self.myprops[('Legend','Orientation')]= self.legendorientation
if shader_mode:
self.myprops[('Legend','Glow enabled')] = self.legend_glow_enabled
self.myprops[('Legend','Glow width')] = self.legend_glow_width
self.myprops[('Legend','Glow color')] = tuple(int(c * 255) for c in self.legend_glow_color[:3])
self.myprops[('Legend','Glow alpha')] = self.legend_glow_color[3]
self.myprops[('Legend','Animation mode')] = self.legend_anim_mode
self.myprops[('Legend','Animation speed')] = self.legend_anim_speed
self.myprops[('Legend','Smoothing')] = self.legend_smoothing
self.myprops[('Legend','Alignment')] = self._convert_alignment_to_int(self.legend_alignment)
self.myprops[('Legend','Line spacing')] = self.legend_line_spacing
self.myprops[('Image','Attached image')] = str(self.attachedimage)
self.myprops[('Image','To show')] = self.imagevisible
self.myprops[('Image','Scale')] = self.image_scale
self.myprops[('Image','Relative position X')] = self.image_relative_posx
self.myprops[('Image','Relative position Y')] = self.image_relative_posy
self.myprops[('Image','Attached point X')] = self.image_attached_pointx
self.myprops[('Image','Attached point Y')] = self.image_attached_pointy
if shader_mode:
self.myprops[('Text along','Enabled')] = self.text_along_enabled
self.myprops[('Text along','Text')] = self.text_along_text
self.myprops[('Text along','Font size')] = self.text_along_font_size
self.myprops[('Text along','Length')] = self.text_along_world_width
self.myprops[('Text along','Height')] = self.text_along_world_height
self.myprops[('Text along','Priority')] = self.text_along_priority
self.myprops[('Text along','Offset')] = self.text_along_offset
self.myprops[('Text along','Perpendicular offset')] = self.text_along_perp
self.myprops[('Text along','Alignment')] = self._convert_alignment_to_int(self.text_along_alignment, default=2)
self.myprops[('Tracking label','Enabled')] = self.tracking_label_enabled
self.myprops[('Tracking label','Snap radius')] = -1.0 if self.tracking_label_snap_radius is None else self.tracking_label_snap_radius
self.myprops[('Tracking label','Font size')] = self.tracking_label_font_size
self.myprops[('Tracking label','Length')] = self.tracking_label_world_width
self.myprops[('Tracking label','Height')] = self.tracking_label_world_height
self.myprops[('Tracking label','Priority')] = self.tracking_label_priority
self.myprops[('Tracking label','Horizontal')] = self.tracking_label_horizontal
if self.parent._rotation_center is not None:
self.myprops[('Rotation','Center X')] = self.parent._rotation_center[0]
self.myprops[('Rotation','Center Y')] = self.parent._rotation_center[1]
else:
self.myprops[('Rotation','Center X')] = 99999.
self.myprops[('Rotation','Center Y')] = 99999.
if self.parent._rotation_step is not None:
self.myprops[('Rotation','Step [degree]')] = self.parent._rotation_step
else:
self.myprops[('Rotation','Step [degree]')] = 99999.
self.myprops[('Rotation', 'Angle [degree]')] = 0.
if self.parent._move_start is not None:
self.myprops[('Move','Start X')] = self.parent._move_start[0]
self.myprops[('Move','Start Y')] = self.parent._move_start[1]
else:
self.myprops[('Move','Start X')] = 99999.
self.myprops[('Move','Start Y')] = 99999.
if self.parent._move_step is not None:
self.myprops[('Move','Step [m]')] = self.parent._move_step
else:
self.myprops[('Move','Step [m]')] = 99999.
self.myprops[('Move','Delta X')] = 0.
self.myprops[('Move','Delta Y')] = 0.
self.myprops.Populate()
[docs]
def update_image_texture(self):
""" Update the image texture if it exists """
if self.textureimage is not None:
(xmin, xmax), (ymin, ymax) = self.parent.get_bounds_xx_yy()
self.textureimage.xmin = xmin
self.textureimage.xmax = xmax
self.textureimage.ymin = ymin
self.textureimage.ymax = ymax
self.textureimage.drawing_scale = self.image_scale
self.textureimage.offset = (self.image_relative_posx, self.image_relative_posy)
[docs]
def _offset_image_texture(self, delta_x:float, delta_y:float):
"""Offset the image texture by a delta in X and Y directions.
:param delta_x: Translation along X.
:param delta_y: Translation along Y.
"""
if self._cached_offset is None:
self._cached_offset = (self.image_relative_posx, self.image_relative_posy)
self.image_relative_posx = self._cached_offset[0] + delta_x
self.image_relative_posy = self._cached_offset[1] + delta_y
[docs]
def _reset_cached_offset(self):
""" Reset the cached offset for the image texture """
self._cached_offset = None