tilemap-editor 3.3.2__tar.gz → 3.3.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {tilemap_editor-3.3.2/src/tilemap_editor.egg-info → tilemap_editor-3.3.4}/PKG-INFO +1 -1
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/pyproject.toml +1 -1
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/editor.py +19 -4
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/node_manager.py +11 -1
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/plugins/sprite_animation/preview.py +2 -1
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/plugins/sprite_editor/editor.py +24 -5
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/plugins/tileset_collision/collision_painter.py +137 -34
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/plugins/tileset_collision/editor.py +192 -2
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap.py +4 -2
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4/src/tilemap_editor.egg-info}/PKG-INFO +1 -1
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor.egg-info/SOURCES.txt +4 -0
- tilemap_editor-3.3.4/src/widgets/particle_presets.py +663 -0
- tilemap_editor-3.3.4/src/widgets/particle_system.py +436 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/widgets/spritesheet_grid.py +190 -30
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/widgets/tile_grid.py +67 -12
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/widgets/tile_selector.py +19 -8
- tilemap_editor-3.3.4/src/widgets/ui/checkbox.py +99 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/widgets/ui/node_editor.py +114 -24
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/widgets/ui/node_selector.py +15 -4
- tilemap_editor-3.3.4/src/widgets/ui/particle_config_dialog.py +712 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/LICENSE +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/MANIFEST.in +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/README.md +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/assets/fonts/jetbrain-fonts/JetBrainsMono-Bold.ttf +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/assets/fonts/jetbrain-fonts/JetBrainsMono-BoldItalic.ttf +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/assets/fonts/jetbrain-fonts/JetBrainsMono-Italic.ttf +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/assets/fonts/jetbrain-fonts/JetBrainsMono-Regular.ttf +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/assets/fonts/noto/NotoSans-Bold.ttf +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/assets/fonts/noto/NotoSans-BoldItalic.ttf +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/assets/fonts/noto/NotoSans-Italic.ttf +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/assets/icons/arrow-down.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/assets/icons/check.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/assets/icons/checked.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/assets/icons/close.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/assets/icons/duplicate.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/assets/icons/error.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/assets/icons/file.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/assets/icons/filedead.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/assets/icons/fit.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/assets/icons/folder.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/assets/icons/grid.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/assets/icons/image.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/assets/icons/info.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/assets/icons/load.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/assets/icons/loop.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/assets/icons/pan.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/assets/icons/pause.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/assets/icons/pencil.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/assets/icons/play.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/assets/icons/plus.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/assets/icons/radio.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/assets/icons/reset.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/assets/icons/save.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/assets/icons/stop.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/assets/icons/tilemap.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/assets/icons/tileset.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/assets/icons/unchecked.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/assets/icons/warning.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/assets/icons/zoomin.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/assets/icons/zoomout.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/setup.cfg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/configs/themes.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/constants.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/event_map.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/layers.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/main.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/nodes.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/plugins/__init__.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/plugins/character_collision/__init__.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/plugins/character_collision/editor.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/plugins/character_collision/models.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/plugins/character_collision/protocols.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/plugins/character_collision/shape_editor.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/plugins/character_collision/standalone.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/plugins/object_tileset_collision/__init__.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/plugins/object_tileset_collision/editor.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/plugins/object_tileset_collision/models.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/plugins/object_tileset_collision/protocols.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/plugins/object_tileset_collision/standalone.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/plugins/sprite_animation/__init__.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/plugins/sprite_animation/__main__.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/plugins/sprite_animation/clipboard_util.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/plugins/sprite_animation/editor.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/plugins/sprite_animation/frame_picker.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/plugins/sprite_animation/models.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/plugins/sprite_animation/protocols.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/plugins/sprite_animation/runtime_load.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/plugins/sprite_animation/standalone.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/plugins/sprite_animation/timeline.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/plugins/sprite_animation/validation.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/plugins/sprite_editor/__init__.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/plugins/sprite_editor/standalone.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/plugins/tileset_collision/__init__.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/plugins/tileset_collision/models.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/plugins/tileset_collision/protocols.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/plugins/tileset_collision/standalone.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/standalone_automap.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/standalone_error_console.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/standalone_filemanager.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/standalone_image_viewer.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor/__init__.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor/__main__.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/__init__.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Bold.ttf +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-BoldItalic.ttf +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Italic.ttf +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Regular.ttf +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/fonts/noto/NotoSans-Bold.ttf +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/fonts/noto/NotoSans-BoldItalic.ttf +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/fonts/noto/NotoSans-Italic.ttf +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/ToolMove.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/arrow-down.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/check.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/checked.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/close.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/duplicate.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/error.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/file.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/filedead.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/fit.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/folder.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/grid.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/image.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/info.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/load.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/loop.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/pan.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/pause.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/pencil.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/play.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/plus.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/radio.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/reset.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/save.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/stop.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/tilemap.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/tileset.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/unchecked.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/warning.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/zoomin.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor/assets/icons/zoomout.svg +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor/cli.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor/main.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor/settings.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor.egg-info/dependency_links.txt +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor.egg-info/entry_points.txt +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor.egg-info/requires.txt +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/tilemap_editor.egg-info/top_level.txt +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/ttypes/__init__.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/ttypes/tilemap.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/utils/__init__.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/utils/editor_preference.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/utils/error_handler.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/utils/font_manager.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/utils/history.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/utils/icon_manager.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/utils/icons_cache.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/utils/log_capture.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/utils/project_paths.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/utils/serialization.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/utils/standalone.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/utils/validation.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/widgets/__init__.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/widgets/automap_models.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/widgets/autotile_template.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/widgets/autotiler.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/widgets/filemanager.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/widgets/input.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/widgets/layer_selector.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/widgets/map_properties.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/widgets/mapsetup.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/widgets/regex_automap_designer.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/widgets/ui/collision_layer_mask.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/widgets/ui/collision_layer_sidebar.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/widgets/ui/confirm_dialog.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/widgets/ui/draw_utils.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/widgets/ui/fileinput.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/widgets/ui/layer_type_dialog.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/widgets/ui/menubar.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/widgets/ui/mode_indicator.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/widgets/ui/notification.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/widgets/ui/property_editor.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/widgets/ui/region_selector.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/widgets/ui/status_bar.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/widgets/ui/theme.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/widgets/ui/tileset_type_dialog.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/widgets/ui/toolbar.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/widgets/ui/tooltip.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/tests/test_animation_library_grid_offset.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/tests/test_collision_layer_mask.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/tests/test_collision_layer_sidebar.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/tests/test_editor_pan_mode.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/tests/test_project_paths.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/tests/test_render_scale.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/tests/test_sprite_animation_editor_grid.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/tests/test_tile_grid_selection.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/tests/test_tile_selector_pick.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/tests/test_tilemap_save.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/tests/test_timeline_invalidate_cache.py +0 -0
- {tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/tests/test_toolbar_tools.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "tilemap-editor"
|
|
7
|
-
version = "3.3.
|
|
7
|
+
version = "3.3.4"
|
|
8
8
|
description = "Pygame tilemap editor with SVG icons, professional UI, and sprite animation tools"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -35,6 +35,7 @@ from widgets.ui.node_selector import NodeSelector
|
|
|
35
35
|
from widgets.ui.node_editor import NodeEditor
|
|
36
36
|
from widgets.ui.notification import NotificationManager
|
|
37
37
|
from widgets.ui.property_editor import PropertyEditor
|
|
38
|
+
from widgets.ui.particle_config_dialog import ParticleConfigDialog
|
|
38
39
|
from widgets.ui.tooltip import TooltipManager
|
|
39
40
|
from widgets.ui.theme import get_theme_manager, set_theme, THEMES
|
|
40
41
|
from utils.log_capture import setup_console_log
|
|
@@ -192,6 +193,7 @@ class Editor:
|
|
|
192
193
|
self.tooltip = TooltipManager()
|
|
193
194
|
self.error_console_process: Optional[subprocess.Popen] = None
|
|
194
195
|
self.property_editor: Optional[PropertyEditor] = None
|
|
196
|
+
self.particle_config_dialog: Optional[ParticleConfigDialog] = None
|
|
195
197
|
|
|
196
198
|
self.child_processes: List[subprocess.Popen] = []
|
|
197
199
|
self.file_manager_process: Optional[subprocess.Popen] = None
|
|
@@ -246,7 +248,7 @@ class Editor:
|
|
|
246
248
|
self.map_properties_dialog = MapPropertiesDialog(self, Rect(center_x, center_y, 400, 260))
|
|
247
249
|
self.node_manager = NodeManager(self)
|
|
248
250
|
self.node_selector = NodeSelector(self, 0, 65, 260, 240)
|
|
249
|
-
self.node_editor = NodeEditor(self, 0, 310, 260,
|
|
251
|
+
self.node_editor = NodeEditor(self, 0, 310, 260, 230)
|
|
250
252
|
|
|
251
253
|
def open_file_manager(
|
|
252
254
|
self,
|
|
@@ -443,6 +445,9 @@ class Editor:
|
|
|
443
445
|
error_handler.capture(e, context="save_map_selected")
|
|
444
446
|
|
|
445
447
|
def export_selection_as_png(self):
|
|
448
|
+
if self.tile_grid_widget is None:
|
|
449
|
+
self.notifications.notify("No tile grid available", duration=2.0)
|
|
450
|
+
return
|
|
446
451
|
rect = self.tile_grid_widget.selection_rect
|
|
447
452
|
if rect is None:
|
|
448
453
|
self.notifications.notify("No region selected", duration=2.0)
|
|
@@ -458,6 +463,9 @@ class Editor:
|
|
|
458
463
|
rect = self.tile_grid_widget.selection_rect
|
|
459
464
|
if rect is None:
|
|
460
465
|
return
|
|
466
|
+
if self.tileset_widget is None:
|
|
467
|
+
self.notifications.notify("No tileset available", duration=2.0)
|
|
468
|
+
return
|
|
461
469
|
x1, y1, x2, y2 = rect
|
|
462
470
|
tw, th = self.tilemap.tile_size
|
|
463
471
|
pw = (x2 - x1 + 1) * tw
|
|
@@ -811,7 +819,7 @@ class Editor:
|
|
|
811
819
|
resolved = None
|
|
812
820
|
|
|
813
821
|
if not resolved or not resolved.exists():
|
|
814
|
-
|
|
822
|
+
self.notifications.notify(f"Could not locate spritesheet: {path.name}", duration=2.0)
|
|
815
823
|
return
|
|
816
824
|
|
|
817
825
|
args = [
|
|
@@ -1135,6 +1143,10 @@ class Editor:
|
|
|
1135
1143
|
if self.property_editor.handle_event(event):
|
|
1136
1144
|
continue
|
|
1137
1145
|
|
|
1146
|
+
if self.particle_config_dialog and self.particle_config_dialog.active:
|
|
1147
|
+
if self.particle_config_dialog.handle_event(event):
|
|
1148
|
+
continue
|
|
1149
|
+
|
|
1138
1150
|
# Priority 2: Autotiler and Regex Designer (block all events when visible)
|
|
1139
1151
|
if self.autotiler.visible:
|
|
1140
1152
|
if self.autotiler.handle_event(event):
|
|
@@ -1354,8 +1366,6 @@ class Editor:
|
|
|
1354
1366
|
self.screen.blit(overlay, (0, 0))
|
|
1355
1367
|
self.layer_type_dialog.draw(self.screen)
|
|
1356
1368
|
|
|
1357
|
-
if self.property_editor and self.property_editor.active:
|
|
1358
|
-
self.property_editor.draw(self.screen)
|
|
1359
1369
|
if self.loading_state["active"]:
|
|
1360
1370
|
overlay = pygame.Surface((self.width, self.height), pygame.SRCALPHA)
|
|
1361
1371
|
overlay.fill((0, 0, 0, 180))
|
|
@@ -1381,6 +1391,11 @@ class Editor:
|
|
|
1381
1391
|
self.node_selector.draw(self.screen)
|
|
1382
1392
|
if self.node_editor:
|
|
1383
1393
|
self.node_editor.draw(self.screen)
|
|
1394
|
+
# Modal dialogs draw on top of everything
|
|
1395
|
+
if self.property_editor and self.property_editor.active:
|
|
1396
|
+
self.property_editor.draw(self.screen)
|
|
1397
|
+
if self.particle_config_dialog and self.particle_config_dialog.active:
|
|
1398
|
+
self.particle_config_dialog.draw(self.screen)
|
|
1384
1399
|
self.menubar.draw(self.screen)
|
|
1385
1400
|
self.tooltip.draw(self.screen)
|
|
1386
1401
|
|
|
@@ -100,12 +100,20 @@ class NodeManager:
|
|
|
100
100
|
|
|
101
101
|
def create_default_node(self, layer_name: str, node_type: str = "area") -> Node:
|
|
102
102
|
count = len(self.nodes) + 1
|
|
103
|
+
props: Dict[str, Any] = {}
|
|
104
|
+
if node_type == "particle_emitter":
|
|
105
|
+
from widgets.particle_system import get_default_config
|
|
106
|
+
props = get_default_config()
|
|
107
|
+
name = f"Emitter {count}"
|
|
108
|
+
else:
|
|
109
|
+
name = f"{node_type.capitalize()} {count}"
|
|
103
110
|
return Node(
|
|
104
111
|
node_id=create_node_id(),
|
|
105
|
-
name=
|
|
112
|
+
name=name,
|
|
106
113
|
node_type=node_type,
|
|
107
114
|
area=NodeRect(x=0, y=0, w=64, h=64),
|
|
108
115
|
layer_name=layer_name,
|
|
116
|
+
properties=props,
|
|
109
117
|
)
|
|
110
118
|
|
|
111
119
|
def rename_group(self, old_name: str, new_name: str) -> bool:
|
|
@@ -132,6 +140,8 @@ class NodeManager:
|
|
|
132
140
|
def reorder_node(self, node_id: str, target_node_id: str, before: bool = True) -> None:
|
|
133
141
|
if node_id not in self.nodes or target_node_id not in self.nodes:
|
|
134
142
|
return
|
|
143
|
+
if node_id == target_node_id:
|
|
144
|
+
return
|
|
135
145
|
node = self.nodes[node_id]
|
|
136
146
|
keys = list(self.nodes.keys())
|
|
137
147
|
keys.remove(node_id)
|
|
@@ -416,7 +416,8 @@ class AnimationPreview:
|
|
|
416
416
|
|
|
417
417
|
def _extract_tile(self, variant_id: int) -> Optional[pygame.Surface]:
|
|
418
418
|
tw, th = self.tile_size
|
|
419
|
-
ox
|
|
419
|
+
ox = self.grid_offset_x
|
|
420
|
+
oy = self.grid_offset_y
|
|
420
421
|
available_w = self.surface.get_width() - ox
|
|
421
422
|
cols = max(1, available_w // tw)
|
|
422
423
|
col = variant_id % cols
|
|
@@ -149,6 +149,24 @@ class SpriteEditor:
|
|
|
149
149
|
if self.grid.redo():
|
|
150
150
|
self.grid.selected_indices.clear()
|
|
151
151
|
return True
|
|
152
|
+
elif event.key == pygame.K_c:
|
|
153
|
+
if self.grid.has_selection():
|
|
154
|
+
self._clipboard = self.grid.copy_selected()
|
|
155
|
+
self._paste_mode = False
|
|
156
|
+
self.grid.paste_preview_idx = -1
|
|
157
|
+
return True
|
|
158
|
+
elif event.key == pygame.K_x:
|
|
159
|
+
if self.grid.has_selection():
|
|
160
|
+
self._clipboard = self.grid.cut_selected()
|
|
161
|
+
self._paste_mode = False
|
|
162
|
+
self.grid.paste_preview_idx = -1
|
|
163
|
+
return True
|
|
164
|
+
elif event.key == pygame.K_v:
|
|
165
|
+
if self._clipboard:
|
|
166
|
+
self._paste_mode = not self._paste_mode
|
|
167
|
+
if not self._paste_mode:
|
|
168
|
+
self.grid.paste_preview_idx = -1
|
|
169
|
+
return True
|
|
152
170
|
|
|
153
171
|
# Toolbar clicks
|
|
154
172
|
if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
|
|
@@ -156,19 +174,20 @@ class SpriteEditor:
|
|
|
156
174
|
if r.collidepoint(mouse):
|
|
157
175
|
return self._handle_tool(lbl)
|
|
158
176
|
|
|
159
|
-
# Paste mode:
|
|
177
|
+
# Paste mode: click places (or exits if outside grid)
|
|
160
178
|
if self._paste_mode and event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
|
|
161
179
|
idx = self.grid.index_at_pos(mouse)
|
|
162
180
|
if idx >= 0 and self.grid.rect.collidepoint(mouse):
|
|
163
181
|
self.grid.paste_at(idx, self._clipboard)
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
182
|
+
self._paste_mode = False
|
|
183
|
+
self.grid.paste_preview_idx = -1
|
|
184
|
+
return True
|
|
167
185
|
|
|
168
|
-
# Paste preview on hover
|
|
186
|
+
# Paste preview on hover (consume event to prevent grid overlap)
|
|
169
187
|
if self._paste_mode and event.type == pygame.MOUSEMOTION:
|
|
170
188
|
idx = self.grid.index_at_pos(mouse)
|
|
171
189
|
self.grid.paste_preview_idx = idx if idx >= 0 else -1
|
|
190
|
+
return True
|
|
172
191
|
|
|
173
192
|
return self.grid.handle_event(event)
|
|
174
193
|
|
{tilemap_editor-3.3.2 → tilemap_editor-3.3.4}/src/plugins/tileset_collision/collision_painter.py
RENAMED
|
@@ -117,7 +117,8 @@ class CollisionPainter:
|
|
|
117
117
|
self.snap_to_grid = False
|
|
118
118
|
self.grid_size = 8 # pixels
|
|
119
119
|
self.show_grid = True
|
|
120
|
-
self.
|
|
120
|
+
self.show_angle_hints = False # show angle arcs at vertices
|
|
121
|
+
self.edge_draw_mode = False # Ctrl+E - straight line drawing
|
|
121
122
|
self._edge_start: Optional[Tuple[int, int]] = None # Start point for edge draw
|
|
122
123
|
self._shift_held = False
|
|
123
124
|
|
|
@@ -270,7 +271,66 @@ class CollisionPainter:
|
|
|
270
271
|
def get_one_way_flags(self) -> List[bool]:
|
|
271
272
|
"""Get one-way collision flags for all polygons"""
|
|
272
273
|
return list(self.polygon_one_way)
|
|
274
|
+
|
|
275
|
+
def toggle_one_way(self) -> None:
|
|
276
|
+
"""Toggle one-way flag on the selected polygon"""
|
|
277
|
+
idx = self.selected_polygon_idx
|
|
278
|
+
if idx is not None and 0 <= idx < len(self.polygon_one_way):
|
|
279
|
+
self.polygon_one_way[idx] = not self.polygon_one_way[idx]
|
|
280
|
+
if self.on_polygon_modified:
|
|
281
|
+
self.on_polygon_modified(idx)
|
|
273
282
|
|
|
283
|
+
def _get_interior_angle(
|
|
284
|
+
self, polygon: List[Tuple[float, float]], idx: int
|
|
285
|
+
) -> float:
|
|
286
|
+
"""Compute interior angle (degrees) at polygon vertex idx."""
|
|
287
|
+
n = len(polygon)
|
|
288
|
+
if n < 3:
|
|
289
|
+
return 0.0
|
|
290
|
+
prev = polygon[(idx - 1) % n]
|
|
291
|
+
curr = polygon[idx]
|
|
292
|
+
nxt = polygon[(idx + 1) % n]
|
|
293
|
+
v1 = (prev[0] - curr[0], prev[1] - curr[1])
|
|
294
|
+
v2 = (nxt[0] - curr[0], nxt[1] - curr[1])
|
|
295
|
+
dot = v1[0] * v2[0] + v1[1] * v2[1]
|
|
296
|
+
mag1 = math.hypot(v1[0], v1[1])
|
|
297
|
+
mag2 = math.hypot(v2[0], v2[1])
|
|
298
|
+
if mag1 < 0.001 or mag2 < 0.001:
|
|
299
|
+
return 0.0
|
|
300
|
+
cos_a = max(-1.0, min(1.0, dot / (mag1 * mag2)))
|
|
301
|
+
return math.degrees(math.acos(cos_a))
|
|
302
|
+
|
|
303
|
+
def _draw_angle_hint(
|
|
304
|
+
self,
|
|
305
|
+
screen: pygame.Surface,
|
|
306
|
+
screen_pos: Tuple[int, int],
|
|
307
|
+
angle_deg: float,
|
|
308
|
+
) -> None:
|
|
309
|
+
"""Draw a small arc + angle text at a vertex."""
|
|
310
|
+
if angle_deg <= 0.5 or angle_deg >= 179.5:
|
|
311
|
+
return
|
|
312
|
+
arc_radius = 14
|
|
313
|
+
segments = max(4, int(angle_deg / 10))
|
|
314
|
+
angle_rad = math.radians(angle_deg)
|
|
315
|
+
pts = [screen_pos]
|
|
316
|
+
for i in range(segments + 1):
|
|
317
|
+
t = -angle_rad * i / segments
|
|
318
|
+
pts.append((
|
|
319
|
+
screen_pos[0] + arc_radius * math.cos(t),
|
|
320
|
+
screen_pos[1] + arc_radius * math.sin(t),
|
|
321
|
+
))
|
|
322
|
+
if len(pts) >= 3:
|
|
323
|
+
wedge = pygame.Surface(self.rect.size, pygame.SRCALPHA)
|
|
324
|
+
pygame.draw.polygon(wedge, (*_COLORS["polygon_fill"], 60), pts)
|
|
325
|
+
screen.blit(wedge, self.rect.topleft)
|
|
326
|
+
label = self._font_sm.render(f"{angle_deg:.0f}°", True, _COLORS["text"])
|
|
327
|
+
label_x = screen_pos[0] + 10
|
|
328
|
+
label_y = screen_pos[1] - label.get_height() - 4
|
|
329
|
+
bg = pygame.Surface((label.get_width() + 4, label.get_height() + 2), pygame.SRCALPHA)
|
|
330
|
+
bg.fill((0, 0, 0, 180))
|
|
331
|
+
screen.blit(bg, (label_x - 2, label_y - 1))
|
|
332
|
+
screen.blit(label, (label_x, label_y))
|
|
333
|
+
|
|
274
334
|
def clear_all(self) -> None:
|
|
275
335
|
"""Clear all polygons"""
|
|
276
336
|
self.polygons = []
|
|
@@ -488,23 +548,26 @@ class CollisionPainter:
|
|
|
488
548
|
|
|
489
549
|
# Keyboard shortcuts
|
|
490
550
|
if event.type == pygame.KEYDOWN:
|
|
551
|
+
mods = pygame.key.get_mods()
|
|
552
|
+
ctrl = mods & (pygame.KMOD_LCTRL | pygame.KMOD_LMETA)
|
|
553
|
+
|
|
491
554
|
# Track Shift state for edge draw
|
|
492
555
|
if event.key in (pygame.K_LSHIFT, pygame.K_RSHIFT):
|
|
493
556
|
self._shift_held = True
|
|
494
|
-
|
|
495
|
-
# E - toggle edge draw mode
|
|
496
|
-
elif event.key == pygame.K_e:
|
|
557
|
+
|
|
558
|
+
# Ctrl+E - toggle edge draw mode
|
|
559
|
+
elif ctrl and event.key == pygame.K_e:
|
|
497
560
|
self.edge_draw_mode = not self.edge_draw_mode
|
|
498
561
|
if not self.edge_draw_mode:
|
|
499
562
|
self._edge_start = None
|
|
500
563
|
return True
|
|
501
|
-
|
|
564
|
+
|
|
502
565
|
# Enter - complete polygon
|
|
503
566
|
elif event.key in (pygame.K_RETURN, pygame.K_KP_ENTER):
|
|
504
567
|
if len(self.current_polygon) >= 3:
|
|
505
568
|
self._complete_polygon()
|
|
506
569
|
return True
|
|
507
|
-
|
|
570
|
+
|
|
508
571
|
# Escape - cancel current polygon or deselect or close help
|
|
509
572
|
elif event.key == pygame.K_ESCAPE:
|
|
510
573
|
if self.show_help:
|
|
@@ -517,44 +580,45 @@ class CollisionPainter:
|
|
|
517
580
|
self.selected_polygon_idx = None
|
|
518
581
|
self.selected_vertex_idx = None
|
|
519
582
|
return True
|
|
520
|
-
|
|
521
|
-
# Delete - remove selected polygon
|
|
583
|
+
|
|
584
|
+
# Delete/Backspace - remove selected polygon or last vertex
|
|
522
585
|
elif event.key in (pygame.K_DELETE, pygame.K_BACKSPACE):
|
|
523
586
|
if self.selected_polygon_idx is not None:
|
|
524
587
|
self._delete_polygon(self.selected_polygon_idx)
|
|
525
588
|
return True
|
|
526
589
|
elif self.current_polygon:
|
|
527
|
-
# Remove last vertex
|
|
528
590
|
self.current_polygon.pop()
|
|
529
591
|
return True
|
|
530
|
-
|
|
531
|
-
# G - toggle grid
|
|
532
|
-
elif event.key == pygame.K_g:
|
|
592
|
+
|
|
593
|
+
# Ctrl+G - toggle grid
|
|
594
|
+
elif ctrl and event.key == pygame.K_g:
|
|
533
595
|
self.show_grid = not self.show_grid
|
|
534
596
|
return True
|
|
535
|
-
|
|
536
|
-
# S - toggle snap (
|
|
537
|
-
elif event.key == pygame.K_s:
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
# O - toggle one-way collision for selected polygon
|
|
545
|
-
elif event.key == pygame.K_o:
|
|
597
|
+
|
|
598
|
+
# Ctrl+Shift+S - toggle snap (Ctrl+S reserved for save)
|
|
599
|
+
elif ctrl and event.key == pygame.K_s and (mods & pygame.KMOD_SHIFT):
|
|
600
|
+
self.snap_to_grid = not self.snap_to_grid
|
|
601
|
+
return True
|
|
602
|
+
|
|
603
|
+
# Ctrl+O - toggle one-way collision for selected polygon
|
|
604
|
+
elif ctrl and event.key == pygame.K_o:
|
|
546
605
|
if self.selected_polygon_idx is not None:
|
|
547
606
|
idx = self.selected_polygon_idx
|
|
548
607
|
self.polygon_one_way[idx] = not self.polygon_one_way[idx]
|
|
549
608
|
if self.on_polygon_modified:
|
|
550
609
|
self.on_polygon_modified(idx)
|
|
551
610
|
return True
|
|
552
|
-
|
|
553
|
-
# R - reset view
|
|
554
|
-
elif event.key == pygame.K_r:
|
|
611
|
+
|
|
612
|
+
# Ctrl+R - reset view
|
|
613
|
+
elif ctrl and event.key == pygame.K_r:
|
|
555
614
|
self._center_view()
|
|
556
615
|
self.zoom = 2.0
|
|
557
616
|
return True
|
|
617
|
+
|
|
618
|
+
# Ctrl+A - toggle angle hints
|
|
619
|
+
elif ctrl and event.key == pygame.K_a:
|
|
620
|
+
self.show_angle_hints = not self.show_angle_hints
|
|
621
|
+
return True
|
|
558
622
|
|
|
559
623
|
if event.type == pygame.KEYUP:
|
|
560
624
|
if event.key in (pygame.K_LSHIFT, pygame.K_RSHIFT):
|
|
@@ -744,20 +808,21 @@ class CollisionPainter:
|
|
|
744
808
|
("Right-click / Enter", "Complete polygon"),
|
|
745
809
|
("Escape", "Cancel current polygon"),
|
|
746
810
|
("Shift (edge mode)", "Constrain to axis"),
|
|
747
|
-
("E", "Toggle edge draw mode"),
|
|
811
|
+
("Ctrl+E", "Toggle edge draw mode"),
|
|
748
812
|
]),
|
|
749
813
|
("SELECTION", [
|
|
750
814
|
("Left-click polygon", "Select polygon"),
|
|
751
815
|
("Left-click vertex", "Select & drag vertex"),
|
|
752
816
|
("Delete / Backspace", "Remove selected polygon"),
|
|
753
|
-
("O", "Toggle one-way collision"),
|
|
817
|
+
("Ctrl+O", "Toggle one-way collision"),
|
|
754
818
|
]),
|
|
755
819
|
("VIEW", [
|
|
756
820
|
("Mouse wheel", "Zoom in/out"),
|
|
757
821
|
("Middle mouse / Space+LMB", "Pan view"),
|
|
758
|
-
("G", "Toggle grid"),
|
|
759
|
-
("S", "Toggle snap to grid"),
|
|
760
|
-
("R", "Reset view"),
|
|
822
|
+
("Ctrl+G", "Toggle grid"),
|
|
823
|
+
("Ctrl+Shift+S", "Toggle snap to grid"),
|
|
824
|
+
("Ctrl+R", "Reset view"),
|
|
825
|
+
("Ctrl+A", "Toggle angle hints"),
|
|
761
826
|
]),
|
|
762
827
|
("MISC", [
|
|
763
828
|
("Info button (top-right)", "Toggle this help panel"),
|
|
@@ -905,6 +970,24 @@ class CollisionPainter:
|
|
|
905
970
|
radius = VERTEX_HOVER_RADIUS if is_hovered_vertex else VERTEX_RADIUS
|
|
906
971
|
pygame.draw.circle(screen, color, (px, py), radius)
|
|
907
972
|
pygame.draw.circle(screen, (0, 0, 0), (px, py), radius, 1)
|
|
973
|
+
|
|
974
|
+
# One-way badge on selected polygon
|
|
975
|
+
if selected and one_way and len(polygon) >= 3:
|
|
976
|
+
cx = int(sum(p[0] for p in screen_points) / len(screen_points))
|
|
977
|
+
cy = int(sum(p[1] for p in screen_points) / len(screen_points))
|
|
978
|
+
badge = self._font_sm.render("ONE-WAY", True, (255, 255, 255))
|
|
979
|
+
bx = cx - badge.get_width() // 2
|
|
980
|
+
by = cy - badge.get_height() // 2
|
|
981
|
+
bg = pygame.Surface((badge.get_width() + 8, badge.get_height() + 4), pygame.SRCALPHA)
|
|
982
|
+
bg.fill((_COLORS["one_way"][0], _COLORS["one_way"][1], _COLORS["one_way"][2], 200))
|
|
983
|
+
screen.blit(bg, (bx - 4, by - 2))
|
|
984
|
+
screen.blit(badge, (bx, by))
|
|
985
|
+
|
|
986
|
+
# Angle hints
|
|
987
|
+
if self.show_angle_hints:
|
|
988
|
+
for i, (px, py) in enumerate(screen_points):
|
|
989
|
+
angle = self._get_interior_angle(polygon, i)
|
|
990
|
+
self._draw_angle_hint(screen, (px, py), angle)
|
|
908
991
|
|
|
909
992
|
def _draw_current_polygon(self, screen: pygame.Surface) -> None:
|
|
910
993
|
"""Draw the polygon currently being drawn"""
|
|
@@ -934,6 +1017,25 @@ class CollisionPainter:
|
|
|
934
1017
|
radius = VERTEX_HOVER_RADIUS if is_first else VERTEX_RADIUS
|
|
935
1018
|
pygame.draw.circle(screen, color, (px, py), radius)
|
|
936
1019
|
pygame.draw.circle(screen, (0, 0, 0), (px, py), radius, 1)
|
|
1020
|
+
|
|
1021
|
+
# Angle hints for vertices with both neighbors
|
|
1022
|
+
if self.show_angle_hints and len(self.current_polygon) >= 3:
|
|
1023
|
+
for i, (px, py) in enumerate(screen_points):
|
|
1024
|
+
if i == 0 or i == len(screen_points) - 1:
|
|
1025
|
+
continue
|
|
1026
|
+
poly = self.current_polygon
|
|
1027
|
+
prev = poly[i - 1]
|
|
1028
|
+
curr = poly[i]
|
|
1029
|
+
nxt = poly[i + 1]
|
|
1030
|
+
v1 = (prev[0] - curr[0], prev[1] - curr[1])
|
|
1031
|
+
v2 = (nxt[0] - curr[0], nxt[1] - curr[1])
|
|
1032
|
+
dot = v1[0] * v2[0] + v1[1] * v2[1]
|
|
1033
|
+
mag1 = math.hypot(v1[0], v1[1])
|
|
1034
|
+
mag2 = math.hypot(v2[0], v2[1])
|
|
1035
|
+
if mag1 > 0.001 and mag2 > 0.001:
|
|
1036
|
+
cos_a = max(-1.0, min(1.0, dot / (mag1 * mag2)))
|
|
1037
|
+
angle = math.degrees(math.acos(cos_a))
|
|
1038
|
+
self._draw_angle_hint(screen, (px, py), angle)
|
|
937
1039
|
|
|
938
1040
|
def _draw_status(self, screen: pygame.Surface) -> None:
|
|
939
1041
|
"""Draw status text"""
|
|
@@ -949,12 +1051,13 @@ class CollisionPainter:
|
|
|
949
1051
|
one_way = self.polygon_one_way[self.selected_polygon_idx]
|
|
950
1052
|
one_way_str = " (ONE-WAY)" if one_way else ""
|
|
951
1053
|
lines.append(f"Selected polygon {self.selected_polygon_idx}{one_way_str}")
|
|
952
|
-
lines.append("Delete to remove, O to toggle one-way")
|
|
1054
|
+
lines.append("Delete to remove, Ctrl+O to toggle one-way")
|
|
953
1055
|
else:
|
|
954
1056
|
lines.append("Click to add vertices, right-click to complete")
|
|
955
1057
|
|
|
956
|
-
|
|
957
|
-
lines.append(f"
|
|
1058
|
+
snap_str = f"Snap: {'ON' if self.snap_to_grid else 'OFF'} (Ctrl+Shift+S)"
|
|
1059
|
+
lines.append(f"Zoom: {self.zoom:.1f}x | Grid: {'ON' if self.show_grid else 'OFF'} (Ctrl+G) | {snap_str}")
|
|
1060
|
+
lines.append(f"Polygons: {len(self.polygons)} | Ctrl+R: reset | Ctrl+A: angle hints {'ON' if self.show_angle_hints else 'OFF'}")
|
|
958
1061
|
|
|
959
1062
|
y = self.rect.y + 5
|
|
960
1063
|
for line in lines:
|