Source code for wolfhece.pyvertexvectors._vectorproperties

"""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
[docs] def init_extra(self): """Override to add GUI-specific ``textureimage`` attribute.""" super().init_extra() self.textureimage: genericImagetexture = None self._shader_ui_mode: bool | None = None self.fill_anim_center_preview: bool = False
@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