From 0e9a655ea11212e8a22fe88e658bf1ec932fc4e1 Mon Sep 17 00:00:00 2001 From: Marc Mintel Date: Wed, 17 Sep 2025 19:43:11 +0200 Subject: [PATCH] wip --- __pycache__/__init__.cpython-311.pyc | Bin 10201 -> 0 bytes .../__pycache__/sync_version.cpython-311.pyc | Bin 0 -> 11321 bytes build/features.json.backup | 67 -- build/file_exclusions.json | 12 +- build/sync_version.py | 7 + build/templates/constants.py.template | 145 ++- build/validate_package.py | 94 ++ dist/text_texture_generator_v1.0.0.zip | Bin 60067 -> 61200 bytes dist/text_texture_generator_v1.0.0_free.zip | Bin 37894 -> 41348 bytes src/__init__.py | 254 ++++- src/__pycache__/__init__.cpython-311.pyc | Bin 10255 -> 12324 bytes src/core/__init__.py | 15 +- src/core/__pycache__/__init__.cpython-311.pyc | Bin 1431 -> 1710 bytes .../generation_engine.cpython-311.pyc | Bin 14566 -> 21555 bytes .../__pycache__/normal_maps.cpython-311.pyc | Bin 4627 -> 4100 bytes .../__pycache__/text_fitting.cpython-311.pyc | Bin 14690 -> 11488 bytes .../text_processor.cpython-311.pyc | Bin 15830 -> 13113 bytes src/core/generation_engine.py | 151 ++- src/core/normal_maps.py | 29 +- src/core/text_fitting.py | 71 +- src/core/text_processor.py | 80 +- src/operators/__init__.py | 119 +- .../__pycache__/__init__.cpython-311.pyc | Bin 1812 -> 2181 bytes .../generation_ops.cpython-311.pyc | Bin 25154 -> 23465 bytes .../__pycache__/preset_ops.cpython-311.pyc | Bin 47576 -> 41133 bytes .../__pycache__/utility_ops.cpython-311.pyc | Bin 31682 -> 25094 bytes src/operators/generation_ops.py | 157 ++- src/operators/preset_ops.py | 157 ++- src/operators/utility_ops.py | 163 ++- .../__pycache__/__init__.cpython-311.pyc | Bin 530 -> 530 bytes .../__pycache__/migration.cpython-311.pyc | Bin 6724 -> 5977 bytes .../storage_system.cpython-311.pyc | Bin 32024 -> 21607 bytes src/presets/migration.py | 23 +- src/presets/storage_system.py | 223 ++-- .../__pycache__/__init__.cpython-311.pyc | Bin 578 -> 578 bytes .../property_groups.cpython-311.pyc | Bin 26556 -> 26261 bytes src/properties/property_groups.py | 24 +- src/ui/__init__.py | 3 + src/ui/__pycache__/__init__.cpython-311.pyc | Bin 1280 -> 1236 bytes src/ui/__pycache__/panels.cpython-311.pyc | Bin 42676 -> 58343 bytes src/ui/__pycache__/preview.cpython-311.pyc | Bin 7967 -> 8551 bytes src/ui/panels.py | 1009 ++++++++++++++--- src/ui/preview.py | 55 +- .../__pycache__/__init__.cpython-311.pyc | Bin 501 -> 501 bytes .../__pycache__/constants.cpython-311.pyc | Bin 2625 -> 6975 bytes .../__pycache__/feature_flags.cpython-311.pyc | Bin 9490 -> 0 bytes src/utils/__pycache__/system.cpython-311.pyc | Bin 4225 -> 4128 bytes src/utils/constants.py | 174 ++- src/utils/feature_flags.py.backup | 150 --- src/utils/system.py | 17 + .../pytest_plugins.cpython-311.pyc | Bin 3381 -> 0 bytes .../__pycache__/__init__.cpython-311.pyc | Bin 257 -> 0 bytes ...tion_pipeline.cpython-311-pytest-8.3.5.pyc | Bin 20428 -> 0 bytes ...rror_handling.cpython-311-pytest-8.3.5.pyc | Bin 0 -> 46457 bytes ..._text_fitting.cpython-311-pytest-8.3.5.pyc | Bin 62561 -> 0 bytes ...fferentiation.cpython-311-pytest-8.3.5.pyc | Bin 0 -> 63163 bytes tests/unit/test_import_error_handling.py | 256 +++++ tests/unit/test_version_differentiation.py | 258 +++++ 58 files changed, 2821 insertions(+), 892 deletions(-) delete mode 100644 __pycache__/__init__.cpython-311.pyc create mode 100644 build/__pycache__/sync_version.cpython-311.pyc delete mode 100644 build/features.json.backup delete mode 100644 src/utils/__pycache__/feature_flags.cpython-311.pyc delete mode 100644 src/utils/feature_flags.py.backup delete mode 100644 tests/__pycache__/pytest_plugins.cpython-311.pyc delete mode 100644 tests/integration/__pycache__/__init__.cpython-311.pyc delete mode 100644 tests/integration/__pycache__/test_text_generation_pipeline.cpython-311-pytest-8.3.5.pyc create mode 100644 tests/unit/__pycache__/test_import_error_handling.cpython-311-pytest-8.3.5.pyc delete mode 100644 tests/unit/__pycache__/test_text_fitting.cpython-311-pytest-8.3.5.pyc create mode 100644 tests/unit/__pycache__/test_version_differentiation.cpython-311-pytest-8.3.5.pyc create mode 100644 tests/unit/test_import_error_handling.py create mode 100644 tests/unit/test_version_differentiation.py diff --git a/__pycache__/__init__.cpython-311.pyc b/__pycache__/__init__.cpython-311.pyc deleted file mode 100644 index a838ab35bbb89e1d279d124cac0e6b18b316b63a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10201 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 MsS)iTM^OI%01m&m{Qv*} literal 0 HcmV?d00001 diff --git a/build/features.json.backup b/build/features.json.backup deleted file mode 100644 index d71fe6e..0000000 --- a/build/features.json.backup +++ /dev/null @@ -1,67 +0,0 @@ -{ - "features": { - "basic": { - "name": "Basic Text Generation", - "description": "Core text texture generation functionality", - "enabled_by_default": true, - "components": [ - "core.text_processor", - "core.generation_engine", - "ui.panels.basic" - ] - }, - "preview": { - "name": "Live Preview", - "description": "Real-time preview of text textures", - "enabled_by_default": true, - "components": ["ui.preview", "core.text_fitting"] - }, - "advanced_styling": { - "name": "Advanced Styling Options", - "description": "Extended styling and customization options", - "enabled_by_default": false, - "components": ["ui.panels.advanced", "core.advanced_styling"] - }, - "normal_maps": { - "name": "Normal Map Generation", - "description": "Generate normal maps from text", - "enabled_by_default": false, - "components": ["core.normal_maps", "ui.panels.normal_maps"] - }, - "presets": { - "name": "Preset Management", - "description": "Save and load text style presets", - "enabled_by_default": false, - "components": [ - "presets.storage_system", - "operators.preset_ops", - "ui.panels.presets" - ] - }, - "batch_processing": { - "name": "Batch Processing", - "description": "Process multiple text textures at once", - "enabled_by_default": false, - "components": ["operators.batch_ops", "ui.panels.batch"] - } - }, - "version_configs": { - "free": { - "enabled_features": ["basic", "preview"], - "max_texture_size": 1024, - "max_concurrent_operations": 1 - }, - "full": { - "enabled_features": [ - "basic", - "preview", - "advanced_styling", - "normal_maps", - "presets", - "batch_processing" - ], - "max_texture_size": 4096, - "max_concurrent_operations": 4 - } - } -} diff --git a/build/file_exclusions.json b/build/file_exclusions.json index 605465a..cb04e9f 100644 --- a/build/file_exclusions.json +++ b/build/file_exclusions.json @@ -6,11 +6,11 @@ "description": "Free version - core text texture generation only", "exclude_files": [ "core/normal_maps.py", - "operators/preset_ops.py", + "presets/", "operators/utility_ops.py", - "presets/" + "operators/preset_ops.py" ], - "exclude_patterns": ["*batch*", "*advanced*"], + "exclude_patterns": ["*batch*", "*advanced*", "*overlay*", "*utility*"], "max_texture_size": 1024, "max_concurrent_operations": 1 }, @@ -25,6 +25,10 @@ "feature_to_files_mapping": { "normal_maps": ["core/normal_maps.py"], "presets": ["presets/", "operators/preset_ops.py"], - "batch_processing": ["operators/utility_ops.py"] + "batch_processing": ["operators/utility_ops.py"], + "image_overlays": ["operators/utility_ops.py"], + "basic_utilities": ["operators/utility_ops.py"], + "advanced_utilities": ["operators/utility_ops.py"], + "advanced_styling": [] } } diff --git a/build/sync_version.py b/build/sync_version.py index 92852f1..56a448a 100755 --- a/build/sync_version.py +++ b/build/sync_version.py @@ -66,6 +66,12 @@ def create_template_variables(config: Dict, version: str, version_type: str = "f # Get version-specific configuration version_config = exclusions_config['version_configs'][version_type] + # Define features based on version type + if version_type == "free": + enabled_features = ["basic", "preview", "image_overlays", "basic_utilities"] + else: # full version + enabled_features = ["basic", "preview", "image_overlays", "basic_utilities", "advanced_styling", "normal_maps", "presets", "batch_processing", "advanced_utilities"] + # Parse Blender version blender_version = config['project']['blender_version_min'] blender_parts = blender_version.split('.') @@ -98,6 +104,7 @@ def create_template_variables(config: Dict, version: str, version_type: str = "f 'BLENDER_VERSION_MINOR': str(blender_minor), 'BLENDER_VERSION_PATCH': str(blender_patch), 'DEPENDENCIES': dependencies_str, + 'ENABLED_FEATURES': json.dumps(enabled_features), 'MAX_TEXTURE_SIZE': str(version_config['max_texture_size']), 'MAX_CONCURRENT_OPERATIONS': str(version_config['max_concurrent_operations']) } diff --git a/build/templates/constants.py.template b/build/templates/constants.py.template index bf08e10..75d1d23 100644 --- a/build/templates/constants.py.template +++ b/build/templates/constants.py.template @@ -2,26 +2,39 @@ Constants and metadata for the Text Texture Generator addon. """ +# Version information +VERSION_TYPE = "{{VERSION_TYPE}}" # "free" or "full" + bl_info = { - "name": "{{PROJECT_NAME}}", + "name": "{{PROJECT_NAME}}" + (" Free" if VERSION_TYPE == "free" else " Full"), "author": "{{PROJECT_AUTHOR}}", "version": ({{VERSION_MAJOR}}, {{VERSION_MINOR}}, {{VERSION_PATCH}}), "blender": ({{BLENDER_VERSION_MAJOR}}, {{BLENDER_VERSION_MINOR}}, {{BLENDER_VERSION_PATCH}}), "location": "{{PROJECT_LOCATION}}", - "description": "{{PROJECT_DESCRIPTION}}", + "description": "{{PROJECT_DESCRIPTION}}" + (" - Free Version (Limited Features)" if VERSION_TYPE == "free" else " - Full Version"), "category": "{{PROJECT_CATEGORY}}", } -# Version information -VERSION_TYPE = "{{VERSION_TYPE}}" # "free" or "full" +# Feature configuration based on version type +ENABLED_FEATURES = ["basic", "preview"] if VERSION_TYPE == "free" else [ + "basic", "preview", "presets", "normal_maps", "batch_processing", + "image_overlays", "basic_utilities", "advanced_utilities", "advanced_styling", + "stroke_effects", "blur_effects", "shadow_glow_effects", "shader_generation" +] # Version-specific limits (applied at build time through file exclusions) def get_version_limits(): """Get version-specific limits.""" - return { - "max_texture_size": {{MAX_TEXTURE_SIZE}}, - "max_concurrent_operations": {{MAX_CONCURRENT_OPERATIONS}} - } + if VERSION_TYPE == "free": + return { + "max_texture_size": {{MAX_TEXTURE_SIZE}}, + "max_concurrent_operations": {{MAX_CONCURRENT_OPERATIONS}} + } + else: + return { + "max_texture_size": 4096, + "max_concurrent_operations": 4 + } def is_free_version(): """Check if this is the free version.""" @@ -29,4 +42,118 @@ def is_free_version(): def is_full_version(): """Check if this is the full version.""" - return VERSION_TYPE == "full" \ No newline at end of file + return VERSION_TYPE == "full" + +# UI Helper Functions for Version Display +def get_version_badge_text(): + """Get version badge text for UI display.""" + return "FREE" if is_free_version() else "PRO" + +def get_version_badge_icon(): + """Get appropriate icon for version badge.""" + return 'GIFT' if is_free_version() else 'STAR' + +def get_upgrade_hint_text(feature_name): + """Get contextual upgrade hint text for specific features.""" + if not is_free_version(): + return "" + + return f"Upgrade to Pro version to access {feature_name}" + +# Feature availability functions +def has_feature(feature_name): + """Check if a specific feature is available.""" + return feature_name in ENABLED_FEATURES + +def has_image_overlays(): + """Check if image overlay features are available""" + return has_feature("image_overlays") + +def has_basic_utilities(): + """Check if basic utility operations are available""" + return has_feature("basic_utilities") + +def has_presets(): + """Check if preset features are available""" + return has_feature("presets") + +def has_normal_maps(): + """Check if normal map generation is available""" + return has_feature("normal_maps") + +def has_batch_processing(): + """Check if batch processing is available""" + return has_feature("batch_processing") + +def has_advanced_utilities(): + """Check if advanced utility operations are available""" + return has_feature("advanced_utilities") + +def has_advanced_styling(): + """Check if advanced styling options are available""" + return has_feature("advanced_styling") + +def has_stroke_effects(): + """Check if stroke effects are available""" + return has_feature("stroke_effects") + +def has_blur_effects(): + """Check if blur effects are available""" + return has_feature("blur_effects") + +def has_shadow_glow_effects(): + """Check if shadow and glow effects are available""" + return has_feature("shadow_glow_effects") + +def has_shader_generation(): + """Check if shader generation is available""" + return has_feature("shader_generation") + +def get_max_resolution(): + """Get maximum texture resolution based on version""" + limits = get_version_limits() + return limits.get("max_texture_size", 1024) + +def should_show_feature_lock(feature_name): + """Check if feature lock indicator should be shown.""" + # Map feature names to their availability functions + feature_checks = { + 'presets': has_presets, + 'normal_maps': has_normal_maps, + 'batch_processing': has_batch_processing, + 'advanced_utilities': has_advanced_utilities, + 'advanced_styling': has_advanced_styling, + 'image_overlays': has_image_overlays, + 'basic_utilities': has_basic_utilities, + 'stroke_effects': has_stroke_effects, + 'blur_effects': has_blur_effects, + 'shadow_glow_effects': has_shadow_glow_effects, + 'shader_generation': has_shader_generation + } + + if is_full_version(): + return False + + # For free version, show lock if feature is not available + if feature_name in feature_checks: + return not feature_checks[feature_name]() + + return True # Show lock for unknown features + +def get_feature_availability_text(): + """Get text describing current feature availability.""" + if is_free_version(): + return "Free version - Basic features available" + return "Pro version - All features available" + +def get_version_info_details(): + """Get detailed version information for display.""" + limits = get_version_limits() + info = { + 'version_type': VERSION_TYPE.upper(), + 'max_texture_size': limits.get('max_texture_size', 'Unlimited'), + 'max_concurrent_operations': limits.get('max_concurrent_operations', 'Unlimited'), + 'features_enabled': len(ENABLED_FEATURES), + 'upgrade_available': is_free_version() + } + return info \ No newline at end of file diff --git a/build/validate_package.py b/build/validate_package.py index 6adeb3f..e6d2823 100755 --- a/build/validate_package.py +++ b/build/validate_package.py @@ -186,6 +186,92 @@ def validate_constants(zip_path: Path, config: Dict, version_type: str) -> Valid return result +def validate_version_differences(zip_path: Path, config: Dict, version_type: str) -> ValidationResult: + """Validate version-specific content and file exclusions.""" + result = ValidationResult() + addon_id = config['project']['id'] + + # Load exclusions configuration + exclusions_path = get_project_root() / "build" / "file_exclusions.json" + try: + with open(exclusions_path, 'r') as f: + exclusions_config = json.load(f) + except Exception as e: + result.add_error(f"Could not load file exclusions config: {e}") + return result + + version_config = exclusions_config['version_configs'].get(version_type, {}) + excluded_files = version_config.get('exclude_files', []) + excluded_patterns = version_config.get('exclude_patterns', []) + + try: + with zipfile.ZipFile(zip_path, 'r') as zip_file: + file_list = zip_file.namelist() + + # Check that excluded files are actually excluded in free version + if version_type == "free": + excluded_found = [] + for excluded_file in excluded_files: + full_path = f"{addon_id}/{excluded_file}" + if full_path in file_list or any(f.startswith(full_path) for f in file_list): + excluded_found.append(excluded_file) + + if excluded_found: + result.add_error(f"Free version contains excluded files: {excluded_found}") + else: + result.add_info(f"Correctly excluded {len(excluded_files)} premium files from free version") + + # Check for excluded patterns + pattern_violations = [] + for file_path in file_list: + for pattern in excluded_patterns: + if pattern.strip('*').lower() in file_path.lower(): + pattern_violations.append((file_path, pattern)) + + if pattern_violations: + result.add_warning(f"Files matching excluded patterns found: {pattern_violations}") + + elif version_type == "full": + # Full version should contain premium files + missing_premium = [] + for premium_file in excluded_files: + full_path = f"{addon_id}/{premium_file}" + if full_path not in file_list and not any(f.startswith(full_path) for f in file_list): + missing_premium.append(premium_file) + + if missing_premium: + result.add_error(f"Full version missing premium files: {missing_premium}") + else: + result.add_info(f"Full version correctly includes all {len(excluded_files)} premium features") + + # Validate UI helper functions are present in constants.py + constants_path = f"{addon_id}/utils/constants.py" + if constants_path in file_list: + constants_content = zip_file.read(constants_path).decode('utf-8') + ui_functions = [ + 'get_version_badge_text', + 'get_version_badge_icon', + 'get_upgrade_hint_text', + 'should_show_feature_lock', + 'get_feature_availability_text', + 'get_version_info_details' + ] + + missing_ui_functions = [] + for func in ui_functions: + if func not in constants_content: + missing_ui_functions.append(func) + + if missing_ui_functions: + result.add_error(f"Missing UI helper functions in constants.py: {missing_ui_functions}") + else: + result.add_info("All UI helper functions present in constants.py") + + except Exception as e: + result.add_error(f"Error validating version differences: {e}") + + return result + def validate_feature_flags(zip_path: Path, config: Dict) -> ValidationResult: """Validate the feature flags system.""" result = ValidationResult() @@ -278,6 +364,14 @@ def validate_package(zip_path: Path, version_type: Optional[str] = None) -> bool features_result.print_results() overall_valid &= features_result.is_valid + print("\n" + "─" * 60) + + # Version differences validation + print("Validating version-specific content and exclusions...") + version_diff_result = validate_version_differences(zip_path, config, version_type) + version_diff_result.print_results() + overall_valid &= version_diff_result.is_valid + print("\n" + "=" * 60) print(f"Overall Validation: {'✅ PASS' if overall_valid else '❌ FAIL'}") diff --git a/dist/text_texture_generator_v1.0.0.zip b/dist/text_texture_generator_v1.0.0.zip index 4eb9b1c7eb98fa3cbe6385981b6d4f386702bc9b..e6d2cec345d4637abb5415a61f670cb91e30ec00 100644 GIT binary patch delta 58775 zcmZs?V{o8d&^8!zV%xTDCllLFCU!Ex%pKddZQGjIwrxB6yl?H+w^h4;`gHZxr+=KP zQ+-vVU=cia3mi{L4jcj<1Ox;IL_eNI*WOBs85#}*B%dAxgfyWK8W%7$w6eEyH8f;& z^xV|Zby#mj{rg-)oC&?|YeQe_0NT!Nb%qW#M=70oMTj2ME8RG$pN1}7R;_!`lYhApwcA%>{h#_>mT}Jrj#5I+`aRct)N08+l^v2rV;ei=+1^d(R%dZD_0*|yP2JMQ- zsFmkfin4|U6`C?@IzckcZ21v9Qu5K|oQ(KALiZ^b3De1mUxSm_H?zf(qN|`Lq$|rFpS3{bXWoN5b^dAp}svWm=-zb&@DVWuHwAV@^3Y z%JX_T8nK~73)RQK42>7p#BMySkIu`AS$N?czO?7vZhn6!K!1p586s;^>lts$l6EY* zsXJv2+HLUFgm`nlYmZZ@^ny@HU;NM0u zmja*!9}>;=?0c3l30N{>BNZ$hfdwe~0jac3oPqmo)=70-wyOr{h{mP=_K69ZSkE6~ z=b7k2Q{P`+qfA&d&RB;fedZy2deTQZK9dWg(O!$l!A5dSWX?!~f@#l^j`i|lD$)9v zVOKr$%!RU=B!#lJM#YTsP%jWp{A?L(Sb-7sD0I)$L-B;}RQsK}@9T}DwvpV{Qtt(1 z%ITKuILBbNg%3LzxyAcnN3}nWu~loGbrfmbw(qs1l+a+#7|IRv$5ck=mun%6RbqYD zvt_fW6T0OBXxhHK5TcUwMzNQLydX11vxIAFUZJdH#MmqZ>skh&!P2#XNSJp3qs z7PJ10MFf!PSCn#s>#`S_hko;SZ?H(?gS?dnAmFQbHC>ac z+CI~eqf2vNn@|$((BIm|<$PzRi7NNTONYZ|-(IEel?_EiJLQj(Emr0Z-l3RYf&I>d zAn?r19ZcZmzW~$v5kg3++@o^VZiAx8nyV9mn4#Lj6k`oEZlrNV5AQ z^!`G#(-TgOdyp2r2Ed%V`=b1bT385c?a?WfRnV@?j1KI$hh;Y_4cNaKCI z{TkNer+234YNJcEdn$k>p9M-~&k@;@kB~@>wOG$*NtI)9VD6*gwAQic<>u)mU#$UTCD4_L2G`CUYKn5T@^<{p_yQ!O2r5=iBl*cu zIJswv_hzY6F)qv$N8xM3Vk8MbxKWm2)r2&HDZHNjBBTjt`kY>?5wurYlzbtCaSs2U zW$TMMmBPR^;KQE)*l6$t21y<2V1HxX59Xgo}TN2#@_Y}SSFL4fwYP+T!fj1c6)I3 z$uoI*I)%kJ7=wehu2clpkF)(DXtumlNJ@Q8k|KZg;NtKjH;FcNCm{{J2J`d?23yOC z$yc8Kf~nTNRHDA7TSpUgvnDTUDckRS$B468YV z0VO%pv{&=V27O}_)B@H$LddGXe7bt`hTjtDZ(F{iH^LxPGgiIW?ViP?cP`ig{H9|% z#Wl*B z={Ae7n6|}u|AJd;=qowvSP#D84S*8r@^eWMlliGMRDY*6CYN*lU7Y6(Q-;db44uL& zpbW8&1KhEmDDF41|52jktfJZG61YH4s0eV;_3!)I=C=FJq*AU+K*H2ff@lk``&Eh3eK}`Kw+X-Vd)$REfZ08 zbK!O(yRM4hcE>ga{$mK&zR(!cDz&i)dZa4mALE}WU)6gbo<_}n?1nO$7* zr5Fl!qr^X3YGpDSK7-lMrz|lQthM;dLN+)F)aj`0HWdL<2Q&MTc(`np0ds|4+3})v z6qaC+_%5^oPpMIuLv-J~ciMZ1W_Yzf){@b;iP6HFkMNGB zQq|?8-spt&eX@2oN}2hqZlcL4*JZ0R1zv3k4qYskM5l&qIReAx(*78VXBjrf50bRH zAaoj4GfLk$t3dEN^IC8RLNQ=hp_qa+q37U&aF8#^PvxV?R)I2s&!UvYnl+3a?H*;+ zYIrZ$Gy)z7cb;6+4fi+yKtO3ZA;S`_HtFY3zPOI3OGdr1^H3u`cT!LLZX@+Q%5Ghpa$_fra*-npqHPX#g zsy&G3#E#E_~b6UoxqKH&d^w7*Du;y`YzEB-O=7xIY+o5okj&;Eq`?lR>_3TzkBc85 zR)%{DJ)P#m)_`jr1gMU1YB&_yGt3Npwfy)fteW<(aP~7yT?u-SCSQTJUkz4DC8*k> z79vF#q2pl`US6)jANnk3)a|WaA_^DI=t4NQ(B^jQP`L*0gj(hrN-)xRWOS=)eDVrD zp1MQ>yoz~zH#ist#e1v%8Yx|C2u-d=HJN`r7XKc8TYlivd=BS7u3hKue$jn%F2?Q3$~4==5iPwpr!Je4{SY;M1r1~ z&3BH|2Bt48$1_j8pwc}qYv4C)P~IZr34A8|`y1yHrmVH{6n#UkH!&g~*Km#6NAxi_ z=lU0tJ7CnP(?@1PHxaH28y&PHsS{jl2~MVQ?IlIL(Oud(Mo7%`xTJLVD`rK>|E{-_ z7rXy0J-`EWuX=f{))v(te`s65VfKaqo%3yT!w`B!S%Bvf*PB+2kfXq^Z}(Yg9-*1- z?)Ih#H5w8ohl>n*>0&|8yB%pg3WXfzhn-D!JRnD%V%32dymLh$uQuYV`pyWOrP6sc zNo^Yuc6sa4k^%N+*VI*xDs?LtzIAybC@-tX2s9*+UCmU!+P>r&O~bT&T2fHWA14SS{Vu z8DQ+~{Y_Tm-EyB)j?d)^Ry1~Q)8yN1owAj*bZNb+mfmjl2+=on6{w0GcYjq%wz8*+ zZGaC3d6n-ZwNiqs?3OTBy}H{=`67V!1i@}zweMjR%e9;TWsum)B^Wti2fEs_Uc7Mc z1`j)y*AR++par$b`vyNR@t(?;9kq#*0XqpkM?rFi4?Xo5JG3N@8_jCSC{MM29e zFi=j_yW8bCGLp0;5?nmEA@>$w|1y(^tO2ntU;4`hu-2JzKMq7&WO!d&v|ZfW0$i_A z|IUR^4c`PlY}|WE^qjcqi$bU8eU^uxX|g-^JYQ#Inf<1_zG$0p&mqzMZ#N*bzr9!# zRz2G3|JCeFr`(}Ux>3P&UBucoQ;y=J+_g|29i(q2`Ir9-qbo7DAG@`DKR$SKnwWt* zcoEPetolAWk_VzrHDLy+RPA@9fq`G{V26PwgR>>aF6$wDCjHY(DWY<$y3D^Y+0xAC zn#kjwhb(5!x$lXpFV_kmc|&8WKAWDdmjVOJ*TiO1wR&x}tc&w^d-ez8hTc#73-1V; z7ggyQSIqm|11-I&1U`$v`Is!Wm$}r+3faj5mDi`~e6dcTJn3k!-XYxJfQo0|5a!HL z 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
    ~#aHs4fsDlFG>Xe@8j;_vv*%Oxg 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 Q?xh;C^!L)qpM>523y$y5Gynhq diff --git a/src/operators/__pycache__/preset_ops.cpython-311.pyc b/src/operators/__pycache__/preset_ops.cpython-311.pyc index 4c49c97959ba9367d4a393d5f548ad21545cf49d..a810a4d8250acae26d5fb88bacdd79002d06d810 100644 GIT binary patch delta 12349 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 literal 31682 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 F{y+W#q;&uQ diff --git a/src/operators/generation_ops.py b/src/operators/generation_ops.py index 1b34dec..568e229 100644 --- a/src/operators/generation_ops.py +++ b/src/operators/generation_ops.py @@ -13,6 +13,7 @@ from ..core.generation_engine import generate_texture_image, generate_preview class TEXT_TEXTURE_OT_generate(Operator): + pass """Generate final texture and pack it""" bl_idname = "text_texture.generate" bl_label = "Generate Text Texture" @@ -20,7 +21,9 @@ class TEXT_TEXTURE_OT_generate(Operator): bl_options = {'REGISTER', 'UNDO'} def execute(self, context): + pass try: + pass props = context.scene.text_texture_props # IMPORTANT: Generate at EXACT user-specified dimensions @@ -32,18 +35,22 @@ class TEXT_TEXTURE_OT_generate(Operator): # Generate at FULL resolution specified by user result = generate_texture_image(props, final_width, final_height) if not result: + pass self.report({'ERROR'}, "Failed to generate texture image") return {'CANCELLED'} # Extract diffuse and normal map images if isinstance(result, tuple): + pass img, normal_map_img = result else: + pass # Backward compatibility img = result normal_map_img = None if not img: + pass self.report({'ERROR'}, "Failed to generate diffuse image") return {'CANCELLED'} @@ -55,11 +62,13 @@ class TEXT_TEXTURE_OT_generate(Operator): sanitized_text = re.sub(r'[\s_-]+', '_', sanitized_text).strip('_') # Fallback if text becomes empty if not sanitized_text: + pass sanitized_text = "Texture" img_name = f"TextTexture_{sanitized_text}" # Remove existing diffuse texture if img_name in bpy.data.images: + pass bpy.data.images.remove(bpy.data.images[img_name]) # Create new diffuse image with USER SPECIFIED dimensions @@ -73,10 +82,12 @@ class TEXT_TEXTURE_OT_generate(Operator): # Create normal map texture if enabled and generated normal_map_blender_img = None if normal_map_img and props.generate_normal_map: + pass normal_map_name = f"TextTexture_Normal_{sanitized_text}" # Remove existing normal map texture if normal_map_name in bpy.data.images: + pass bpy.data.images.remove(bpy.data.images[normal_map_name]) # Create new normal map image @@ -87,16 +98,19 @@ class TEXT_TEXTURE_OT_generate(Operator): # Copy RGB channels and set alpha to 1.0 temp_pixels = list(normal_map_img.pixels[:]) for i in range(3, len(temp_pixels), 4): + pass temp_pixels[i] = 1.0 # Set alpha to 1.0 for normal maps normal_map_blender_img.pixels[:] = temp_pixels normal_map_blender_img.pack() # PACK NORMAL MAP - print(f"[Normal Map] Created and packed normal map texture: {normal_map_name}") # Add to shader if context.space_data and context.space_data.type == 'NODE_EDITOR': + pass if context.space_data.tree_type == 'ShaderNodeTree': + pass material = context.space_data.id if material and material.use_nodes: + pass nodes = material.node_tree.nodes # Add diffuse texture node @@ -108,25 +122,93 @@ class TEXT_TEXTURE_OT_generate(Operator): # Add normal map node if normal map was created if normal_map_blender_img: + pass normal_node = nodes.new(type='ShaderNodeTexImage') normal_node.image = normal_map_blender_img normal_node.location = (0, -300) normal_node.label = "Normal Map" - print(f"[Normal Map] Added normal map node to shader editor") # Provide comprehensive feedback if normal_map_blender_img: + pass self.report({'INFO'}, f"Generated and packed: {img_name} + {normal_map_blender_img.name} ({final_width}x{final_height}px)") else: + pass self.report({'INFO'}, f"Generated and packed: {img_name} ({final_width}x{final_height}px)") return {'FINISHED'} except Exception as e: + pass self.report({'ERROR'}, f"Failed: {str(e)}") return {'CANCELLED'} +class TEXT_TEXTURE_OT_preview(Operator): + pass + """Generate basic preview for free version users""" + bl_idname = "text_texture.preview" + bl_label = "Generate Preview" + bl_description = "Generate a basic preview of your text texture" + bl_options = {'REGISTER', 'UNDO'} + + def execute(self, context): + pass + try: + pass + # Import the preview function + from ..core.generation_engine import generate_preview + + # Get properties and generate preview with correct parameters + props = context.scene.text_texture_props + preview_size = props.preview_size if hasattr(props, 'preview_size') else 512 + result = generate_preview(props, preview_size, preview_size) + + if result: + pass + self.report({'INFO'}, f"Preview generated at {preview_size}px") + + # Open the preview image in the image editor + # Find or create an image editor area + for area in context.screen.areas: + pass + if area.type == 'IMAGE_EDITOR': + pass + for space in area.spaces: + pass + if space.type == 'IMAGE_EDITOR': + pass + space.image = result + area.tag_redraw() + break + break + else: + pass + # If no image editor found, try to open in UV editor or create viewer + try: + pass + # Try to show in any available viewer + bpy.ops.image.view_all('INVOKE_DEFAULT') + except: + pass + # Fallback message + self.report({'INFO'}, f"Preview '{result.name}' created - open Image Editor to view") + + return {'FINISHED'} + else: + pass + self.report({'ERROR'}, "Failed to generate preview - check console for details") + return {'CANCELLED'} + + except Exception as e: + pass + self.report({'ERROR'}, f"Preview failed: {str(e)}") + import traceback + traceback.print_exc() + return {'CANCELLED'} + + class TEXT_TEXTURE_OT_generate_shader(Operator): + pass """Generate complete shader with text texture""" bl_idname = "text_texture.generate_shader" bl_label = "Generate Shader" @@ -134,41 +216,39 @@ class TEXT_TEXTURE_OT_generate_shader(Operator): bl_options = {'REGISTER', 'UNDO'} def execute(self, context): + pass try: - print(f"[TTG SHADER DEBUG] === SHADER GENERATION STARTED ===") + pass props = context.scene.text_texture_props # Generate texture first final_width = props.texture_width final_height = props.texture_height - print(f"[TTG SHADER DEBUG] Step 1: Starting texture generation at {final_width}x{final_height}px") result = generate_texture_image(props, final_width, final_height) - print(f"[TTG SHADER DEBUG] Step 2: Texture generation returned: {type(result)}") if not result: + pass self.report({'ERROR'}, "Failed to generate texture image") return {'CANCELLED'} - print(f"[TTG SHADER DEBUG] Step 3: Processing result...") # Extract diffuse and normal map images if isinstance(result, tuple): + pass img, normal_map_img = result - print(f"[TTG SHADER DEBUG] Step 3a: Got tuple result - img: {type(img)}, normal: {type(normal_map_img)}") else: + pass # Backward compatibility img = result normal_map_img = None - print(f"[TTG SHADER DEBUG] Step 3b: Got single result - img: {type(img)}") if not img: + pass self.report({'ERROR'}, "Failed to generate diffuse image") return {'CANCELLED'} - print(f"[TTG SHADER DEBUG] Step 4: Image validation passed, proceeding with shader creation...") - print(f"[TTG SHADER DEBUG] Step 5: Creating diffuse texture...") # Create diffuse texture name with proper sanitization import re # Remove non-ASCII and non-alphanumeric characters, keep spaces temporarily @@ -177,74 +257,66 @@ class TEXT_TEXTURE_OT_generate_shader(Operator): sanitized_text = re.sub(r'[\s_-]+', '_', sanitized_text).strip('_') # Fallback if text becomes empty if not sanitized_text: + pass sanitized_text = "Texture" img_name = f"TextTexture_{sanitized_text}" - print(f"[TTG SHADER DEBUG] Step 5a: Sanitized texture name: {img_name}") # Remove existing diffuse texture if it exists if img_name in bpy.data.images: - print(f"[TTG SHADER DEBUG] Step 5b: Removing existing texture") + pass bpy.data.images.remove(bpy.data.images[img_name]) - print(f"[TTG SHADER DEBUG] Step 5c: Creating new Blender image...") # Create new diffuse texture blender_img = bpy.data.images.new(img_name, final_width, final_height, alpha=True) - print(f"[TTG SHADER DEBUG] Step 5d: New image created, copying pixel data...") # Directly copy pixel data from generated image to Blender image # The generated image is already in the correct format, just copy it blender_img.pixels[:] = img.pixels[:] - print(f"[TTG SHADER DEBUG] Step 5e: Pixel data copied, packing image...") blender_img.pack() - print(f"[TTG SHADER DEBUG] Step 5f: Image packed successfully") - print(f"[TTG SHADER DEBUG] Step 6: Processing normal maps...") # Create normal map texture if enabled and generated normal_map_blender_img = None if normal_map_img and props.generate_normal_map: - print(f"[TTG SHADER DEBUG] Step 6a: Normal map enabled and generated, processing...") + pass # Use the same sanitized text for consistency normal_map_name = f"TextTexture_Normal_{sanitized_text}" - print(f"[TTG SHADER DEBUG] Step 6b: Sanitized normal map name: {normal_map_name}") # Remove existing normal map texture if normal_map_name in bpy.data.images: - print(f"[TTG SHADER DEBUG] Step 6c: Removing existing normal map") + pass bpy.data.images.remove(bpy.data.images[normal_map_name]) - print(f"[TTG SHADER DEBUG] Step 6d: Creating normal map image...") # Create new normal map image normal_map_blender_img = bpy.data.images.new(normal_map_name, final_width, final_height, alpha=False) normal_map_blender_img.colorspace_settings.name = 'Non-Color' # Important for normal maps - print(f"[TTG SHADER DEBUG] Step 6e: Normal map image created, copying pixels...") # Directly copy normal map pixel data # Copy RGB channels and set alpha to 1.0 temp_pixels = list(normal_map_img.pixels[:]) for i in range(3, len(temp_pixels), 4): + pass temp_pixels[i] = 1.0 # Set alpha to 1.0 for normal maps normal_map_blender_img.pixels[:] = temp_pixels - print(f"[TTG SHADER DEBUG] Step 6f: Normal map pixels copied, packing...") normal_map_blender_img.pack() - print(f"[TTG SHADER DEBUG] Step 6g: Normal map packed successfully") else: - print(f"[TTG SHADER DEBUG] Step 6: Normal map generation skipped") + pass - print(f"[TTG SHADER DEBUG] Step 7: Creating material...") # Create or get material using custom name or fallback if props.custom_material_name.strip(): + pass material_name = props.custom_material_name.strip() - print(f"[TTG SHADER DEBUG] Step 7a: Using custom material name: {material_name}") else: + pass # Use the same sanitized text for consistency material_name = f"TextTexture_Mat_{sanitized_text}" - print(f"[TTG SHADER DEBUG] Step 7b: Using sanitized material name: {material_name}") if material_name in bpy.data.materials: + pass mat = bpy.data.materials[material_name] # Clear existing nodes mat.node_tree.nodes.clear() else: + pass mat = bpy.data.materials.new(name=material_name) mat.use_nodes = True mat.node_tree.nodes.clear() @@ -259,6 +331,7 @@ class TEXT_TEXTURE_OT_generate_shader(Operator): # Shader type based on settings if props.shader_type == 'PRINCIPLED': + pass shader_node = nodes.new(type='ShaderNodeBsdfPrincipled') shader_node.location = (200, 0) @@ -269,6 +342,7 @@ class TEXT_TEXTURE_OT_generate_shader(Operator): # Connect texture to shader if props.shader_connection == 'BASE_COLOR': + pass links.new(tex_node.outputs['Color'], shader_node.inputs['Base Color']) elif props.shader_connection == 'EMISSION': links.new(tex_node.outputs['Color'], shader_node.inputs['Emission Color']) @@ -281,11 +355,13 @@ class TEXT_TEXTURE_OT_generate_shader(Operator): # Connect alpha if enabled if props.use_alpha: + pass links.new(tex_node.outputs['Alpha'], shader_node.inputs['Alpha']) mat.blend_method = 'BLEND' # Add normal map nodes if normal map was generated if normal_map_blender_img: + pass # Create normal map texture node normal_tex_node = nodes.new(type='ShaderNodeTexImage') normal_tex_node.image = normal_map_blender_img @@ -302,7 +378,6 @@ class TEXT_TEXTURE_OT_generate_shader(Operator): # Connect normal map node to shader links.new(normal_map_node.outputs['Normal'], shader_node.inputs['Normal']) - print(f"[Normal Map] Added normal map nodes to Principled BSDF shader") # Handle shadow/reflection disabling current_shader = shader_node @@ -310,6 +385,7 @@ class TEXT_TEXTURE_OT_generate_shader(Operator): # Disable shadows if enabled if props.disable_shadows: + pass # Create light path node light_path_node = nodes.new(type='ShaderNodeLightPath') light_path_node.location = (50, 150) @@ -334,8 +410,10 @@ class TEXT_TEXTURE_OT_generate_shader(Operator): # Disable reflections if enabled if props.disable_reflections: + pass # Create or reuse light path node if not props.disable_shadows: + pass light_path_node = nodes.new(type='ShaderNodeLightPath') light_path_node.location = (50, 200) @@ -359,8 +437,10 @@ class TEXT_TEXTURE_OT_generate_shader(Operator): # Disable backfacing if enabled if props.disable_backfacing: + pass # Create or reuse light path node if not props.disable_shadows and not props.disable_reflections: + pass light_path_node = nodes.new(type='ShaderNodeLightPath') light_path_node.location = (50, 250) @@ -413,6 +493,7 @@ class TEXT_TEXTURE_OT_generate_shader(Operator): # Disable shadows if enabled if props.disable_shadows: + pass # Create light path node light_path_node = nodes.new(type='ShaderNodeLightPath') light_path_node.location = (50, 150) @@ -437,8 +518,10 @@ class TEXT_TEXTURE_OT_generate_shader(Operator): # Disable reflections if enabled if props.disable_reflections: + pass # Create or reuse light path node if not props.disable_shadows: + pass light_path_node = nodes.new(type='ShaderNodeLightPath') light_path_node.location = (50, 200) @@ -495,6 +578,7 @@ class TEXT_TEXTURE_OT_generate_shader(Operator): # Disable shadows if enabled if props.disable_shadows: + pass # Create light path node light_path_node = nodes.new(type='ShaderNodeLightPath') light_path_node.location = (50, 200) @@ -519,8 +603,10 @@ class TEXT_TEXTURE_OT_generate_shader(Operator): # Disable reflections if enabled if props.disable_reflections: + pass # Create or reuse light path node if not props.disable_shadows: + pass light_path_node = nodes.new(type='ShaderNodeLightPath') light_path_node.location = (50, 250) @@ -544,8 +630,10 @@ class TEXT_TEXTURE_OT_generate_shader(Operator): # Disable backfacing if enabled if props.disable_backfacing: + pass # Create or reuse light path node if not props.disable_shadows and not props.disable_reflections: + pass light_path_node = nodes.new(type='ShaderNodeLightPath') light_path_node.location = (50, 300) @@ -581,29 +669,38 @@ class TEXT_TEXTURE_OT_generate_shader(Operator): # Assign material to active object if enabled and possible if props.auto_assign_material and context.active_object and context.active_object.type in ['MESH', 'CURVE']: + pass obj = context.active_object # Check if material slot exists, reuse if found, otherwise create new one slot_found = False for i, slot in enumerate(obj.material_slots): + pass if slot.material and slot.material.name == material_name: + pass # Material already exists in a slot, update it obj.material_slots[i].material = mat slot_found = True break if not slot_found: + pass # Create new material slot if not found obj.data.materials.append(mat) self.report({'INFO'}, f"Generated shader '{material_name}' and assigned to {obj.name}") else: + pass self.report({'INFO'}, f"Generated shader '{material_name}' (assign manually to object)") # Switch to Shader Editor if available and set material for area in context.screen.areas: + pass if area.type == 'NODE_EDITOR': + pass for space in area.spaces: + pass if space.type == 'NODE_EDITOR': + pass space.shader_type = 'OBJECT' space.id = mat area.tag_redraw() @@ -612,6 +709,7 @@ class TEXT_TEXTURE_OT_generate_shader(Operator): return {'FINISHED'} except Exception as e: + pass self.report({'ERROR'}, f"Shader generation failed: {str(e)}") import traceback traceback.print_exc() @@ -620,5 +718,6 @@ class TEXT_TEXTURE_OT_generate_shader(Operator): # Export all operator classes for wildcard imports __all__ = [ 'TEXT_TEXTURE_OT_generate', + 'TEXT_TEXTURE_OT_preview', 'TEXT_TEXTURE_OT_generate_shader', ] \ No newline at end of file diff --git a/src/operators/preset_ops.py b/src/operators/preset_ops.py index fef9e55..0f9b77d 100644 --- a/src/operators/preset_ops.py +++ b/src/operators/preset_ops.py @@ -15,6 +15,7 @@ from ..presets.storage_system import ( from ..utils import bl_info class TEXT_TEXTURE_OT_save_preset(Operator): + pass """Save current settings as preset""" bl_idname = "text_texture.save_preset" bl_label = "Save Preset" @@ -34,16 +35,19 @@ class TEXT_TEXTURE_OT_save_preset(Operator): ) def execute(self, context): + pass props = context.scene.text_texture_props # Use provided preset name or fallback to input field preset_name = self.preset_name.strip() if self.preset_name else props.new_preset_name.strip() if not preset_name: + pass self.report({'ERROR'}, "Please enter a preset name") return {'CANCELLED'} # When overwriting from button, update the input field for consistency if self.preset_name and self.preset_name.strip(): + pass props.new_preset_name = preset_name # Validate preset name characters (avoid filesystem issues) @@ -51,23 +55,27 @@ class TEXT_TEXTURE_OT_save_preset(Operator): # Allow Unicode letters, numbers, spaces, hyphens, underscores, and common accented characters # This pattern supports international characters like ö, ü, ñ, etc. if not re.match(r'^[\w\s\-]+$', preset_name, re.UNICODE): + pass self.report({'ERROR'}, "Preset name contains invalid characters for filesystem compatibility") return {'CANCELLED'} # Additional check for filesystem-unsafe characters invalid_chars = ['<', '>', ':', '"', '|', '?', '*', '/', '\\'] if any(char in preset_name for char in invalid_chars): + pass self.report({'ERROR'}, f"Preset name cannot contain: {' '.join(invalid_chars)}") return {'CANCELLED'} # Check for duplicate names preset_file = os.path.join(get_preset_path(), f"{preset_name}.json") if os.path.exists(preset_file) and not self.overwrite: + pass # Suggest alternative names base_name = preset_name counter = 1 suggested_name = f"{base_name}_{counter}" while os.path.exists(os.path.join(get_preset_path(), f"{suggested_name}.json")): + pass counter += 1 suggested_name = f"{base_name}_{counter}" @@ -75,16 +83,6 @@ class TEXT_TEXTURE_OT_save_preset(Operator): return {'CANCELLED'} # DETAILED LOGGING: Current shader properties before saving - print(f"[PRESET SAVE DEBUG] ==================== SAVING PRESET: '{preset_name}' ====================") - print(f"[PRESET SAVE DEBUG] Current shader properties from props:") - print(f"[PRESET SAVE DEBUG] disable_shadows: {props.disable_shadows}") - print(f"[PRESET SAVE DEBUG] disable_reflections: {props.disable_reflections}") - print(f"[PRESET SAVE DEBUG] disable_backfacing: {props.disable_backfacing}") - print(f"[PRESET SAVE DEBUG] shader_type: {props.shader_type}") - print(f"[PRESET SAVE DEBUG] shader_connection: {props.shader_connection}") - print(f"[PRESET SAVE DEBUG] use_alpha: {props.use_alpha}") - print(f"[PRESET SAVE DEBUG] emission_strength: {props.emission_strength}") - print(f"[PRESET SAVE DEBUG] auto_assign_material: {props.auto_assign_material}") preset_data = { 'text': props.text, @@ -142,13 +140,10 @@ class TEXT_TEXTURE_OT_save_preset(Operator): } # DETAILED LOGGING: Verify shader properties in preset_data - print(f"[PRESET SAVE DEBUG] Shader properties in preset_data dictionary:") - print(f"[PRESET SAVE DEBUG] disable_shadows: {preset_data.get('disable_shadows', 'NOT FOUND')}") - print(f"[PRESET SAVE DEBUG] disable_reflections: {preset_data.get('disable_reflections', 'NOT FOUND')}") - print(f"[PRESET SAVE DEBUG] disable_backfacing: {preset_data.get('disable_backfacing', 'NOT FOUND')}") # Save overlay data for overlay in props.image_overlays: + pass overlay_data = { 'name': overlay.name, 'image_path': overlay.image_path, @@ -162,25 +157,31 @@ class TEXT_TEXTURE_OT_save_preset(Operator): preset_data['image_overlays'].append(overlay_data) try: + pass # Save based on checkbox setting - single destination only success = save_preset_unified(preset_data, preset_name, props.save_with_blend_file) if not success: + pass self.report({'ERROR'}, f"Failed to save preset '{preset_name}'") return {'CANCELLED'} # Check if preset already exists in the collection and update it existing_preset = None for preset in props.presets: + pass if preset.name == preset_name: + pass existing_preset = preset break if not existing_preset: + pass preset = props.presets.add() preset.name = preset_name preset.source = 'BLEND_FILE' if props.save_with_blend_file else 'PERSISTENT_FILE' else: + pass # Update source based on where it was saved existing_preset.source = 'BLEND_FILE' if props.save_with_blend_file else 'PERSISTENT_FILE' @@ -188,38 +189,47 @@ class TEXT_TEXTURE_OT_save_preset(Operator): location = "with .blend file" if props.save_with_blend_file else "to persistent storage" if self.overwrite: + pass self.report({'INFO'}, f"✅ Preset '{preset_name}' updated {location}") else: + pass self.report({'INFO'}, f"✅ Preset '{preset_name}' saved {location}") except (OSError, IOError, json.JSONEncodeError) as e: + pass self.report({'ERROR'}, f"Failed to save preset: {str(e)}") return {'CANCELLED'} except Exception as e: + pass self.report({'ERROR'}, f"Unexpected error saving preset: {str(e)}") return {'CANCELLED'} return {'FINISHED'} def invoke(self, context, event): + pass # Check if preset exists and prompt for overwrite props = context.scene.text_texture_props preset_name = props.new_preset_name.strip() if preset_name and preset_name != "New Preset": + pass preset_file = os.path.join(get_preset_path(), f"{preset_name}.json") if os.path.exists(preset_file): + pass return context.window_manager.invoke_confirm(self, event) return self.execute(context) def draw(self, context): + pass layout = self.layout props = context.scene.text_texture_props preset_name = props.new_preset_name.strip() layout.label(text=f"Overwrite existing preset '{preset_name}'?") class TEXT_TEXTURE_OT_save_preset_enter(Operator): + pass """Save preset when Enter key is pressed in name field""" bl_idname = "text_texture.save_preset_enter" bl_label = "Save Preset (Enter)" @@ -227,10 +237,12 @@ class TEXT_TEXTURE_OT_save_preset_enter(Operator): bl_options = {'REGISTER', 'UNDO'} def execute(self, context): + pass # Delegate to the main save preset operator return bpy.ops.text_texture.save_preset('INVOKE_DEFAULT') class TEXT_TEXTURE_OT_load_preset(Operator): + pass """Load preset""" bl_idname = "text_texture.load_preset" bl_label = "Load Preset" @@ -240,33 +252,29 @@ class TEXT_TEXTURE_OT_load_preset(Operator): preset_name: StringProperty() def execute(self, context): + pass props = context.scene.text_texture_props # DEFENSIVE PROGRAMMING: Ensure presets are available before loading if not ensure_presets_available(): + pass self.report({'ERROR'}, "Failed to ensure presets are loaded - cannot load preset") return {'CANCELLED'} # DETAILED LOGGING: Current shader properties before loading - print(f"[PRESET LOAD DEBUG] ==================== LOADING PRESET: '{self.preset_name}' ====================") - print(f"[PRESET LOAD DEBUG] Current shader properties BEFORE loading:") - print(f"[PRESET LOAD DEBUG] disable_shadows: {props.disable_shadows}") - print(f"[PRESET LOAD DEBUG] disable_reflections: {props.disable_reflections}") - print(f"[PRESET LOAD DEBUG] disable_backfacing: {props.disable_backfacing}") - print(f"[PRESET LOAD DEBUG] shader_type: {props.shader_type}") - print(f"[PRESET LOAD DEBUG] shader_connection: {props.shader_connection}") try: + pass # Get unified preset list and find the one to load all_presets = get_unified_preset_list() if self.preset_name not in all_presets: + pass self.report({'ERROR'}, f"Preset '{self.preset_name}' not found") return {'CANCELLED'} preset_data, source = all_presets[self.preset_name] - print(f"[PRESET LOAD DEBUG] Loading preset '{self.preset_name}' from {source}") props.is_updating = True @@ -290,56 +298,36 @@ class TEXT_TEXTURE_OT_load_preset(Operator): props.prepend_append_layout = preset_data.get('prepend_append_layout', props.prepend_append_layout) # DETAILED LOGGING: Shader property loading with before/after values - print(f"[PRESET LOAD DEBUG] Loading shader properties...") old_shader_type = props.shader_type props.shader_type = preset_data.get('shader_type', props.shader_type) - print(f"[PRESET LOAD DEBUG] shader_type: {old_shader_type} -> {props.shader_type}") old_shader_connection = props.shader_connection props.shader_connection = preset_data.get('shader_connection', props.shader_connection) - print(f"[PRESET LOAD DEBUG] shader_connection: {old_shader_connection} -> {props.shader_connection}") old_use_alpha = props.use_alpha props.use_alpha = preset_data.get('use_alpha', props.use_alpha) - print(f"[PRESET LOAD DEBUG] use_alpha: {old_use_alpha} -> {props.use_alpha}") old_emission_strength = props.emission_strength props.emission_strength = preset_data.get('emission_strength', props.emission_strength) - print(f"[PRESET LOAD DEBUG] emission_strength: {old_emission_strength} -> {props.emission_strength}") old_auto_assign = props.auto_assign_material props.auto_assign_material = preset_data.get('auto_assign_material', props.auto_assign_material) - print(f"[PRESET LOAD DEBUG] auto_assign_material: {old_auto_assign} -> {props.auto_assign_material}") # CRITICAL SHADER PROPERTIES - Most detailed logging - print(f"[PRESET LOAD DEBUG] ==================== CRITICAL SHADER PROPERTIES ====================") old_disable_shadows = props.disable_shadows new_disable_shadows = preset_data.get('disable_shadows', props.disable_shadows) props.disable_shadows = new_disable_shadows - print(f"[PRESET LOAD DEBUG] disable_shadows:") - print(f"[PRESET LOAD DEBUG] OLD VALUE: {old_disable_shadows} (type: {type(old_disable_shadows)})") - print(f"[PRESET LOAD DEBUG] PRESET DATA: {preset_data.get('disable_shadows', 'NOT FOUND')} (type: {type(preset_data.get('disable_shadows', 'N/A'))})") - print(f"[PRESET LOAD DEBUG] NEW VALUE: {props.disable_shadows} (type: {type(props.disable_shadows)})") old_disable_reflections = props.disable_reflections new_disable_reflections = preset_data.get('disable_reflections', props.disable_reflections) props.disable_reflections = new_disable_reflections - print(f"[PRESET LOAD DEBUG] disable_reflections:") - print(f"[PRESET LOAD DEBUG] OLD VALUE: {old_disable_reflections} (type: {type(old_disable_reflections)})") - print(f"[PRESET LOAD DEBUG] PRESET DATA: {preset_data.get('disable_reflections', 'NOT FOUND')} (type: {type(preset_data.get('disable_reflections', 'N/A'))})") - print(f"[PRESET LOAD DEBUG] NEW VALUE: {props.disable_reflections} (type: {type(props.disable_reflections)})") old_disable_backfacing = props.disable_backfacing new_disable_backfacing = preset_data.get('disable_backfacing', props.disable_backfacing) props.disable_backfacing = new_disable_backfacing - print(f"[PRESET LOAD DEBUG] disable_backfacing:") - print(f"[PRESET LOAD DEBUG] OLD VALUE: {old_disable_backfacing} (type: {type(old_disable_backfacing)})") - print(f"[PRESET LOAD DEBUG] PRESET DATA: {preset_data.get('disable_backfacing', 'NOT FOUND')} (type: {type(preset_data.get('disable_backfacing', 'N/A'))})") - print(f"[PRESET LOAD DEBUG] NEW VALUE: {props.disable_backfacing} (type: {type(props.disable_backfacing)})") - print(f"[PRESET LOAD DEBUG] ================================================================") props.generate_normal_map = preset_data.get('generate_normal_map', props.generate_normal_map) props.normal_map_strength = preset_data.get('normal_map_strength', props.normal_map_strength) @@ -369,6 +357,7 @@ class TEXT_TEXTURE_OT_load_preset(Operator): props.shadow_color = preset_data.get('shadow_color', props.shadow_color) # Handle new shadow_spread property with backward compatibility if hasattr(props, 'shadow_spread'): + pass props.shadow_spread = preset_data.get('shadow_spread', 1.0) props.enable_glow = preset_data.get('enable_glow', props.enable_glow) props.glow_blur = preset_data.get('glow_blur', props.glow_blur) @@ -389,6 +378,7 @@ class TEXT_TEXTURE_OT_load_preset(Operator): props.image_overlays.clear() overlay_data_list = preset_data.get('image_overlays', []) for overlay_data in overlay_data_list: + pass overlay = props.image_overlays.add() overlay.name = overlay_data.get('name', 'Overlay') overlay.image_path = overlay_data.get('image_path', '') @@ -402,12 +392,6 @@ class TEXT_TEXTURE_OT_load_preset(Operator): props.is_updating = False # FINAL VERIFICATION: Check what the properties actually are after loading - print(f"[PRESET LOAD DEBUG] ==================== FINAL VERIFICATION ====================") - print(f"[PRESET LOAD DEBUG] Properties AFTER loading preset '{self.preset_name}':") - print(f"[PRESET LOAD DEBUG] disable_shadows: {props.disable_shadows} (type: {type(props.disable_shadows)})") - print(f"[PRESET LOAD DEBUG] disable_reflections: {props.disable_reflections} (type: {type(props.disable_reflections)})") - print(f"[PRESET LOAD DEBUG] disable_backfacing: {props.disable_backfacing} (type: {type(props.disable_backfacing)})") - print(f"[PRESET LOAD DEBUG] ================================================================") # Always generate preview after loading preset # Import preview generation function @@ -422,14 +406,17 @@ class TEXT_TEXTURE_OT_load_preset(Operator): self.report({'INFO'}, f"✅ Loaded preset '{self.preset_name}' {source_info}") except (OSError, IOError) as e: + pass props.is_updating = False self.report({'ERROR'}, f"Failed to read preset file: {str(e)}") return {'CANCELLED'} except json.JSONDecodeError as e: + pass props.is_updating = False self.report({'ERROR'}, f"Invalid preset file format: {str(e)}") return {'CANCELLED'} except Exception as e: + pass props.is_updating = False self.report({'ERROR'}, f"Unexpected error loading preset: {str(e)}") return {'CANCELLED'} @@ -437,6 +424,7 @@ class TEXT_TEXTURE_OT_load_preset(Operator): return {'FINISHED'} class TEXT_TEXTURE_OT_delete_preset(Operator): + pass """Delete preset""" bl_idname = "text_texture.delete_preset" bl_label = "Delete Preset" @@ -446,55 +434,68 @@ class TEXT_TEXTURE_OT_delete_preset(Operator): preset_name: StringProperty() def execute(self, context): + pass props = context.scene.text_texture_props try: + pass # Delete from both blend file and local storage blend_deleted = delete_blend_file_preset(self.preset_name) preset_file = os.path.join(get_preset_path(), f"{self.preset_name}.json") local_deleted = False if os.path.exists(preset_file): + pass os.remove(preset_file) local_deleted = True if not blend_deleted and not local_deleted: + pass self.report({'WARNING'}, f"Preset '{self.preset_name}' not found in blend file or local storage") # Remove from UI list for i, preset in enumerate(props.presets): + pass if preset.name == self.preset_name: + pass props.presets.remove(i) break delete_info = [] if blend_deleted: + pass delete_info.append("blend file") if local_deleted: + pass delete_info.append("local storage") location_text = " and ".join(delete_info) if delete_info else "nowhere (not found)" self.report({'INFO'}, f"🗑️ Deleted preset '{self.preset_name}' from {location_text}") except (OSError, IOError) as e: + pass self.report({'ERROR'}, f"Failed to delete preset file: {str(e)}") return {'CANCELLED'} except Exception as e: + pass self.report({'ERROR'}, f"Unexpected error deleting preset: {str(e)}") return {'CANCELLED'} return {'FINISHED'} def invoke(self, context, event): + pass # Add confirmation dialog for better UX return context.window_manager.invoke_confirm(self, event) def draw(self, context): + pass layout = self.layout layout.label(text=f"Delete preset '{self.preset_name}'?") layout.label(text="This action cannot be undone.", icon='ERROR') class TEXT_TEXTURE_OT_refresh_presets(Operator): + pass """Refresh presets from blend file and local storage""" bl_idname = "text_texture.refresh_presets" bl_label = "Refresh Presets" @@ -502,11 +503,14 @@ class TEXT_TEXTURE_OT_refresh_presets(Operator): bl_options = {'REGISTER', 'UNDO'} def execute(self, context): + pass try: + pass # Use the reliable synchronous refresh system success = refresh_presets_sync() if success: + pass props = context.scene.text_texture_props total_presets = len(props.presets) @@ -517,21 +521,25 @@ class TEXT_TEXTURE_OT_refresh_presets(Operator): self.report({'INFO'}, f"✅ Refreshed {total_presets} presets ({blend_count} blend file, {persistent_count} persistent, {local_count} local)") else: + pass self.report({'WARNING'}, "Preset refresh completed with issues - check console for details") # Force UI redraw for area in context.screen.areas: + pass area.tag_redraw() return {'FINISHED'} except Exception as e: + pass self.report({'ERROR'}, f"Failed to refresh presets: {str(e)}") import traceback traceback.print_exc() return {'CANCELLED'} class TEXT_TEXTURE_OT_export_presets(Operator): + pass """Export all presets to a file for backup""" bl_idname = "text_texture.export_presets" bl_label = "Export Presets" @@ -546,7 +554,9 @@ class TEXT_TEXTURE_OT_export_presets(Operator): ) def execute(self, context): + pass try: + pass props = context.scene.text_texture_props # Collect all presets from all sources @@ -555,6 +565,7 @@ class TEXT_TEXTURE_OT_export_presets(Operator): # Get blend file presets blend_presets = get_blend_file_presets() for name, data in blend_presets.items(): + pass all_presets[name] = { "data": data, "source": "BLEND_FILE" @@ -563,11 +574,15 @@ class TEXT_TEXTURE_OT_export_presets(Operator): # Get persistent presets persistent_dir = get_persistent_preset_path() if os.path.exists(persistent_dir): + pass for filename in os.listdir(persistent_dir): + pass if filename.endswith('.json'): + pass preset_name = os.path.splitext(filename)[0] if preset_name not in all_presets: # Don't override blend file presets try: + pass with open(os.path.join(persistent_dir, filename), 'r') as f: preset_data = json.load(f) all_presets[preset_name] = { @@ -575,16 +590,20 @@ class TEXT_TEXTURE_OT_export_presets(Operator): "source": "PERSISTENT_FILE" } except (json.JSONDecodeError, OSError) as e: - print(f"[TTG] Error reading persistent preset {preset_name}: {e}") + pass # Get legacy presets legacy_dir = get_preset_path() if os.path.exists(legacy_dir) and legacy_dir != persistent_dir: + pass for filename in os.listdir(legacy_dir): + pass if filename.endswith('.json'): + pass preset_name = os.path.splitext(filename)[0] if preset_name not in all_presets: # Don't override higher priority presets try: + pass with open(os.path.join(legacy_dir, filename), 'r') as f: preset_data = json.load(f) all_presets[preset_name] = { @@ -592,9 +611,10 @@ class TEXT_TEXTURE_OT_export_presets(Operator): "source": "LOCAL_FILE" } except (json.JSONDecodeError, OSError) as e: - print(f"[TTG] Error reading legacy preset {preset_name}: {e}") + pass if not all_presets: + pass self.report({'WARNING'}, "No presets found to export") return {'CANCELLED'} @@ -614,10 +634,12 @@ class TEXT_TEXTURE_OT_export_presets(Operator): return {'FINISHED'} except Exception as e: + pass self.report({'ERROR'}, f"Failed to export presets: {str(e)}") return {'CANCELLED'} def invoke(self, context, event): + pass # Set default filename with timestamp import datetime timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") @@ -627,6 +649,7 @@ class TEXT_TEXTURE_OT_export_presets(Operator): return {'RUNNING_MODAL'} class TEXT_TEXTURE_OT_import_presets(Operator): + pass """Import presets from a backup file""" bl_idname = "text_texture.import_presets" bl_label = "Import Presets" @@ -657,8 +680,11 @@ class TEXT_TEXTURE_OT_import_presets(Operator): ) def execute(self, context): + pass try: + pass if not os.path.exists(self.filepath): + pass self.report({'ERROR'}, "Backup file not found") return {'CANCELLED'} @@ -670,11 +696,13 @@ class TEXT_TEXTURE_OT_import_presets(Operator): # Validate format if "presets" not in import_data: + pass self.report({'ERROR'}, "Invalid backup file format") return {'CANCELLED'} imported_presets = import_data["presets"] if not imported_presets: + pass self.report({'WARNING'}, "No presets found in backup file") return {'CANCELLED'} @@ -685,8 +713,11 @@ class TEXT_TEXTURE_OT_import_presets(Operator): # Also check persistent storage persistent_dir = get_persistent_preset_path() if os.path.exists(persistent_dir): + pass for filename in os.listdir(persistent_dir): + pass if filename.endswith('.json'): + pass existing_preset_names.add(os.path.splitext(filename)[0]) imported_count = 0 @@ -694,29 +725,37 @@ class TEXT_TEXTURE_OT_import_presets(Operator): errors = [] for preset_name, preset_info in imported_presets.items(): + pass preset_data = preset_info.get("data", {}) # Handle conflicts final_name = preset_name if preset_name in existing_preset_names: + pass if self.import_mode == 'SKIP': + pass skipped_count += 1 continue elif self.import_mode == 'RENAME': counter = 1 base_name = preset_name while final_name in existing_preset_names: + pass final_name = f"{base_name}_imported_{counter}" counter += 1 # OVERWRITE mode uses original name try: + pass # Import to blend file or persistent storage based on setting if self.import_to_blend: + pass success = save_blend_file_preset(final_name, preset_data) if not success: + pass raise Exception("Failed to save to blend file") else: + pass # Save to persistent storage persistent_file = os.path.join(get_persistent_preset_path(), f"{final_name}.json") with open(persistent_file, 'w') as f: @@ -727,50 +766,63 @@ class TEXT_TEXTURE_OT_import_presets(Operator): # Add to UI list if not already present existing_preset = None for preset in props.presets: + pass if preset.name == final_name: + pass existing_preset = preset break if not existing_preset: + pass new_preset = props.presets.add() new_preset.name = final_name new_preset.source = 'BLEND_FILE' if self.import_to_blend else 'PERSISTENT_FILE' else: + pass existing_preset.source = 'BLEND_FILE' if self.import_to_blend else 'PERSISTENT_FILE' except Exception as e: + pass errors.append(f"{preset_name}: {str(e)}") # Report results if imported_count > 0: + pass storage_location = "blend file" if self.import_to_blend else "persistent storage" self.report({'INFO'}, f"✅ Imported {imported_count} presets to {storage_location}") if skipped_count > 0: + pass self.report({'INFO'}, f"⏭️ Skipped {skipped_count} existing presets") else: + pass self.report({'WARNING'}, "No presets were imported") if errors: + pass self.report({'WARNING'}, f"Errors importing {len(errors)} presets - check console") for error in errors[:5]: # Show first 5 errors - print(f"[TTG] Import error: {error}") + print(f"Import error: {error}") return {'FINISHED'} except Exception as e: + pass self.report({'ERROR'}, f"Failed to import presets: {str(e)}") return {'CANCELLED'} def invoke(self, context, event): + pass context.window_manager.fileselect_add(self) return {'RUNNING_MODAL'} def draw(self, context): + pass layout = self.layout layout.prop(self, "import_mode") layout.prop(self, "import_to_blend") class TEXT_TEXTURE_OT_show_migration_report(Operator): + pass """Show migration report with backup and upgrade information""" bl_idname = "text_texture.show_migration_report" bl_label = "Show Migration Report" @@ -778,12 +830,15 @@ class TEXT_TEXTURE_OT_show_migration_report(Operator): bl_options = {'REGISTER', 'UNDO'} def execute(self, context): + pass return {'FINISHED'} def invoke(self, context, event): + pass return context.window_manager.invoke_props_dialog(self, width=400) def draw(self, context): + pass layout = self.layout layout.label(text="Migration Report", icon='INFO') layout.separator() diff --git a/src/operators/utility_ops.py b/src/operators/utility_ops.py index 47c2acb..4a8270e 100644 --- a/src/operators/utility_ops.py +++ b/src/operators/utility_ops.py @@ -10,53 +10,52 @@ import os # Import functions from parent module try: - print("[TTG DEBUG] IMPORT TRACE: Attempting to import from ui.preview...") - print("[TTG DEBUG] IMPORT TRACE: Looking for generate_preview in ui.preview module") + pass # First, let's see what's actually available in ui.preview from .. import ui - print(f"[TTG DEBUG] IMPORT TRACE: ui.preview module contents: {dir(ui.preview)}") # Now try the actual import that's failing from ..core.generation_engine import generate_preview - print("[TTG DEBUG] IMPORT TRACE: SUCCESS - generate_preview imported from ui.preview") from ..utils.system import get_system_fonts, get_font_enum_items from ..presets.storage_system import refresh_presets_sync, ensure_presets_available from ..utils.constants import sync_margin_values except ImportError as e: - print(f"[TTG DEBUG] IMPORT ERROR: Failed to import from ui.preview: {e}") - print("[TTG DEBUG] IMPORT TRACE: This confirms generate_preview is NOT in ui.preview") + pass # Let's check if it's available in core.generation_engine try: - print("[TTG DEBUG] IMPORT TRACE: Attempting fallback import from core.generation_engine...") + pass from ..core.generation_engine import generate_preview - print("[TTG DEBUG] IMPORT TRACE: SUCCESS - generate_preview found in core.generation_engine!") from ..utils.system import get_system_fonts, get_font_enum_items from ..presets.storage_system import refresh_presets_sync, ensure_presets_available from ..utils.constants import sync_margin_values - print("[TTG DEBUG] IMPORT TRACE: All imports successful with corrected path") except ImportError as fallback_error: - print(f"[TTG DEBUG] IMPORT ERROR: Fallback import also failed: {fallback_error}") - print(f"[TTG DEBUG] IMPORT ERROR: Original error was: {e}") + pass # Fallback imports for development/testing - print("[TTG DEBUG] IMPORT TRACE: Using fallback placeholder functions") def generate_preview(context): + pass print("Fallback generate_preview called") return None def get_system_fonts(): + pass return {} def get_font_enum_items(): + pass return [("default", "Default Font", "Use system default font", 0)] def refresh_presets_sync(): + pass return True def ensure_presets_available(): + pass return True def sync_margin_values(props, changed_margin): pass + pass class TEXT_TEXTURE_OT_refresh_preview(Operator): + pass """Force refresh the preview""" bl_idname = "text_texture.refresh_preview" bl_label = "Refresh Preview" @@ -64,6 +63,7 @@ class TEXT_TEXTURE_OT_refresh_preview(Operator): bl_options = {'REGISTER', 'UNDO'} def execute(self, context): + pass props = context.scene.text_texture_props # Clear existing preview first @@ -73,18 +73,22 @@ class TEXT_TEXTURE_OT_refresh_preview(Operator): result = generate_preview(context) if result: + pass self.report({'INFO'}, f"Preview refreshed at {props.preview_size}px") else: + pass self.report({'ERROR'}, "Failed to generate preview - check console for details") # Force UI update for area in context.screen.areas: + pass area.tag_redraw() return {'FINISHED'} class TEXT_TEXTURE_OT_view_preview_zoom(Operator): + pass """Open preview in new Image Editor window with zoom support""" bl_idname = "text_texture.view_preview_zoom" bl_label = "Enable Zoom" @@ -92,55 +96,66 @@ class TEXT_TEXTURE_OT_view_preview_zoom(Operator): bl_options = {'REGISTER', 'UNDO'} def execute(self, context): + pass props = context.scene.text_texture_props # Generate preview image when button is clicked - print("[TTG] Generating preview on button click...") result = generate_preview(context) if not result: + pass self.report({'ERROR'}, "Failed to generate preview") return {'CANCELLED'} if not props.preview_image: + pass self.report({'ERROR'}, "No preview image to display") return {'CANCELLED'} # Open a new window with Image Editor - print(f"[ZOOM] Opening new window for preview image: {props.preview_image.name}") try: + pass # Create a new window bpy.ops.screen.userpref_show('INVOKE_DEFAULT') # Wait a moment and then change the newly opened window to Image Editor def setup_image_window(): + pass # Find the newest window (should be the one we just opened) newest_window = None for window in bpy.context.window_manager.windows: + pass if window.screen.name.startswith("temp"): + pass newest_window = window break if not newest_window: + pass # Fallback: use any non-main window for window in bpy.context.window_manager.windows: + pass if window != bpy.context.window: + pass newest_window = window break if newest_window: + pass # Set the area type to Image Editor for area in newest_window.screen.areas: + pass if area.type == 'PREFERENCES': + pass area.type = 'IMAGE_EDITOR' - print("[ZOOM] Changed area to Image Editor") # Set the preview image for space in area.spaces: + pass if space.type == 'IMAGE_EDITOR': + pass space.image = props.preview_image - print(f"[ZOOM] Set preview image: {props.preview_image.name}") # Set up proper viewing override = { @@ -152,18 +167,17 @@ class TEXT_TEXTURE_OT_view_preview_zoom(Operator): with bpy.context.temp_override(**override): try: + pass bpy.ops.image.view_all(fit_view=True) - print("[ZOOM] Applied fit to view") except Exception as e: - print(f"[ZOOM] Error applying fit to view: {e}") + pass break break self.report({'INFO'}, "✅ NEW WINDOW OPENED! Use mouse wheel to zoom, middle-mouse to pan") - print("[ZOOM] New window zoom feature activated successfully") else: - print("[ZOOM] Could not find new window") + pass self.report({'ERROR'}, "Failed to setup new window") return None # Stop timer @@ -172,14 +186,15 @@ class TEXT_TEXTURE_OT_view_preview_zoom(Operator): bpy.app.timers.register(setup_image_window, first_interval=0.1) except Exception as e: - print(f"[ZOOM] Error creating new window: {e}") + pass # Fallback to old behavior if new window creation fails self.report({'WARNING'}, "New window failed, trying area split instead") # Find any area to split for area in context.screen.areas: + pass if area.type in ['NODE_EDITOR', 'VIEW_3D', 'PROPERTIES']: - print(f"[ZOOM] Fallback: Splitting {area.type} to create Image Editor") + pass # Split horizontally to create a new area with context.temp_override(area=area): @@ -187,14 +202,17 @@ class TEXT_TEXTURE_OT_view_preview_zoom(Operator): # Find the newly created area for new_area in context.screen.areas: + pass if new_area != area and new_area.y != area.y: + pass new_area.type = 'IMAGE_EDITOR' # Set the image in the Image Editor for space in new_area.spaces: + pass if space.type == 'IMAGE_EDITOR': + pass space.image = props.preview_image - print("[ZOOM] Fallback: Image set in split Image Editor") break # Tag for redraw and fit to view @@ -205,10 +223,10 @@ class TEXT_TEXTURE_OT_view_preview_zoom(Operator): with context.temp_override(**override): try: + pass bpy.ops.image.view_all(fit_view=True) - print("[ZOOM] Fallback: Fit to view applied") except Exception as e: - print(f"[ZOOM] Fallback: Error applying fit to view: {e}") + pass self.report({'INFO'}, "✅ ZOOM ENABLED! Use mouse wheel to zoom, middle-mouse to pan") return {'FINISHED'} @@ -221,6 +239,7 @@ class TEXT_TEXTURE_OT_view_preview_zoom(Operator): class TEXT_TEXTURE_OT_open_panel(Operator): + pass """Open Text Texture Generator panel""" bl_idname = "text_texture.open_panel" bl_label = "Open Text Texture Panel" @@ -228,11 +247,16 @@ class TEXT_TEXTURE_OT_open_panel(Operator): bl_options = {'REGISTER', 'UNDO'} def execute(self, context): + pass # Ensure the sidebar is visible for area in context.screen.areas: + pass if area.type == 'VIEW_3D': + pass for space in area.spaces: + pass if space.type == 'VIEW_3D': + pass space.show_region_ui = True # Switch to Tool tab area.tag_redraw() @@ -240,7 +264,9 @@ class TEXT_TEXTURE_OT_open_panel(Operator): return {'FINISHED'} elif area.type == 'NODE_EDITOR': for space in area.spaces: + pass if space.type == 'NODE_EDITOR': + pass space.show_region_ui = True area.tag_redraw() self.report({'INFO'}, "Text Texture panel opened in Shader Editor sidebar") @@ -251,6 +277,7 @@ class TEXT_TEXTURE_OT_open_panel(Operator): class TEXT_TEXTURE_OT_refresh_fonts(Operator): + pass """Refresh fonts""" bl_idname = "text_texture.refresh_fonts" bl_label = "Refresh Fonts" @@ -258,7 +285,9 @@ class TEXT_TEXTURE_OT_refresh_fonts(Operator): bl_options = {'REGISTER', 'UNDO'} def execute(self, context): + pass if hasattr(get_font_enum_items, 'cached_fonts'): + pass del get_font_enum_items.cached_fonts get_font_enum_items.cached_fonts = get_system_fonts() self.report({'INFO'}, f"Found {len(get_font_enum_items.cached_fonts)} fonts") @@ -266,6 +295,7 @@ class TEXT_TEXTURE_OT_refresh_fonts(Operator): class TEXT_TEXTURE_OT_refresh_presets(Operator): + pass """Refresh presets from blend file and local storage""" bl_idname = "text_texture.refresh_presets" bl_label = "Refresh Presets" @@ -273,11 +303,14 @@ class TEXT_TEXTURE_OT_refresh_presets(Operator): bl_options = {'REGISTER', 'UNDO'} def execute(self, context): + pass try: + pass # Use the reliable synchronous refresh system success = refresh_presets_sync() if success: + pass props = context.scene.text_texture_props total_presets = len(props.presets) @@ -288,15 +321,18 @@ class TEXT_TEXTURE_OT_refresh_presets(Operator): self.report({'INFO'}, f"✅ Refreshed {total_presets} presets ({blend_count} blend file, {persistent_count} persistent, {local_count} local)") else: + pass self.report({'WARNING'}, "Preset refresh completed with issues - check console for details") # Force UI redraw for area in context.screen.areas: + pass area.tag_redraw() return {'FINISHED'} except Exception as e: + pass self.report({'ERROR'}, f"Failed to refresh presets: {str(e)}") import traceback traceback.print_exc() @@ -304,6 +340,7 @@ class TEXT_TEXTURE_OT_refresh_presets(Operator): class TEXT_TEXTURE_OT_add_overlay(Operator): + pass """Add new image overlay""" bl_idname = "text_texture.add_overlay" bl_label = "Add Overlay" @@ -311,6 +348,7 @@ class TEXT_TEXTURE_OT_add_overlay(Operator): bl_options = {'REGISTER', 'UNDO'} def execute(self, context): + pass props = context.scene.text_texture_props overlay = props.image_overlays.add() @@ -319,6 +357,7 @@ class TEXT_TEXTURE_OT_add_overlay(Operator): # Force UI redraw for area in context.screen.areas: + pass area.tag_redraw() self.report({'INFO'}, f"Added overlay: {overlay.name}") @@ -326,6 +365,7 @@ class TEXT_TEXTURE_OT_add_overlay(Operator): class TEXT_TEXTURE_OT_remove_overlay(Operator): + pass """Remove active image overlay""" bl_idname = "text_texture.remove_overlay" bl_label = "Remove Overlay" @@ -333,28 +373,34 @@ class TEXT_TEXTURE_OT_remove_overlay(Operator): bl_options = {'REGISTER', 'UNDO'} def execute(self, context): + pass props = context.scene.text_texture_props if props.image_overlays and props.active_overlay_index < len(props.image_overlays): + pass overlay_name = props.image_overlays[props.active_overlay_index].name props.image_overlays.remove(props.active_overlay_index) # Update active index if props.active_overlay_index >= len(props.image_overlays) and props.image_overlays: + pass props.active_overlay_index = len(props.image_overlays) - 1 # Force UI redraw for area in context.screen.areas: + pass area.tag_redraw() self.report({'INFO'}, f"Removed overlay: {overlay_name}") else: + pass self.report({'WARNING'}, "No overlay to remove") return {'FINISHED'} class TEXT_TEXTURE_OT_set_anchor_point(Operator): + pass """Set anchor point for text positioning""" bl_idname = "text_texture.set_anchor_point" bl_label = "Set Anchor Point" @@ -364,12 +410,14 @@ class TEXT_TEXTURE_OT_set_anchor_point(Operator): anchor_point: StringProperty() def execute(self, context): + pass props = context.scene.text_texture_props props.anchor_point = self.anchor_point return {'FINISHED'} class TEXT_TEXTURE_OT_sync_margins(Operator): + pass """Sync margin values when linked""" bl_idname = "text_texture.sync_margins" bl_label = "Sync Margins" @@ -379,12 +427,14 @@ class TEXT_TEXTURE_OT_sync_margins(Operator): margin_type: StringProperty() def execute(self, context): + pass props = context.scene.text_texture_props sync_margin_values(props, self.margin_type) return {'FINISHED'} class TEXT_TEXTURE_OT_duplicate_overlay(Operator): + pass """Duplicate image overlay""" bl_idname = "text_texture.duplicate_overlay" bl_label = "Duplicate Overlay" @@ -394,19 +444,14 @@ class TEXT_TEXTURE_OT_duplicate_overlay(Operator): overlay_index: IntProperty() def execute(self, context): + pass props = context.scene.text_texture_props if 0 <= self.overlay_index < len(props.image_overlays): + pass source_overlay = props.image_overlays[self.overlay_index] # Debug logging - print(f"[DUPLICATE DEBUG] Duplicating overlay '{source_overlay.name}'") - print(f"[DUPLICATE DEBUG] Source properties:") - print(f"[DUPLICATE DEBUG] image_path: '{source_overlay.image_path}'") - print(f"[DUPLICATE DEBUG] enabled: {source_overlay.enabled}") - print(f"[DUPLICATE DEBUG] positioning_mode: '{source_overlay.positioning_mode}'") - print(f"[DUPLICATE DEBUG] scale: {source_overlay.scale}") - print(f"[DUPLICATE DEBUG] z_index: {source_overlay.z_index}") new_overlay = props.image_overlays.add() @@ -421,10 +466,11 @@ class TEXT_TEXTURE_OT_duplicate_overlay(Operator): # For PREPEND/APPEND overlays, increment z_index to ensure duplicate appears above original # This prevents the duplicate from being rendered behind the original overlay if source_overlay.positioning_mode in ['PREPEND', 'APPEND']: + pass # Increment z_index to ensure proper layering order for duplicated overlay new_overlay.z_index = min(source_overlay.z_index + 1, 10) # Cap at max z_index value - print(f"[DUPLICATE DEBUG] Fixed z_index for {source_overlay.positioning_mode} overlay: {source_overlay.z_index} -> {new_overlay.z_index}") else: + pass # For ABSOLUTE overlays, keep same z_index (different positions so no overlap issue) new_overlay.z_index = source_overlay.z_index new_overlay.positioning_mode = source_overlay.positioning_mode @@ -433,55 +479,47 @@ class TEXT_TEXTURE_OT_duplicate_overlay(Operator): new_overlay.enabled = source_overlay.enabled # Verify the copy was successful - print(f"[DUPLICATE DEBUG] New overlay properties:") - print(f"[DUPLICATE DEBUG] name: '{new_overlay.name}'") - print(f"[DUPLICATE DEBUG] image_path: '{new_overlay.image_path}'") - print(f"[DUPLICATE DEBUG] enabled: {new_overlay.enabled}") - print(f"[DUPLICATE DEBUG] positioning_mode: '{new_overlay.positioning_mode}'") - print(f"[DUPLICATE DEBUG] scale: {new_overlay.scale}") - print(f"[DUPLICATE DEBUG] z_index: {new_overlay.z_index}") # Move to position after source new_index = len(props.image_overlays) - 1 target_index = min(self.overlay_index + 1, new_index) if new_index != target_index: + pass props.image_overlays.move(new_index, target_index) - print(f"[DUPLICATE DEBUG] Moved overlay from index {new_index} to {target_index}") props.active_overlay_index = target_index # Clear any cached images to force reload - print(f"[DUPLICATE DEBUG] Clearing image cache and forcing preview update...") # Set the updating flag to prevent recursive updates props.is_updating = True try: + pass # Force immediate preview regeneration - print(f"[DUPLICATE DEBUG] Generating new preview...") preview_result = generate_preview(context) if preview_result: - print(f"[DUPLICATE DEBUG] Preview generated successfully") + pass else: - print(f"[DUPLICATE DEBUG] Preview generation failed") + pass # Force UI redraw for area in context.screen.areas: + pass area.tag_redraw() except Exception as e: - print(f"[DUPLICATE DEBUG] Error updating preview: {e}") + pass import traceback traceback.print_exc() finally: props.is_updating = False - print(f"[DUPLICATE DEBUG] Total overlays now: {len(props.image_overlays)}") - print(f"[DUPLICATE DEBUG] Duplication operation completed") self.report({'INFO'}, f"✅ Duplicated overlay: {source_overlay.name}") else: + pass self.report({'ERROR'}, "Invalid overlay index") return {'CANCELLED'} @@ -489,6 +527,7 @@ class TEXT_TEXTURE_OT_duplicate_overlay(Operator): class TEXT_TEXTURE_OT_delete_overlay(Operator): + pass """Delete image overlay""" bl_idname = "text_texture.delete_overlay" bl_label = "Delete Overlay" @@ -498,38 +537,39 @@ class TEXT_TEXTURE_OT_delete_overlay(Operator): overlay_index: IntProperty() def execute(self, context): + pass props = context.scene.text_texture_props - print(f"[DELETE DEBUG] Deleting overlay at index {self.overlay_index}") if 0 <= self.overlay_index < len(props.image_overlays): + pass overlay_name = props.image_overlays[self.overlay_index].name - print(f"[DELETE DEBUG] Deleting overlay: '{overlay_name}'") props.image_overlays.remove(self.overlay_index) # Update active index if props.active_overlay_index >= len(props.image_overlays) and props.image_overlays: + pass props.active_overlay_index = len(props.image_overlays) - 1 elif not props.image_overlays: props.active_overlay_index = 0 - print(f"[DELETE DEBUG] Overlay deleted successfully. Total overlays now: {len(props.image_overlays)}") # Force preview update after deletion try: + pass generate_preview(context) - print(f"[DELETE DEBUG] Preview updated after deletion") except Exception as e: - print(f"[DELETE DEBUG] Error updating preview: {e}") + pass # Force UI redraw for area in context.screen.areas: + pass area.tag_redraw() self.report({'INFO'}, f"Deleted overlay: {overlay_name}") else: - print(f"[DELETE DEBUG] Invalid overlay index: {self.overlay_index}") + pass self.report({'ERROR'}, "Invalid overlay index") return {'CANCELLED'} @@ -537,6 +577,7 @@ class TEXT_TEXTURE_OT_delete_overlay(Operator): class TEXT_TEXTURE_OT_move_overlay(Operator): + pass """Move image overlay up or down in the list""" bl_idname = "text_texture.move_overlay" bl_label = "Move Overlay" @@ -552,39 +593,41 @@ class TEXT_TEXTURE_OT_move_overlay(Operator): ) def execute(self, context): + pass props = context.scene.text_texture_props - print(f"[MOVE DEBUG] Moving overlay at index {self.overlay_index} direction: {self.direction}") if 0 <= self.overlay_index < len(props.image_overlays): + pass current_index = self.overlay_index new_index = current_index if self.direction == 'UP' and current_index > 0: + pass new_index = current_index - 1 elif self.direction == 'DOWN' and current_index < len(props.image_overlays) - 1: new_index = current_index + 1 if new_index != current_index: + pass overlay_name = props.image_overlays[current_index].name props.image_overlays.move(current_index, new_index) props.active_overlay_index = new_index - print(f"[MOVE DEBUG] Successfully moved overlay '{overlay_name}' from {current_index} to {new_index}") # Force preview update after moving (order affects rendering) try: + pass generate_preview(context) - print(f"[MOVE DEBUG] Preview updated after move operation") except Exception as e: - print(f"[MOVE DEBUG] Error updating preview: {e}") + pass self.report({'INFO'}, f"Moved overlay {self.direction.lower()}") else: - print(f"[MOVE DEBUG] No movement needed - overlay is already at edge") + pass self.report({'INFO'}, f"Overlay is already at {self.direction.lower()} edge") else: - print(f"[MOVE DEBUG] Invalid overlay index: {self.overlay_index}") + pass self.report({'ERROR'}, "Invalid overlay index") return {'CANCELLED'} diff --git a/src/presets/__pycache__/__init__.cpython-311.pyc b/src/presets/__pycache__/__init__.cpython-311.pyc index 0276c6fcde8178aea19a852d66e852dff8aac6d9..e9006adab2bb56f36e1b849b352649a8dba5e998 100644 GIT binary patch delta 20 acmbQlGKqzIIWI340}#YtIJ%LWoe2Oi=LEa} delta 20 acmbQlGKqzIIWI340}vSV9ofju&IAA}A_L6; diff --git a/src/presets/__pycache__/migration.cpython-311.pyc b/src/presets/__pycache__/migration.cpython-311.pyc index 4bfb94cc2797d8fb822779e49f489f04b2989f8e..b965d0a0a3d8e58d2115ba43296734bfa1a39de9 100644 GIT binary patch delta 1287 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 literal 32024 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 diff --git a/src/presets/migration.py b/src/presets/migration.py index dd08a70..74e13fd 100644 --- a/src/presets/migration.py +++ b/src/presets/migration.py @@ -6,26 +6,28 @@ import shutil from datetime import datetime def migrate_presets_if_needed(): + pass """ Migrate presets from older versions if needed. This function checks if migration is required and performs it automatically. Returns: dict with 'migrated_count' key indicating number of presets migrated. """ - print("[TTG MIGRATION DEBUG] Starting migrate_presets_if_needed()") preferences = bpy.context.preferences.addons.get("Text_Texture_Generator_Pro") if not preferences: - print("[TTG MIGRATION DEBUG] No preferences found - returning proper dict format") + pass return {'migrated_count': 0} addon_version = preferences.preferences.get("addon_version", "0.0.0") current_version = "1.0.0" # This should match the addon's current version if addon_version != current_version: + pass print(f"Text Texture Generator: Migrating presets from version {addon_version} to {current_version}") # Backup existing presets before migration backup_success = backup_presets() if not backup_success: + pass print("Text Texture Generator: Warning - Failed to backup presets before migration") # Perform migration steps based on version differences @@ -33,6 +35,7 @@ def migrate_presets_if_needed(): migrated_count = 0 try: + pass # Version-specific migration logic would go here # For now, we'll just update the version number preferences.preferences["addon_version"] = current_version @@ -42,27 +45,30 @@ def migrate_presets_if_needed(): migrated_count = 1 if migration_success: + pass print(f"Text Texture Generator: Successfully migrated presets to version {current_version}") else: + pass print("Text Texture Generator: Migration completed with warnings") except Exception as e: + pass print(f"Text Texture Generator: Error during preset migration: {str(e)}") migration_success = False migrated_count = 0 - print(f"[TTG MIGRATION DEBUG] Migration completed, returning proper dict format: migrated_count={migrated_count}") return {'migrated_count': migrated_count} - print("[TTG MIGRATION DEBUG] No migration needed, returning proper dict format: migrated_count=0") return {'migrated_count': 0} # No migration needed def backup_presets(): + pass """ Create a backup of current presets before migration. Returns True if backup was successful, False otherwise. """ try: + pass # Get the addon directory addon_dir = os.path.dirname(__file__) parent_dir = os.path.dirname(addon_dir) @@ -76,28 +82,35 @@ def backup_presets(): # Check for blend file presets if bpy.data.filepath: + pass blend_file_dir = os.path.dirname(bpy.data.filepath) preset_file = os.path.join(blend_file_dir, ".text_texture_presets.json") if os.path.exists(preset_file): + pass preset_files.append(preset_file) # Check for persistent presets preferences = bpy.context.preferences.addons.get("Text_Texture_Generator_Pro") if preferences and hasattr(preferences.preferences, 'persistent_presets_path'): + pass persistent_path = preferences.preferences.persistent_presets_path if persistent_path and os.path.exists(persistent_path): + pass preset_files.append(persistent_path) # Check for legacy presets in addon directory legacy_preset_file = os.path.join(parent_dir, "text_texture_presets.json") if os.path.exists(legacy_preset_file): + pass preset_files.append(legacy_preset_file) # Create backup if there are files to backup if preset_files: + pass os.makedirs(backup_dir, exist_ok=True) for preset_file in preset_files: + pass filename = os.path.basename(preset_file) backup_path = os.path.join(backup_dir, filename) shutil.copy2(preset_file, backup_path) @@ -117,9 +130,11 @@ def backup_presets(): print(f"Text Texture Generator: Created preset backup in {backup_dir}") return True else: + pass print("Text Texture Generator: No preset files found to backup") return True # Not an error if there are no presets to backup except Exception as e: + pass print(f"Text Texture Generator: Error creating preset backup: {str(e)}") return False \ No newline at end of file diff --git a/src/presets/storage_system.py b/src/presets/storage_system.py index a705733..42a1bf4 100644 --- a/src/presets/storage_system.py +++ b/src/presets/storage_system.py @@ -34,6 +34,7 @@ import platform # ============================================================================ def get_preset_path(): + pass """Get path for storing presets (legacy local storage) LEGACY SYSTEM: This storage location is vulnerable to addon updates. @@ -46,36 +47,43 @@ def get_preset_path(): addon_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) presets_dir = os.path.join(addon_dir, "presets") if not os.path.exists(presets_dir): + pass try: + pass os.makedirs(presets_dir) except OSError as e: + pass print(f"Error creating presets directory: {e}") return presets_dir def get_persistent_preset_path(): + pass """Get persistent path for storing presets (survives addon updates)""" import bpy # Get Blender's user config directory config_dir = bpy.utils.user_resource('CONFIG') if not config_dir: + pass # Fallback to addon directory if config path unavailable - print("[TTG] Warning: Could not get user config directory, falling back to addon directory") return get_preset_path() # Create addon-specific subdirectory persistent_dir = os.path.join(config_dir, "text_texture_generator", "presets") try: + pass if not os.path.exists(persistent_dir): + pass os.makedirs(persistent_dir, exist_ok=True) return persistent_dir except OSError as e: - print(f"[TTG] Error creating persistent presets directory: {e}") + pass # Fallback to addon directory return get_preset_path() def get_addon_version(): + pass """Get current addon version for migration tracking""" # Import bl_info from the main addon file import sys @@ -86,192 +94,179 @@ def get_addon_version(): init_file = os.path.join(main_module_path, "__init__.py") try: + pass spec = importlib.util.spec_from_file_location("main_addon", init_file) main_addon = importlib.util.module_from_spec(spec) spec.loader.exec_module(main_addon) return main_addon.bl_info["version"] except Exception as e: - print(f"[TTG] Error getting addon version: {e}") + pass return (2, 3, 2) # Fallback version def get_version_file_path(): + pass """Get path to version tracking file""" persistent_dir = os.path.dirname(get_persistent_preset_path()) return os.path.join(persistent_dir, "addon_version.json") def get_stored_version(): + pass """Get previously stored addon version""" version_file = get_version_file_path() try: + pass if os.path.exists(version_file): + pass with open(version_file, 'r') as f: data = json.load(f) return tuple(data.get('version', (0, 0, 0))) except (json.JSONDecodeError, OSError) as e: - print(f"[TTG] Error reading version file: {e}") + pass + print(f"Error reading version file: {e}") return (0, 0, 0) def save_current_version(): + pass """Save current addon version to persistent storage""" version_file = get_version_file_path() try: + pass version_data = { 'version': get_addon_version(), 'timestamp': time.time() } with open(version_file, 'w') as f: json.dump(version_data, f, indent=2) - print(f"[TTG] Saved addon version {get_addon_version()} to persistent storage") except (OSError, json.JSONEncodeError) as e: - print(f"[TTG] Error saving version file: {e}") + pass + print(f"Error saving version file: {e}") def get_blend_file_presets(): + pass """Get presets stored in the current blend file""" try: + pass # Get custom properties from the scene import bpy scene = bpy.context.scene # DETAILED LOGGING: Function entry - print(f"[GET_BLEND_FILE_PRESETS DEBUG] ==================== LOADING FROM BLEND FILE ==================") - print(f"[GET_BLEND_FILE_PRESETS DEBUG] Function called to get presets from blend file") # Store presets in scene custom properties if "text_texture_presets" not in scene: - print(f"[GET_BLEND_FILE_PRESETS DEBUG] No 'text_texture_presets' found in scene, creating empty") + pass scene["text_texture_presets"] = "{}" else: - print(f"[GET_BLEND_FILE_PRESETS DEBUG] Found 'text_texture_presets' in scene") + pass + print("Found 'text_texture_presets' in scene") preset_data = scene.get("text_texture_presets", "{}") - print(f"[GET_BLEND_FILE_PRESETS DEBUG] Raw preset data type: {type(preset_data)}") - print(f"[GET_BLEND_FILE_PRESETS DEBUG] Raw preset data length: {len(str(preset_data))} characters") if isinstance(preset_data, str): - print(f"[GET_BLEND_FILE_PRESETS DEBUG] Preset data is string, attempting JSON parse...") + pass try: + pass import json parsed_presets = json.loads(preset_data) - print(f"[GET_BLEND_FILE_PRESETS DEBUG] Successfully parsed JSON") - print(f"[GET_BLEND_FILE_PRESETS DEBUG] Parsed presets type: {type(parsed_presets)}") - print(f"[GET_BLEND_FILE_PRESETS DEBUG] Parsed presets count: {len(parsed_presets) if isinstance(parsed_presets, dict) else 'N/A'}") if isinstance(parsed_presets, dict): - print(f"[GET_BLEND_FILE_PRESETS DEBUG] Preset names: {list(parsed_presets.keys())}") + pass + print(f"Parsed presets count: {len(parsed_presets)}") # DETAILED VERIFICATION: Check shader properties in each preset for preset_name, preset_content in parsed_presets.items(): - print(f"[GET_BLEND_FILE_PRESETS DEBUG] Preset '{preset_name}' analysis:") + pass if isinstance(preset_content, dict): - print(f"[GET_BLEND_FILE_PRESETS DEBUG] Keys: {list(preset_content.keys())}") - print(f"[GET_BLEND_FILE_PRESETS DEBUG] disable_shadows: {preset_content.get('disable_shadows', 'NOT FOUND')}") - print(f"[GET_BLEND_FILE_PRESETS DEBUG] disable_reflections: {preset_content.get('disable_reflections', 'NOT FOUND')}") - print(f"[GET_BLEND_FILE_PRESETS DEBUG] disable_backfacing: {preset_content.get('disable_backfacing', 'NOT FOUND')}") + pass + print(f"Preset '{preset_name}' is valid") else: - print(f"[GET_BLEND_FILE_PRESETS DEBUG] ERROR: Preset content is not a dict: {type(preset_content)}") + pass + print(f"Preset '{preset_name}' is not a dict") - print(f"[GET_BLEND_FILE_PRESETS DEBUG] Returning parsed presets") - print(f"[GET_BLEND_FILE_PRESETS DEBUG] ================================================================") return parsed_presets except json.JSONDecodeError as json_err: - print(f"[GET_BLEND_FILE_PRESETS DEBUG] JSON decode error: {json_err}") - print(f"[GET_BLEND_FILE_PRESETS DEBUG] Raw data causing error: {preset_data[:200]}...") # First 200 chars + pass print("Error parsing blend file presets, resetting to empty") scene["text_texture_presets"] = "{}" - print(f"[GET_BLEND_FILE_PRESETS DEBUG] Reset to empty, returning empty dict") return {} else: - print(f"[GET_BLEND_FILE_PRESETS DEBUG] Preset data is not string, returning empty dict") - print(f"[GET_BLEND_FILE_PRESETS DEBUG] ================================================================") + pass return {} except Exception as e: - print(f"[GET_BLEND_FILE_PRESETS DEBUG] EXCEPTION occurred: {e}") + pass import traceback - print(f"[GET_BLEND_FILE_PRESETS DEBUG] Full traceback: {traceback.format_exc()}") print(f"Error getting blend file presets: {e}") return {} def save_blend_file_preset(preset_name, preset_data): + pass """Save a preset to the current blend file""" try: + pass import bpy import json scene = bpy.context.scene # DETAILED LOGGING: Function entry - print(f"[SAVE_BLEND_FILE_PRESET DEBUG] ==================== SAVING TO BLEND FILE ===================") - print(f"[SAVE_BLEND_FILE_PRESET DEBUG] Function called with preset_name: '{preset_name}'") - print(f"[SAVE_BLEND_FILE_PRESET DEBUG] Preset data type: {type(preset_data)}") if isinstance(preset_data, dict): - print(f"[SAVE_BLEND_FILE_PRESET DEBUG] Preset data keys: {list(preset_data.keys())}") - print(f"[SAVE_BLEND_FILE_PRESET DEBUG] Shader properties in preset_data:") - print(f"[SAVE_BLEND_FILE_PRESET DEBUG] disable_shadows: {preset_data.get('disable_shadows', 'NOT FOUND')}") - print(f"[SAVE_BLEND_FILE_PRESET DEBUG] disable_reflections: {preset_data.get('disable_reflections', 'NOT FOUND')}") - print(f"[SAVE_BLEND_FILE_PRESET DEBUG] disable_backfacing: {preset_data.get('disable_backfacing', 'NOT FOUND')}") + pass + print(f"Preset data keys: {list(preset_data.keys())}") else: - print(f"[SAVE_BLEND_FILE_PRESET DEBUG] ERROR: preset_data is not a dictionary!") + pass + print("ERROR: preset_data is not a dictionary!") # Get existing presets - print(f"[SAVE_BLEND_FILE_PRESET DEBUG] Getting existing presets from blend file...") presets = get_blend_file_presets() - print(f"[SAVE_BLEND_FILE_PRESET DEBUG] Existing presets count: {len(presets)}") - print(f"[SAVE_BLEND_FILE_PRESET DEBUG] Existing preset names: {list(presets.keys())}") # Add or update the preset - print(f"[SAVE_BLEND_FILE_PRESET DEBUG] Adding/updating preset '{preset_name}'...") presets[preset_name] = preset_data - print(f"[SAVE_BLEND_FILE_PRESET DEBUG] Presets now contains {len(presets)} items") # VERIFY: Check that shader properties are in the preset we're about to save if preset_name in presets and isinstance(presets[preset_name], dict): + pass saved_preset = presets[preset_name] - print(f"[SAVE_BLEND_FILE_PRESET DEBUG] VERIFICATION - Shader properties in final preset:") - print(f"[SAVE_BLEND_FILE_PRESET DEBUG] disable_shadows: {saved_preset.get('disable_shadows', 'NOT FOUND')}") - print(f"[SAVE_BLEND_FILE_PRESET DEBUG] disable_reflections: {saved_preset.get('disable_reflections', 'NOT FOUND')}") - print(f"[SAVE_BLEND_FILE_PRESET DEBUG] disable_backfacing: {saved_preset.get('disable_backfacing', 'NOT FOUND')}") + print(f"Verified preset saved successfully") # Save back to scene custom properties - print(f"[SAVE_BLEND_FILE_PRESET DEBUG] Converting presets to JSON...") json_data = json.dumps(presets, indent=2) - print(f"[SAVE_BLEND_FILE_PRESET DEBUG] JSON data length: {len(json_data)} characters") - print(f"[SAVE_BLEND_FILE_PRESET DEBUG] Saving to scene custom properties...") scene["text_texture_presets"] = json_data - print(f"[SAVE_BLEND_FILE_PRESET DEBUG] Successfully saved to scene['text_texture_presets']") # FINAL VERIFICATION: Read back from scene to confirm save - print(f"[SAVE_BLEND_FILE_PRESET DEBUG] FINAL VERIFICATION - Reading back from scene...") readback_data = scene.get("text_texture_presets", "{}") if readback_data: + pass try: + pass readback_presets = json.loads(readback_data) if preset_name in readback_presets: + pass readback_preset = readback_presets[preset_name] - print(f"[SAVE_BLEND_FILE_PRESET DEBUG] READBACK VERIFICATION - Shader properties:") - print(f"[SAVE_BLEND_FILE_PRESET DEBUG] disable_shadows: {readback_preset.get('disable_shadows', 'NOT FOUND')}") - print(f"[SAVE_BLEND_FILE_PRESET DEBUG] disable_reflections: {readback_preset.get('disable_reflections', 'NOT FOUND')}") - print(f"[SAVE_BLEND_FILE_PRESET DEBUG] disable_backfacing: {readback_preset.get('disable_backfacing', 'NOT FOUND')}") + print(f"Readback verification successful") else: - print(f"[SAVE_BLEND_FILE_PRESET DEBUG] ERROR: Preset '{preset_name}' not found in readback data!") + pass + print(f"ERROR: Preset '{preset_name}' not found in readback data!") except json.JSONDecodeError as json_err: - print(f"[SAVE_BLEND_FILE_PRESET DEBUG] ERROR: Could not parse readback JSON: {json_err}") + pass + print(f"ERROR: Could not parse readback JSON: {json_err}") else: - print(f"[SAVE_BLEND_FILE_PRESET DEBUG] ERROR: No data in scene['text_texture_presets'] after save!") + pass + print(f"ERROR: No data in scene['text_texture_presets'] after save!") print(f"Saved preset '{preset_name}' to blend file") - print(f"[SAVE_BLEND_FILE_PRESET DEBUG] ================================================================") return True except Exception as e: - print(f"[SAVE_BLEND_FILE_PRESET DEBUG] EXCEPTION occurred: {e}") + pass import traceback - print(f"[SAVE_BLEND_FILE_PRESET DEBUG] Full traceback: {traceback.format_exc()}") print(f"Error saving preset to blend file: {e}") return False def delete_blend_file_preset(preset_name): + pass """Delete a preset from the current blend file""" try: + pass import bpy import json scene = bpy.context.scene @@ -281,51 +276,57 @@ def delete_blend_file_preset(preset_name): # Remove the preset if it exists if preset_name in presets: + pass del presets[preset_name] scene["text_texture_presets"] = json.dumps(presets, indent=2) print(f"Deleted preset '{preset_name}' from blend file") return True else: + pass print(f"Preset '{preset_name}' not found in blend file") return False except Exception as e: + pass print(f"Error deleting preset from blend file: {e}") return False def migrate_from_legacy_system(): + pass """ One-time migration from old three-tier system to simplified two-tier system. Moves LOCAL_FILE presets to PERSISTENT_FILE and creates backup. """ try: + pass legacy_dir = get_preset_path() persistent_dir = get_persistent_preset_path() # Check if legacy directory exists and is different from persistent if not os.path.exists(legacy_dir) or legacy_dir == persistent_dir: - print("[TTG] No separate legacy presets directory found, migration not needed") + pass return True # Check for migration marker file to avoid duplicate migrations migration_marker = os.path.join(persistent_dir, ".migration_complete") if os.path.exists(migration_marker): - print("[TTG] Migration already completed previously") + pass return True # Find legacy preset files legacy_files = [] for filename in os.listdir(legacy_dir): + pass if filename.endswith('.json'): + pass legacy_files.append(filename) if not legacy_files: - print("[TTG] No legacy presets to migrate") + pass # Create marker even if no files to migrate with open(migration_marker, 'w') as f: f.write("Migration completed - no legacy presets found") return True - print(f"[TTG] Found {len(legacy_files)} legacy presets to migrate") # Create backup directory in persistent location backup_dir = os.path.join(persistent_dir, "legacy_backup") @@ -334,8 +335,11 @@ def migrate_from_legacy_system(): # Load existing persistent presets to avoid conflicts existing_persistent = set() if os.path.exists(persistent_dir): + pass for filename in os.listdir(persistent_dir): + pass if filename.endswith('.json'): + pass existing_persistent.add(filename) migrated_count = 0 @@ -343,27 +347,32 @@ def migrate_from_legacy_system(): # Migrate each legacy preset for filename in legacy_files: + pass preset_name = os.path.splitext(filename)[0] legacy_file = os.path.join(legacy_dir, filename) persistent_file = os.path.join(persistent_dir, filename) backup_file = os.path.join(backup_dir, filename) try: + pass # Create backup copy first import shutil shutil.copy2(legacy_file, backup_file) # If preset doesn't exist in persistent storage, migrate it if filename not in existing_persistent: + pass shutil.copy2(legacy_file, persistent_file) migrated_count += 1 - print(f"[TTG] Migrated preset '{preset_name}' to persistent storage") else: + pass skipped_count += 1 - print(f"[TTG] Skipped preset '{preset_name}' (already exists in persistent storage)") except Exception as e: - print(f"[TTG] Error migrating preset '{preset_name}': {e}") + pass + print(f"Error migrating preset '{preset_name}': {e}") + + print(f"Migration completed: {migrated_count} migrated, {skipped_count} skipped") # Create migration completion marker with open(migration_marker, 'w') as f: @@ -376,15 +385,14 @@ def migrate_from_legacy_system(): import json json.dump(migration_info, f, indent=2) - print(f"[TTG] ✅ Migration completed: {migrated_count} migrated, {skipped_count} skipped") - print(f"[TTG] Backup created at: {backup_dir}") return True except Exception as e: - print(f"[TTG] ❌ Error during migration: {e}") + pass return False def get_unified_preset_list(): + pass """ Get unified preset list with two-tier loading and priority rules. Returns dict with preset names as keys and (preset_data, source) as values. @@ -395,64 +403,81 @@ def get_unified_preset_list(): all_presets = {} try: + pass # Load persistent presets first (lower priority) persistent_dir = get_persistent_preset_path() if os.path.exists(persistent_dir): + pass for filename in os.listdir(persistent_dir): + pass if filename.endswith('.json') and not filename.startswith('.'): + pass preset_name = os.path.splitext(filename)[0] try: + pass with open(os.path.join(persistent_dir, filename), 'r') as f: preset_data = json.load(f) all_presets[preset_name] = (preset_data, 'PERSISTENT_FILE') except (json.JSONDecodeError, OSError) as e: - print(f"[TTG] Error reading persistent preset {preset_name}: {e}") + pass + print(f"Error reading persistent preset {preset_name}: {e}") # Load blend file presets and override any conflicts (higher priority) blend_presets = get_blend_file_presets() for name, data in blend_presets.items(): + pass all_presets[name] = (data, 'BLEND_FILE') except Exception as e: - print(f"[TTG] Error in get_unified_preset_list: {e}") + pass + print(f"Error in get_unified_preset_list: {e}") return all_presets def save_preset_unified(preset_data, name, save_to_blend_file=False): + pass """ Save a preset to either persistent file or blend file based on user choice. Default behavior saves to persistent file. """ if save_to_blend_file: + pass return save_blend_file_preset(name, preset_data) else: + pass # Save to persistent file try: + pass persistent_dir = get_persistent_preset_path() persistent_file = os.path.join(persistent_dir, f"{name}.json") with open(persistent_file, 'w') as f: json.dump(preset_data, f, indent=2) - print(f"[TTG] Saved preset '{name}' to persistent storage") return True except Exception as e: - print(f"[TTG] Error saving preset to persistent storage: {e}") + pass + print(f"Error saving preset to persistent storage: {e}") return False def initialize_presets(): + pass """Initialize the simplified preset system and run migration if needed.""" try: + pass import bpy if not hasattr(bpy.context, 'scene') or not bpy.context.scene: + pass return props = bpy.context.scene.text_texture_props if not props: + pass return - print(f"[TTG] Starting preset initialization...") + + print("Starting preset initialization...") # Run one-time migration from old system migrate_from_legacy_system() @@ -465,6 +490,7 @@ def initialize_presets(): # Add all presets to UI list for preset_name, (preset_data, source) in all_presets.items(): + pass preset = props.presets.add() preset.name = preset_name preset.source = source @@ -473,40 +499,49 @@ def initialize_presets(): blend_count = len([p for p in props.presets if p.source == 'BLEND_FILE']) persistent_count = len([p for p in props.presets if p.source == 'PERSISTENT_FILE']) - print(f"[TTG] ✅ Initialized {len(props.presets)} presets ({blend_count} blend file, {persistent_count} persistent)") + + print(f"Initialized {len(props.presets)} presets ({blend_count} blend file, {persistent_count} persistent)") except Exception as e: - print(f"[TTG] Critical error in initialize_presets: {e}") + pass + print(f"Critical error in initialize_presets: {e}") import traceback traceback.print_exc() def refresh_presets(): + pass """Refresh the preset list by rescanning files (legacy function)""" import bpy refresh_presets_sync() # Force UI update try: + pass if bpy.context.area: + pass bpy.context.area.tag_redraw() except: pass + pass def refresh_presets_sync(): + pass """Centralized, synchronous preset refresh - guarantees completion""" try: + pass import bpy - print("[TTG] 🔄 Synchronizing presets...") + + print("Synchronizing presets...") # Ensure we have valid context if not hasattr(bpy.context, 'scene') or not bpy.context.scene: - print("[TTG] ⚠️ No valid scene context for preset refresh") + pass return False props = bpy.context.scene.text_texture_props if not props: - print("[TTG] ⚠️ No text_texture_props found") + pass return False # Run the simplified initialization @@ -515,37 +550,45 @@ def refresh_presets_sync(): # Validate that presets are actually loaded preset_count = len(props.presets) if preset_count == 0: - print("[TTG] ⚠️ Warning: No presets found after refresh") + pass + print("Warning: No presets found after refresh") else: - print(f"[TTG] ✅ Preset sync complete - {preset_count} presets available") + pass + print(f"Preset sync complete - {preset_count} presets available") return True except Exception as e: - print(f"[TTG] ❌ Error in refresh_presets_sync: {e}") + pass import traceback traceback.print_exc() return False def ensure_presets_available(): + pass """Defensive programming - ensure presets are loaded before UI operations""" try: + pass import bpy if not hasattr(bpy.context, 'scene') or not bpy.context.scene: + pass return False props = bpy.context.scene.text_texture_props if not props: + pass return False # If no presets loaded, try to load them if len(props.presets) == 0: - print("[TTG] 🛡️ No presets available - attempting to load...") + pass + print("No presets available - attempting to load...") return refresh_presets_sync() return True except Exception as e: - print(f"[TTG] Error in ensure_presets_available: {e}") + pass + print(f"Error in ensure_presets_available: {e}") return False \ No newline at end of file diff --git a/src/properties/__pycache__/__init__.cpython-311.pyc b/src/properties/__pycache__/__init__.cpython-311.pyc index c5cb4ea39b0d649e19a51a0def50909a03799e6a..8426375652de01d505db7dc9d229dffe73979168 100644 GIT binary patch delta 20 acmX@aa)^a{IWI340}#YtIJ%MBoCyFpX9Y|E delta 20 acmX@aa)^a{IWI340}vSV9ofij&IAB5q6A$4 diff --git a/src/properties/__pycache__/property_groups.cpython-311.pyc b/src/properties/__pycache__/property_groups.cpython-311.pyc index f7b56d2f71f514afefcccf7343b391247568eaaa..12cba6fede66589f3234fbef52e55d549bc07ece 100644 GIT binary patch delta 7509 zcma)>31Ae}8GvW@-ksfKlg&XwA^~!+5Xd1{4j>5zj6i?@ns8(Z`G>H9WaI2|8rW2+ zQpJisDYVuWQG=MM&_!EYTea2NYE`hHaTSkxYg?C!ibqTTH#;+#WM`ub|GzWyzW2ZP z|L=eA&AvSLBD?>2HsOfFVPo*)Ir;R4?48FG&g;14EW_Nu9$>7Dm00yTM!l5*h7{^E zgWbAzJ%#DVzst%5kd!|Gi+sf}pzGB!Odm@0BNQPkHWR&XH_0XKX8G!ns=oY;VboUJ zSu3fwr3G1L54)ZP{cg)neYajsc&u{G?`%!g8@Ud{bnC&;Zyefp7M))virZ8lHu_t2 zd?zV0j^P@x-hL@e9%Fuib379;4BELj% z!eOqiqsu1*Y6Zb3kaAOBdOkk&uR?X9hZ(RtBL<@*b-&&<^y#cUaqs-S^{?3T zUb5$%$uB#dUvjD{LTUbjrPxT$Syfh1Wkv0jh)7tPbD)?#A? zCM-jE>x{H)917I{<4`zROy*PZ&+qWHUl1J>*67C#b z#ypx{?b1J)%vQU|SN4J+jhBw$^}vD-^t_?p(2EJtN0UPQ@J29J;+4plzzi0!B4z6} zB83wDAjL_P3GCp21V{uYa!I{7&HI$8j$E?BxgZ6C@xfFDOoMcA!YIhlG?=Myqah2M z&4x=fxiJ!Fh8!3R<6u1G@`j*B5A!6@0{KwT#=wNbEV$9+#2B9^DNPr`WSD}D7eO(S zCB3+$^(o65dR?lJm%>!YP)PF7=*$8uOotgT6D!6Bzl>Ida?HRA@pvyzp zw7Cmf0=!U0HYZNcD8olWIel*SZ(bMJ-o*=)5-P}3iJnZQRD2lLcX&HdwQR%USxzsT z9y;QDoXuK{6^ppfcC@Kt*MqE$yy?m{3p9U(EaFO; zRG3RI#pIt(uc)d=@o&e;*Q5GJ7nx9dO1sV^)|gJhX>RY(^u+k!-qWY-KfFOnP9sRk z*`@vP_>pS163_l(d|fBN0sQ77zm)ZRGlDfMw-qtKr%7{L9v`Z zZADQ&@*4J84X!o^7@k>)o5)Jt3c58*Gq+#Yi;G~NvJqog^isYVdDGzb+OBM`v0EHY z1u#jK^x`yU`>iq2wkZUM(_D*iCp2HRbDfATYv7bWxxRPf?=f#2Cgga57$>eMxSl`>l%l~)hpj0Fn5vM|Z~+;KoFS2Rm@Ha}ZJ z9WV!~aE+{n8U*JKTgDA=nZnh=yb-wh3U@iw!4R~mkfo5bpF$=?5*zE49l;<- zupM|<&nKe6witsOB*qF}Xorn34yjG*K#OtiP`cI$K46gv#`djCY3^zea3}KPP82|N z$he8-HY?;7*ox#fB)4mlI~4L7=!TuJ3v(ez$zV($dnA;DAHcP69fr9W(~KtL?wC;j zP+_l!8@_|xBe8n;5!@*HPF)YG-#QF%lLR?nFWijT@Z&i7aErq2gIh=7Zc{o);CAdF zoiF><&dBQzodrKp-~(__3zk+Q%KlVh?eH_W1AY#7VnDCc4E--8QV+j`Low+n&HuX; z=rG)^1ZQY$?opU~p*Ie5L}BiOqj8w~CB^}L&>zIP*$59{&tqr%g9`W%9D@mP9Db$A zg(QxH6YwxJ!6Wc%P3|`mXNO1Ox0tTK!*mTkrWv%~OK1Z80UpQc`NRm*^GSvMBm4>3 zr$%6(mRKjB)c&W?s zDfNPc=;1{;skwKZQizw}@0!GEg*XE*YZ9+WgdGOp)ix#wVH}0m`d^P((atL18}KHM z)?3KEt-<|6Y4RO-7n&8%2Dk0^B!YwY;RDQ%V)(HCBWc}H`TwzE=o9#-rlEgHgcbe` zpW3Bjz0dpUW?8w3Zpg5)G^NI|G(m<<8)>4TY0Cgz;DX?4wPJ z@67avA6`39612R-=ZPwlPP|W&O~zQ1fBe~za-pQOF{o1yQ+I=DTJs+Z(p7O8h4&fo zVRR#+%j`b3KXdqIgv@BE4()apO18MTX}(cl=_Qm7(w7gs8H17o3tyCEH(e3Sx|2`KWg(r+AEbR+22@MEIX5DnNQltM9Jlf{5di4(6PRp*Ma zPD#vGgWmFVfaPDM)a^@ysHWlux)i^s#qoQ(WSUl(fihF#%i{3mD!u~wO40f(&8<90 zdnCVp(y+`ysfv?w)hZurkeeHK0F)P1q-s&-p%}5-ONMtD^Ir^#{KiT{emP2=81gH` zkS~Z6@`X}&5|CenQa|#0Bod2N4$w?n3PFuuF<8)`0+-?a^0=O^kVu+pSE5{n(umRo z`%&xZihyaRm1QewO`89->ME4gDBrK@8damsSj&U5mQGLn9;wL|sUD5kI+Rujj`-@0 zi2U+RJISN0rz;(Px1ktO^rC+oG-i=kYK&%8JIY3_7zV}B-=y-V1JTatVnfZ35I~>G zn=WLp76(9x|N4Q9U)8Aq-Um0w$@MKNa4Uk_w0bXge!HskbT8O}a?SS`jBZ-bg7D5| z%!FO!1$UNho|}!HAdMzj6GyXUWah+Lwk@=4;t_V~4%d~u;P?7EbLu+R`-F~`0ABCN zTeiWswP=GE_#BPm96#Smv9OEZ*45Gpo-I)m9xs|8d4*F(4Ly7boA7*U6W@tPR2vK5 zFR#W9wxQt1TNOQbHLH$fhMq@-tz81&>ZLstDnd6+ZemTlv7)eod_1L=olA;~KB%ps z4~D9b4DvI#MP*w&+@cMH4Sw$qUbu`>;;~l+SzTPq&LoG6o1CUzL6^j% zv_XG)wDLxhHRm?dS$g>fIWec1eVe$d zZq2$1AGp&yx8+XDDU7}MO^`sK9^ z!eMfIT~5Y5lyWwB+cu~%;UW`yzV3|9bdk1ynB2E;9BT_bw{WTMJG%NU z)zGSDuQ@GOHsf}M%^nZL{! z{~3QHS+o97a12ZTwxiNDY&x41VH8ygENhA|+WL^=oLF*RUA|DCL~F#VgOktEr{@QY X@~f|QP*z(X>^LWuoR3p39`XJk?S?ZH delta 7798 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 nczKOFmeq(JpL9 diff --git a/src/properties/property_groups.py b/src/properties/property_groups.py index 0cc2d05..2f37140 100644 --- a/src/properties/property_groups.py +++ b/src/properties/property_groups.py @@ -18,55 +18,65 @@ import os # Import update functions from the main module (will need to be adjusted during integration) def update_live_preview(self, context): + pass """Update preview when properties change""" if not hasattr(context, 'scene'): + pass return props = context.scene.text_texture_props - print(f"DEBUG: update_live_preview called, is_updating: {props.is_updating}") # Always update preview unless already updating if not props.is_updating: - print("DEBUG: Triggering preview generation from property update") + pass # Use a timer to debounce rapid property changes for better performance if hasattr(update_live_preview, '_timer'): + pass bpy.app.timers.unregister(update_live_preview._timer) def delayed_preview(): + pass # Import generate_preview function (will need to be adjusted during integration) try: + pass from . import generate_preview generate_preview(context) except ImportError: + pass # Fallback during development - print("DEBUG: generate_preview not available during extraction") return None # Stop timer update_live_preview._timer = delayed_preview bpy.app.timers.register(delayed_preview, first_interval=0.1) def get_font_enum_items(self, context): + pass """Dynamic enum items for font selection""" items = [("default", "Default Font", "Use system default font", 0)] if not hasattr(get_font_enum_items, 'cached_fonts'): + pass # Import get_system_fonts function (will need to be adjusted during integration) try: + pass from ..utils.system import get_system_fonts get_font_enum_items.cached_fonts = get_system_fonts() except ImportError: + pass # Fallback during development get_font_enum_items.cached_fonts = {} fonts = get_font_enum_items.cached_fonts for i, (font_name, font_path) in enumerate(sorted(fonts.items())[:50]): + pass items.append((font_path, font_name, f"Font: {font_name}", i + 1)) return items class TEXT_TEXTURE_ImageOverlay(PropertyGroup): + pass """Single image overlay item""" name: StringProperty( name="Image Name", @@ -168,6 +178,7 @@ class TEXT_TEXTURE_ImageOverlay(PropertyGroup): ) class TEXT_TEXTURE_Preset(PropertyGroup): + pass """Single preset item""" name: StringProperty( name="Preset Name", @@ -186,6 +197,7 @@ class TEXT_TEXTURE_Preset(PropertyGroup): ) class TEXT_TEXTURE_Properties(PropertyGroup): + pass """Properties for text texture generation""" is_updating: BoolProperty(default=False) @@ -238,6 +250,12 @@ class TEXT_TEXTURE_Properties(PropertyGroup): default=False ) + expand_version_info: BoolProperty( + name="Version Information", + description="Show/hide Version Information section", + default=False + ) + text: StringProperty( name="Text", description="Text to render as texture", diff --git a/src/ui/__init__.py b/src/ui/__init__.py index b16a4cf..7a9e079 100644 --- a/src/ui/__init__.py +++ b/src/ui/__init__.py @@ -8,14 +8,17 @@ from .panels import ( # Menu functions for Blender integration def menu_func_node_editor(self, context): + pass """Add menu item to Node Editor's Add menu""" self.layout.operator("text_texture.generate", text="Text Texture", icon='IMAGE_DATA') def menu_func_view3d(self, context): + pass """Add menu item to 3D View's Add menu""" self.layout.operator("text_texture.generate", text="Text Texture", icon='IMAGE_DATA') def menu_func_image(self, context): + pass """Add menu item to Image Editor menu""" self.layout.operator("text_texture.generate", text="Text Texture", icon='IMAGE_DATA') diff --git a/src/ui/__pycache__/__init__.cpython-311.pyc b/src/ui/__pycache__/__init__.cpython-311.pyc index 9107426d27a0d314bf27fafb9ea09e84a1a802d4..9a0a99736f3cdedcbc1f655534133b8bf4e486aa 100644 GIT binary patch delta 492 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 Q7IPcA*&AwNSG*YTzikAh$p8QV delta 2211 zcmah~Yit|G5#HtT$h)IRN+M4b_2NkuB~d0N#ftojO+=0@#fh!7c3j7_1N)>qQmiRb z-8)4VLB?+3A3|6KTZ=4gX=9y^uA;GfVU{U zDrwHsNVB9_Jd3Vy=g=&U)aKuo6lgE%hN+~JimD`YSyP!NxoMqb=IDUX*4$@VoYKu1 zk~NeU$&Ah{%}HHJ(wGqFjVdE*#;}xWQ^{pBa|+S3nobO5W*Xc~sD{4bgZ4Y;0&EKr z`-6YOi?|;H{h9Okc#JLxKfxaQtoR%?Z6kC^Z1eYm4a1@xd45_uS{udi1nIxa0!x2qq2L7#R#>9+AQSZ)Hn?i#$x!w4*mVfesz z`bI?bnLO-7cO4kSyUtz1ecat%OO9bC_dU}C6TfQg2fQarBZs+rzR<{i?%sYLKNXTUR&Gt>}!0FTOW_qTk7Vo8#HEEtZjZ7DP-OR1qkZnfI86WOZp94?o)|Z3iuC7vqg3Py99VRN1b(o{Yu&}4I|$i4y}yb$cf^8u_nJ?BbKhcoA$~Jf_6<~g1K_nT^7+u$ zOQ$ZDhb~oyE?J5-f3Vba?l&<2c*_1n#h)no6D!Rrl6R8h=D_Deeh0K{VB}-w&oX5p%4Qh>5hFGmq&2%=U z7;}cHPwk|ns%$QO%tr&3slaKX!`x*Z3YM8u#&tz~MNMba@yuqqx>J%narid6&5>>5 zktl?C+qP=!8(76jgQZGQNQAZo;y6Xm2cE=x?#u^p!p7@NfeFUK7?_?i>5Q7k@pz)KhltspCo$Ke`fVUd%7#-zdCU_(LFiKM*a&4wnP*N+4cE4!L8k zv+LIA$0t5K@u~k)v)p;8LjTfMN55?A?rdK=w!Y?Tn18kG>#F#=mOJkIq9tE+#UGqM z27LF&)*m_O^P!nbdte(8JQk5wx$)__jpIEEx>fFL$4?O8H6nHfEXjmb7pr(|q`^8_ z=D 0: - move_up_op = header_row.operator("text_texture.move_overlay", text="", icon='TRIA_UP') - move_up_op.overlay_index = i - move_up_op.direction = 'UP' + move_up_op = safe_operator_call(header_row, "text_texture.move_overlay", premium_lock, + text="", icon='TRIA_UP') + if move_up_op: + move_up_op.overlay_index = i + move_up_op.direction = 'UP' if i < len(props.image_overlays) - 1: - move_down_op = header_row.operator("text_texture.move_overlay", text="", icon='TRIA_DOWN') - move_down_op.overlay_index = i - move_down_op.direction = 'DOWN' + move_down_op = safe_operator_call(header_row, "text_texture.move_overlay", premium_lock, + text="", icon='TRIA_DOWN') + if move_down_op: + move_down_op.overlay_index = i + move_down_op.direction = 'DOWN' # Show details section with enable/disable state details_col = box.column() @@ -94,6 +286,7 @@ def draw_image_overlays_section(layout, props): # Status indicator if not overlay.enabled: + pass status_row = details_col.row() status_row.label(text="⚠️ Overlay Disabled", icon='INFO') details_col.separator() @@ -105,6 +298,7 @@ def draw_image_overlays_section(layout, props): details_col.prop(overlay, "positioning_mode", text="Position Mode") if overlay.positioning_mode == 'ABSOLUTE': + pass # Position controls for absolute mode pos_row = details_col.row(align=True) pos_row.prop(overlay, "x_position", text="X Position") @@ -121,6 +315,7 @@ def draw_image_overlays_section(layout, props): trans_row.prop(overlay, "z_index", text="Z-Level") def draw_positioning_section(layout, props): + pass """Draw positioning and alignment controls""" from ..utils.system import get_anchor_point_matrix @@ -142,30 +337,40 @@ def draw_positioning_section(layout, props): row = layout.row(align=True) row.prop(props, "prepend_text", text="Prepend") if props.prepend_text.strip(): + pass row.prop(props, "prepend_margin", text="", slider=True) row = layout.row(align=True) row.prop(props, "append_text", text="Append") if props.append_text.strip(): + pass row.prop(props, "append_margin", text="", slider=True) layout.separator() layout.prop(props, "position_mode", text="Mode") if props.position_mode == 'PRESET': + pass layout.separator() layout.label(text="Anchor Point:", icon='ANCHOR_TOP') anchor_matrix = get_anchor_point_matrix() for row_idx, row in enumerate(anchor_matrix): + pass grid_row = layout.row(align=True) grid_row.scale_y = 1.2 for col_idx, anchor in enumerate(row): + pass if anchor == props.anchor_point: - op = grid_row.operator("text_texture.set_anchor_point", text="●", depress=True) + premium_lock = create_premium_lock_function() + op = safe_operator_call(grid_row, "text_texture.set_anchor_point", premium_lock, + text="●", depress=True) else: - op = grid_row.operator("text_texture.set_anchor_point", text="○") - op.anchor_point = anchor + premium_lock = create_premium_lock_function() + op = safe_operator_call(grid_row, "text_texture.set_anchor_point", premium_lock, + text="○") + if op: + op.anchor_point = anchor # Margins layout.separator() @@ -176,6 +381,7 @@ def draw_positioning_section(layout, props): margins_row = layout.row(align=True) margins_row.prop(props, "margin_x", text="X") if not props.margins_linked: + pass margins_row.prop(props, "margin_y", text="Y") # Offsets @@ -196,6 +402,7 @@ def draw_positioning_section(layout, props): layout.prop(props, "constrain_to_canvas") def draw_font_settings_section(layout, props): + pass """Draw extended font settings""" # Font size moved here from top level layout.prop(props, "font_size") @@ -211,6 +418,7 @@ def draw_font_settings_section(layout, props): # Custom font controls moved here from top level layout.prop(props, "use_custom_font") if props.use_custom_font: + pass layout.prop(props, "custom_font_path", text="") layout.separator() @@ -219,6 +427,7 @@ def draw_font_settings_section(layout, props): row.prop(props, "text_align") def draw_colors_section(layout, props): + pass """Draw color controls for text and background""" layout.label(text="Text Color:", icon='COLOR') layout.prop(props, "text_color", text="") @@ -227,72 +436,110 @@ def draw_colors_section(layout, props): layout.label(text="Background Color:", icon='IMAGE_PLANE') layout.prop(props, "background_transparent") if not props.background_transparent: + pass layout.prop(props, "background_color", text="") def draw_preview_options_section(layout, props): + pass """Draw preview options (preview-specific settings only)""" layout.prop(props, "preview_size", text="Preview Size") layout.separator() layout.label(text="Preview Background Display:", icon='SCENE') layout.prop(props, "preview_bg_type", text="Background") if props.preview_bg_type == 'color': + pass layout.prop(props, "preview_bg_color", text="Preview Display Color") elif props.preview_bg_type == 'gradient': layout.prop(props, "preview_bg_color", text="Color 1") layout.prop(props, "preview_bg_color2", text="Color 2") def draw_presets_section(layout, props): + pass """Draw unified presets save/load controls""" - # Save preset section + # Check if presets are available, show lock if not + if not has_presets(): + pass + draw_feature_lock_indicator(layout, 'presets') + return + + # Save preset section - use safe operator calls save_row = layout.row(align=True) save_row.prop(props, "new_preset_name", text="", placeholder="Preset Name") - save_row.operator("text_texture.save_preset", text="Save", icon='FILE_NEW') + + premium_lock = create_premium_lock_function() + safe_operator_call(save_row, "text_texture.save_preset", premium_lock, + text="Save", icon='FILE_NEW') # Save with blend file checkbox layout.prop(props, "save_with_blend_file") layout.separator() - # Import/Export and refresh buttons + # Import/Export and refresh buttons - use safe operator calls backup_row = layout.row(align=True) - backup_row.operator("text_texture.export_presets", text="Export Backup", icon='EXPORT') - backup_row.operator("text_texture.import_presets", text="Import Backup", icon='IMPORT') + + export_lock = create_premium_lock_function() + import_lock = create_premium_lock_function() + + safe_operator_call(backup_row, "text_texture.export_presets", export_lock, + text="Export Backup", icon='EXPORT') + safe_operator_call(backup_row, "text_texture.import_presets", import_lock, + text="Import Backup", icon='IMPORT') utility_row = layout.row(align=True) - utility_row.operator("text_texture.refresh_presets", text="Refresh", icon='FILE_REFRESH') - utility_row.operator("text_texture.show_migration_report", text="Migration Report", icon='INFO') + + refresh_lock = create_premium_lock_function() + migration_lock = create_premium_lock_function() + + safe_operator_call(utility_row, "text_texture.refresh_presets", refresh_lock, + text="Refresh", icon='FILE_REFRESH') + safe_operator_call(utility_row, "text_texture.show_migration_report", migration_lock, + text="Migration Report", icon='INFO') layout.separator() # Load presets section - unified list if props.presets: + pass layout.label(text=f"📁 Available Presets ({len(props.presets)}):", icon='PRESET') # Sort presets alphabetically for consistent display sorted_presets = sorted(props.presets, key=lambda p: p.name.lower()) for preset in sorted_presets: + pass row = layout.row(align=True) row.scale_y = 0.9 # Storage indicator icon if preset.source == 'BLEND_FILE': + pass icon = '📄' # Blend file indicator else: + pass icon = '🏠' # Persistent storage indicator - # Load button with indicator - load_btn = row.operator("text_texture.load_preset", text=f"{icon} {preset.name}") - load_btn.preset_name = preset.name + # Load button with indicator - use safe operator call + load_lock = create_premium_lock_function() + load_btn = safe_operator_call(row, "text_texture.load_preset", load_lock, + text=f"{icon} {preset.name}") + if load_btn: + load_btn.preset_name = preset.name - # Quick overwrite button - overwrite_btn = row.operator("text_texture.save_preset", text="", icon='FILE_REFRESH') - overwrite_btn.overwrite = True - overwrite_btn.preset_name = preset.name + # Quick overwrite button - use safe operator call + overwrite_lock = create_premium_lock_function() + overwrite_btn = safe_operator_call(row, "text_texture.save_preset", overwrite_lock, + text="", icon='FILE_REFRESH') + if overwrite_btn: + overwrite_btn.overwrite = True + overwrite_btn.preset_name = preset.name - # Delete button - delete_btn = row.operator("text_texture.delete_preset", text="", icon='TRASH') - delete_btn.preset_name = preset.name + # Delete button - use safe operator call + delete_lock = create_premium_lock_function() + delete_btn = safe_operator_call(row, "text_texture.delete_preset", delete_lock, + text="", icon='TRASH') + if delete_btn: + delete_btn.preset_name = preset.name # Legend for storage indicators layout.separator() @@ -303,6 +550,7 @@ def draw_presets_section(layout, props): legend_box.label(text="📄 Blend file (travels with .blend file)") else: + pass layout.label(text="📂 No presets saved yet", icon='INFO') layout.label(text="💡 Save your first preset above!", icon='LIGHT_DATA') layout.separator() @@ -311,7 +559,14 @@ def draw_presets_section(layout, props): info_box.label(text="Check the box to save with your .blend file!") def draw_shader_section(layout, props): + pass """Draw shader generation controls""" + # Check if shader generation is available + from ..utils.constants import has_shader_generation + if not has_shader_generation(): + draw_feature_lock_indicator(layout, 'shader_generation') + return + # Create Shader Button (as requested) row = layout.row() row.scale_y = 1.5 @@ -325,8 +580,10 @@ def draw_shader_section(layout, props): # Show relevant options based on shader type if props.shader_type == 'PRINCIPLED': + pass col.prop(props, "shader_connection", text="Connect") if props.shader_connection == 'EMISSION': + pass col.prop(props, "emission_strength", text="Strength") elif props.shader_type == 'EMISSION': col.prop(props, "emission_strength", text="Strength") @@ -340,28 +597,42 @@ def draw_shader_section(layout, props): # Clean status info if props.auto_assign_material: + pass layout.separator() info_row = layout.row() info_row.scale_y = 0.8 try: + pass context = bpy.context if context and context.active_object and context.active_object.type in ['MESH', 'CURVE']: + pass info_row.label(text=f"→ {context.active_object.name}", icon='CHECKMARK') else: + pass info_row.label(text="Select mesh/curve object", icon='INFO') except: + pass info_row.label(text="Select mesh/curve object", icon='INFO') def draw_shader_options_only(layout, props): + pass """Draw only shader options without the Create button - for collapsible section""" + # Check if shader generation is available + from ..utils.constants import has_shader_generation + if not has_shader_generation(): + draw_feature_lock_indicator(layout, 'shader_generation') + return + # Clean grid layout for shader options grid = layout.grid_flow(row_major=True, columns=2, align=True) grid.prop(props, "shader_type", text="Type") if props.shader_type == 'PRINCIPLED': + pass grid.prop(props, "shader_connection", text="Connect") if props.shader_connection == 'EMISSION': + pass grid.prop(props, "emission_strength", text="Strength") elif props.shader_type == 'EMISSION': grid.prop(props, "emission_strength", text="Strength") @@ -379,12 +650,14 @@ def draw_shader_options_only(layout, props): row.prop(props, "disable_backfacing", toggle=True) def draw_normal_maps_section(layout, props): + pass """Draw normal map generation controls""" # Main toggle for normal map generation layout.prop(props, "generate_normal_map", text="Generate Normal Map", icon='TEXTURE') # Show controls only if normal map generation is enabled if props.generate_normal_map: + pass layout.separator() # Normal map settings in a clean layout @@ -405,12 +678,14 @@ def draw_normal_maps_section(layout, props): info_box.label(text="• Invert changes raised/recessed effect") def draw_text_fitting_section(layout, props): + pass """Draw text fitting controls""" # Main toggle for text fitting layout.prop(props, "enable_text_fitting", text="Enable Text Fitting", icon='FULLSCREEN_ENTER') # Show controls only if text fitting is enabled if props.enable_text_fitting: + pass layout.separator() # Fitting mode selection @@ -437,6 +712,7 @@ def draw_text_fitting_section(layout, props): info_box.label(text="💡 Text fitting automatically sizes text", icon='INFO') if props.text_fitting_mode == 'FIT_WIDTH': + pass info_box.label(text="• Fits text width to canvas width") elif props.text_fitting_mode == 'FIT_HEIGHT': info_box.label(text="• Fits text height to canvas height") @@ -448,12 +724,14 @@ def draw_text_fitting_section(layout, props): info_box.label(text="• Respects min/max font size limits") def draw_multiline_section(layout, props): + pass """Draw multiline text controls""" # Main toggle for multiline text layout.prop(props, "enable_multiline", text="Enable Multiline", icon='TEXT') # Show controls only if multiline is enabled if props.enable_multiline: + pass layout.separator() # Line height and alignment settings @@ -466,6 +744,7 @@ def draw_multiline_section(layout, props): # Auto wrap settings layout.prop(props, "auto_wrap", text="Auto Wrap") if props.auto_wrap: + pass layout.prop(props, "wrap_width", text="Wrap Width (0 = texture width)") layout.separator() @@ -486,12 +765,20 @@ def draw_multiline_section(layout, props): info_box.label(text="• Line height controls spacing") def draw_stroke_section(layout, props): + pass """Draw stroke and outline controls""" + # Check if stroke effects are available + from ..utils.constants import has_stroke_effects + if not has_stroke_effects(): + draw_feature_lock_indicator(layout, 'stroke_effects') + return + # Main toggle for stroke layout.prop(props, "enable_stroke", text="Enable Stroke", icon='MOD_OUTLINE') # Show controls only if stroke is enabled if props.enable_stroke: + pass layout.separator() # Stroke width and color @@ -511,6 +798,7 @@ def draw_stroke_section(layout, props): info_box.label(text="💡 Stroke adds outline to text:", icon='INFO') if props.stroke_position == 'OUTER': + pass info_box.label(text="• Outer: Expands text bounds outward") elif props.stroke_position == 'INNER': info_box.label(text="• Inner: Shrinks text, stroke inside") @@ -521,11 +809,19 @@ def draw_stroke_section(layout, props): info_box.label(text="• Works with multiline text") def draw_shadow_glow_section(layout, props): + pass """Draw shadow and glow effects controls""" + # Check if shadow/glow effects are available + from ..utils.constants import has_shadow_glow_effects + if not has_shadow_glow_effects(): + draw_feature_lock_indicator(layout, 'shadow_glow_effects') + return + # Drop shadow section layout.prop(props, "enable_shadow", text="Enable Drop Shadow", icon='SHADERFX') if props.enable_shadow: + pass layout.separator() # Shadow offset controls in a row @@ -545,6 +841,7 @@ def draw_shadow_glow_section(layout, props): layout.prop(props, "enable_glow", text="Enable Glow", icon='LIGHT_SUN') if props.enable_glow: + pass layout.separator() # Glow properties @@ -554,26 +851,37 @@ def draw_shadow_glow_section(layout, props): # Info box with usage tips if props.enable_shadow or props.enable_glow: + pass layout.separator() info_box = layout.box() info_box.scale_y = 0.8 info_box.label(text="💡 Shadow & Glow effects:", icon='INFO') if props.enable_shadow: + pass info_box.label(text="• Drop shadow: offset + blur for depth") if props.enable_glow: + pass info_box.label(text="• Glow: blur without offset for aura effect") info_box.label(text="• Higher blur = softer effect") info_box.label(text="• Alpha controls effect intensity") def draw_blur_section(layout, props): + pass """Draw blur effects controls""" + # Check if blur effects are available + from ..utils.constants import has_blur_effects + if not has_blur_effects(): + draw_feature_lock_indicator(layout, 'blur_effects') + return + # Main toggle for blur layout.prop(props, "enable_blur", text="Enable Blur", icon='MOD_SMOOTH') # Show controls only if blur is enabled if props.enable_blur: + pass layout.separator() # Blur radius setting @@ -595,6 +903,7 @@ def draw_blur_section(layout, props): # ============================================================================ class TEXT_TEXTURE_PT_panel(Panel): + pass """Main Panel in Shader Editor""" bl_label = "Text Texture Generator" bl_idname = "TEXT_TEXTURE_PT_panel" @@ -604,29 +913,29 @@ class TEXT_TEXTURE_PT_panel(Panel): @classmethod def poll(cls, context): + pass # DEBUG: Log poll method calls - print(f"[TTG DEBUG] Panel poll called - space_data: {context.space_data}") - print(f"[TTG DEBUG] space_data.type: {context.space_data.type if context.space_data else 'None'}") if hasattr(context.space_data, 'tree_type'): - print(f"[TTG DEBUG] tree_type: {context.space_data.tree_type}") + pass else: - print("[TTG DEBUG] No tree_type attribute") + pass # More permissive poll condition to ensure panel shows up if context.space_data.type == 'NODE_EDITOR': + pass if hasattr(context.space_data, 'tree_type'): + pass result = context.space_data.tree_type == 'ShaderNodeTree' - print(f"[TTG DEBUG] Poll result: {result}") return result else: + pass # Allow panel even without tree_type (sometimes happens) - print("[TTG DEBUG] Poll result: True (no tree_type but NODE_EDITOR)") return True - print("[TTG DEBUG] Poll result: False (not NODE_EDITOR)") return False def draw(self, context): + pass layout = self.layout props = context.scene.text_texture_props @@ -642,83 +951,205 @@ class TEXT_TEXTURE_PT_panel(Panel): layout.separator(factor=0.5) draw_generate_button(layout) - # CREATE SHADER BUTTON AT TOP LEVEL - AS REQUESTED + # CREATE SHADER BUTTON - WITH PROPER RESTRICTION layout.separator(factor=0.5) create_shader_row = layout.row() create_shader_row.scale_y = 1.5 - create_shader_row.operator("text_texture.generate_shader", text="Create Shader", icon='MATERIAL') - # Preview Button at top level + # Check if shader generation is available + from ..utils.constants import has_shader_generation + if has_shader_generation(): + create_shader_row.operator("text_texture.generate_shader", text="Create Shader", icon='MATERIAL') + else: + # Show locked shader button for free version + lock_row = create_shader_row.row() + lock_row.enabled = False + lock_row.operator("text_texture.generate_shader", text="🔒 Create Shader (Pro Only)", icon='LOCKED') + + # Preview Button - ALWAYS available (core functionality for all users) layout.separator(factor=0.5) preview_row = layout.row() preview_row.scale_y = 1.5 - preview_row.operator("text_texture.view_preview_zoom", text="Open Preview Window", icon='WINDOW') + preview_row.operator("text_texture.preview", text="Preview Texture", icon='HIDE_OFF') layout.separator(factor=1.2) - # COLLAPSIBLE SECTIONS + # COLLAPSIBLE SECTIONS - FREE FEATURES FIRST + + # === FREE FEATURES === # Multiline Section (collapsed by default) multiline_section = draw_collapsible_header(layout, "expand_multiline", "Multiline Text", 'TEXT') if multiline_section: + pass draw_multiline_section(multiline_section, props) - # Shader Section (collapsed by default) - ONLY OPTIONS, NOT THE BUTTON - shader_section = draw_collapsible_header(layout, "expand_shader", "Shader Options", 'SETTINGS') - if shader_section: - draw_shader_options_only(shader_section, props) - - # Image Overlays Section (collapsed by default) - overlay_section = draw_collapsible_header(layout, "expand_image_overlays", "Image Overlays", 'IMAGE_DATA') - if overlay_section: - draw_image_overlays_section(overlay_section, props) - - # Positioning & Layout Section (collapsed by default) - position_section = draw_collapsible_header(layout, "expand_positioning", "Positioning & Layout", 'ANCHOR_CENTER') - if position_section: - draw_positioning_section(position_section, props) - # Font Settings Section (collapsed by default) font_section = draw_collapsible_header(layout, "expand_font_settings", "Font Settings", 'FONT_DATA') if font_section: + pass draw_font_settings_section(font_section, props) + # Positioning & Layout Section (collapsed by default) - FREE FEATURE moved to free group + position_section = draw_collapsible_header(layout, "expand_positioning", "Positioning & Layout", 'ANCHOR_CENTER') + if position_section: + draw_positioning_section(position_section, props) + # Text Fitting Section (collapsed by default) text_fitting_section = draw_collapsible_header(layout, "expand_text_fitting", "Text Fitting", 'FULLSCREEN_ENTER') if text_fitting_section: + pass draw_text_fitting_section(text_fitting_section, props) - # Stroke Section (collapsed by default) - stroke_section = draw_collapsible_header(layout, "expand_stroke", "Stroke & Outlines", 'MOD_OUTLINE') - if stroke_section: - draw_stroke_section(stroke_section, props) - - # Shadow & Glow Section (collapsed by default) - shadow_glow_section = draw_collapsible_header(layout, "expand_shadow_glow", "Shadow & Glow Effects", 'SHADERFX') - if shadow_glow_section: - draw_shadow_glow_section(shadow_glow_section, props) - - # Blur Section (collapsed by default) - blur_section = draw_collapsible_header(layout, "expand_blur", "Blur Effects", 'MOD_SMOOTH') - if blur_section: - draw_blur_section(blur_section, props) - - # Normal Maps Section (collapsed by default) - normal_maps_section = draw_collapsible_header(layout, "expand_normal_maps", "Normal Maps", 'TEXTURE') - if normal_maps_section: - draw_normal_maps_section(normal_maps_section, props) - # Colors Section (collapsed by default) colors_section = draw_collapsible_header(layout, "expand_colors", "Colors", 'COLOR') if colors_section: + pass draw_colors_section(colors_section, props) - # Presets Section (collapsed by default) - presets_section = draw_collapsible_header(layout, "expand_presets", "Presets", 'PRESET') - if presets_section: - draw_presets_section(presets_section, props) + # === PREMIUM FEATURES === + + # Shader Section (collapsed by default) - ONLY OPTIONS, NOT THE BUTTON + shader_header_row = layout.row(align=True) + + # Gray out header if locked and prevent expansion + from ..utils.constants import has_shader_generation + if not has_shader_generation(): + shader_header_row.enabled = False + # Don't allow expansion for locked features + shader_expanded = None + # Show lock indicator instead of expandable header + lock_header = shader_header_row.row() + lock_header.label(text="Shader Options", icon='SETTINGS') + lock_header.label(text="🔒 PRO", icon='LOCKED') + else: + shader_expanded = draw_collapsible_header(shader_header_row, "expand_shader", "Shader Options", 'SETTINGS') + + # Only show content if available and expanded + if shader_expanded: + draw_shader_options_only(shader_expanded, props) + + # Image Overlays Section (collapsed by default) - Show with premium indicator + overlay_header_row = layout.row(align=True) + + # Gray out header if locked and prevent expansion + from ..utils.constants import has_image_overlays + if not has_image_overlays(): + overlay_header_row.enabled = False + # Don't allow expansion for locked features + overlay_expanded = None + # Show lock indicator instead of expandable header + lock_header = overlay_header_row.row() + lock_header.label(text="Image Overlays", icon='IMAGE_DATA') + lock_header.label(text="🔒 PRO", icon='LOCKED') + else: + overlay_expanded = draw_collapsible_header(overlay_header_row, "expand_image_overlays", "Image Overlays", 'IMAGE_DATA') + + # Only show content if available and expanded + if overlay_expanded: + draw_image_overlays_section(overlay_expanded, props) + + + # Stroke Section (collapsed by default) - Show with premium indicator + stroke_header_row = layout.row(align=True) + + # Gray out header if locked and prevent expansion + from ..utils.constants import has_stroke_effects + if not has_stroke_effects(): + stroke_header_row.enabled = False + # Don't allow expansion for locked features + stroke_expanded = None + # Show lock indicator instead of expandable header + lock_header = stroke_header_row.row() + lock_header.label(text="Stroke & Outlines", icon='MOD_OUTLINE') + lock_header.label(text="🔒 PRO", icon='LOCKED') + else: + stroke_expanded = draw_collapsible_header(stroke_header_row, "expand_stroke", "Stroke & Outlines", 'MOD_OUTLINE') + + # Only show content if available and expanded + if stroke_expanded: + draw_stroke_section(stroke_expanded, props) + + # Shadow & Glow Section (collapsed by default) - Show with premium indicator + shadow_glow_header_row = layout.row(align=True) + + # Gray out header if locked and prevent expansion + from ..utils.constants import has_shadow_glow_effects + if not has_shadow_glow_effects(): + shadow_glow_header_row.enabled = False + # Don't allow expansion for locked features + shadow_glow_expanded = None + # Show lock indicator instead of expandable header + lock_header = shadow_glow_header_row.row() + lock_header.label(text="Shadow & Glow Effects", icon='SHADERFX') + lock_header.label(text="🔒 PRO", icon='LOCKED') + else: + shadow_glow_expanded = draw_collapsible_header(shadow_glow_header_row, "expand_shadow_glow", "Shadow & Glow Effects", 'SHADERFX') + + # Only show content if available and expanded + if shadow_glow_expanded: + draw_shadow_glow_section(shadow_glow_expanded, props) + + # Blur Section (collapsed by default) - Show with premium indicator + blur_header_row = layout.row(align=True) + + # Gray out header if locked and prevent expansion + from ..utils.constants import has_blur_effects + if not has_blur_effects(): + blur_header_row.enabled = False + # Don't allow expansion for locked features + blur_expanded = None + # Show lock indicator instead of expandable header + lock_header = blur_header_row.row() + lock_header.label(text="Blur Effects", icon='MOD_SMOOTH') + lock_header.label(text="🔒 PRO", icon='LOCKED') + else: + blur_expanded = draw_collapsible_header(blur_header_row, "expand_blur", "Blur Effects", 'MOD_SMOOTH') + + # Only show content if available and expanded + if blur_expanded: + draw_blur_section(blur_expanded, props) + + # Normal Maps Section (collapsed by default) - Show with premium indicator + normal_header_row = layout.row(align=True) + + # Gray out header if locked and prevent expansion + if not has_normal_maps(): + normal_header_row.enabled = False + # Don't allow expansion for locked features + normal_expanded = None + # Show lock indicator instead of expandable header + lock_header = normal_header_row.row() + lock_header.label(text="Normal Maps", icon='TEXTURE') + lock_header.label(text="🔒 PRO", icon='LOCKED') + else: + normal_expanded = draw_collapsible_header(normal_header_row, "expand_normal_maps", "Normal Maps", 'TEXTURE') + + # Only show content if available and expanded + if normal_expanded: + draw_normal_maps_section(normal_expanded, props) + + # Presets Section (collapsed by default) - Show with premium indicator + presets_header_row = layout.row(align=True) + + # Gray out header if locked and prevent expansion + if not has_presets(): + presets_header_row.enabled = False + # Don't allow expansion for locked features + presets_expanded = None + # Show lock indicator instead of expandable header + lock_header = presets_header_row.row() + lock_header.label(text="Presets", icon='PRESET') + lock_header.label(text="🔒 PRO", icon='LOCKED') + else: + presets_expanded = draw_collapsible_header(presets_header_row, "expand_presets", "Presets", 'PRESET') + + # Only show content if available and expanded + if presets_expanded: + draw_presets_section(presets_expanded, props) class TEXT_TEXTURE_PT_panel_3d(Panel): + pass """3D View Panel - Access from sidebar (N key)""" bl_label = "Text Texture Generator" bl_idname = "TEXT_TEXTURE_PT_panel_3d" @@ -728,6 +1159,7 @@ class TEXT_TEXTURE_PT_panel_3d(Panel): bl_options = {'DEFAULT_CLOSED'} def draw(self, context): + pass layout = self.layout props = context.scene.text_texture_props @@ -743,87 +1175,205 @@ class TEXT_TEXTURE_PT_panel_3d(Panel): layout.separator(factor=0.5) draw_generate_button(layout) - # CREATE SHADER BUTTON AT TOP LEVEL - AS REQUESTED + # CREATE SHADER BUTTON - WITH PROPER RESTRICTION layout.separator(factor=0.5) create_shader_row = layout.row() create_shader_row.scale_y = 1.5 - create_shader_row.operator("text_texture.generate_shader", text="Create Shader", icon='MATERIAL') - # Preview Button at top level + # Check if shader generation is available + from ..utils.constants import has_shader_generation + if has_shader_generation(): + create_shader_row.operator("text_texture.generate_shader", text="Create Shader", icon='MATERIAL') + else: + # Show locked shader button for free version + lock_row = create_shader_row.row() + lock_row.enabled = False + lock_row.operator("text_texture.generate_shader", text="🔒 Create Shader (Pro Only)", icon='LOCKED') + + # Preview Button - ALWAYS available (core functionality for all users) layout.separator(factor=0.5) preview_row = layout.row() preview_row.scale_y = 1.5 - preview_row.operator("text_texture.view_preview_zoom", text="Open Preview Window", icon='WINDOW') + preview_row.operator("text_texture.preview", text="Preview Texture", icon='HIDE_OFF') layout.separator(factor=1.2) - # COLLAPSIBLE SECTIONS + # COLLAPSIBLE SECTIONS - FREE FEATURES FIRST + + # === FREE FEATURES === # Multiline Section (collapsed by default) multiline_section = draw_collapsible_header(layout, "expand_multiline", "Multiline Text", 'TEXT') if multiline_section: + pass draw_multiline_section(multiline_section, props) - # Shader Section (collapsed by default) - ONLY OPTIONS, NOT THE BUTTON - shader_section = draw_collapsible_header(layout, "expand_shader", "Shader Options", 'SETTINGS') - if shader_section: - draw_shader_options_only(shader_section, props) - - # Image Overlays Section (collapsed by default) - overlay_section = draw_collapsible_header(layout, "expand_image_overlays", "Image Overlays", 'IMAGE_DATA') - if overlay_section: - draw_image_overlays_section(overlay_section, props) - - # Positioning & Layout Section (collapsed by default) - position_section = draw_collapsible_header(layout, "expand_positioning", "Positioning & Layout", 'ANCHOR_CENTER') - if position_section: - draw_positioning_section(position_section, props) - # Font Settings Section (collapsed by default) font_section = draw_collapsible_header(layout, "expand_font_settings", "Font Settings", 'FONT_DATA') if font_section: + pass draw_font_settings_section(font_section, props) + # Positioning & Layout Section (collapsed by default) - FREE FEATURE moved to free group + position_section = draw_collapsible_header(layout, "expand_positioning", "Positioning & Layout", 'ANCHOR_CENTER') + if position_section: + draw_positioning_section(position_section, props) + # Text Fitting Section (collapsed by default) text_fitting_section = draw_collapsible_header(layout, "expand_text_fitting", "Text Fitting", 'FULLSCREEN_ENTER') if text_fitting_section: + pass draw_text_fitting_section(text_fitting_section, props) - # Stroke Section (collapsed by default) - stroke_section = draw_collapsible_header(layout, "expand_stroke", "Stroke & Outlines", 'MOD_OUTLINE') - if stroke_section: - draw_stroke_section(stroke_section, props) - - # Shadow & Glow Section (collapsed by default) - shadow_glow_section = draw_collapsible_header(layout, "expand_shadow_glow", "Shadow & Glow Effects", 'SHADERFX') - if shadow_glow_section: - draw_shadow_glow_section(shadow_glow_section, props) - - # Tiling Section removed - feature was removed from addon - - # Blur Section (collapsed by default) - blur_section = draw_collapsible_header(layout, "expand_blur", "Blur Effects", 'MOD_SMOOTH') - if blur_section: - draw_blur_section(blur_section, props) - - # Batch Processing Section removed - feature was removed from addon - - # Normal Maps Section (collapsed by default) - normal_maps_section = draw_collapsible_header(layout, "expand_normal_maps", "Normal Maps", 'TEXTURE') - if normal_maps_section: - draw_normal_maps_section(normal_maps_section, props) - # Colors Section (collapsed by default) colors_section = draw_collapsible_header(layout, "expand_colors", "Colors", 'COLOR') if colors_section: + pass draw_colors_section(colors_section, props) - # Presets Section (collapsed by default) - presets_section = draw_collapsible_header(layout, "expand_presets", "Presets", 'PRESET') - if presets_section: - draw_presets_section(presets_section, props) + # === PREMIUM FEATURES === + + # Shader Section (collapsed by default) - ONLY OPTIONS, NOT THE BUTTON + shader_header_row = layout.row(align=True) + + # Gray out header if locked and prevent expansion + from ..utils.constants import has_shader_generation + if not has_shader_generation(): + shader_header_row.enabled = False + # Don't allow expansion for locked features + shader_expanded = None + # Show lock indicator instead of expandable header + lock_header = shader_header_row.row() + lock_header.label(text="Shader Options", icon='SETTINGS') + lock_header.label(text="🔒 PRO", icon='LOCKED') + else: + shader_expanded = draw_collapsible_header(shader_header_row, "expand_shader", "Shader Options", 'SETTINGS') + + # Only show content if available and expanded + if shader_expanded: + draw_shader_options_only(shader_expanded, props) + + # Image Overlays Section (collapsed by default) - Show with premium indicator + overlay_header_row = layout.row(align=True) + + # Gray out header if locked and prevent expansion + from ..utils.constants import has_image_overlays + if not has_image_overlays(): + overlay_header_row.enabled = False + # Don't allow expansion for locked features + overlay_expanded = None + # Show lock indicator instead of expandable header + lock_header = overlay_header_row.row() + lock_header.label(text="Image Overlays", icon='IMAGE_DATA') + lock_header.label(text="🔒 PRO", icon='LOCKED') + else: + overlay_expanded = draw_collapsible_header(overlay_header_row, "expand_image_overlays", "Image Overlays", 'IMAGE_DATA') + + # Only show content if available and expanded + if overlay_expanded: + draw_image_overlays_section(overlay_expanded, props) + + + # Stroke Section (collapsed by default) - Show with premium indicator + stroke_header_row = layout.row(align=True) + + # Gray out header if locked and prevent expansion + from ..utils.constants import has_stroke_effects + if not has_stroke_effects(): + stroke_header_row.enabled = False + # Don't allow expansion for locked features + stroke_expanded = None + # Show lock indicator instead of expandable header + lock_header = stroke_header_row.row() + lock_header.label(text="Stroke & Outlines", icon='MOD_OUTLINE') + lock_header.label(text="🔒 PRO", icon='LOCKED') + else: + stroke_expanded = draw_collapsible_header(stroke_header_row, "expand_stroke", "Stroke & Outlines", 'MOD_OUTLINE') + + # Only show content if available and expanded + if stroke_expanded: + draw_stroke_section(stroke_expanded, props) + + # Shadow & Glow Section (collapsed by default) - Show with premium indicator + shadow_glow_header_row = layout.row(align=True) + + # Gray out header if locked and prevent expansion + from ..utils.constants import has_shadow_glow_effects + if not has_shadow_glow_effects(): + shadow_glow_header_row.enabled = False + # Don't allow expansion for locked features + shadow_glow_expanded = None + # Show lock indicator instead of expandable header + lock_header = shadow_glow_header_row.row() + lock_header.label(text="Shadow & Glow Effects", icon='SHADERFX') + lock_header.label(text="🔒 PRO", icon='LOCKED') + else: + shadow_glow_expanded = draw_collapsible_header(shadow_glow_header_row, "expand_shadow_glow", "Shadow & Glow Effects", 'SHADERFX') + + # Only show content if available and expanded + if shadow_glow_expanded: + draw_shadow_glow_section(shadow_glow_expanded, props) + + # Blur Section (collapsed by default) - Show with premium indicator + blur_header_row = layout.row(align=True) + + # Gray out header if locked and prevent expansion + from ..utils.constants import has_blur_effects + if not has_blur_effects(): + blur_header_row.enabled = False + # Don't allow expansion for locked features + blur_expanded = None + # Show lock indicator instead of expandable header + lock_header = blur_header_row.row() + lock_header.label(text="Blur Effects", icon='MOD_SMOOTH') + lock_header.label(text="🔒 PRO", icon='LOCKED') + else: + blur_expanded = draw_collapsible_header(blur_header_row, "expand_blur", "Blur Effects", 'MOD_SMOOTH') + + # Only show content if available and expanded + if blur_expanded: + draw_blur_section(blur_expanded, props) + + # Normal Maps Section (collapsed by default) - Show with premium indicator + normal_header_row = layout.row(align=True) + + # Gray out header if locked and prevent expansion + if not has_normal_maps(): + normal_header_row.enabled = False + # Don't allow expansion for locked features + normal_expanded = None + # Show lock indicator instead of expandable header + lock_header = normal_header_row.row() + lock_header.label(text="Normal Maps", icon='TEXTURE') + lock_header.label(text="🔒 PRO", icon='LOCKED') + else: + normal_expanded = draw_collapsible_header(normal_header_row, "expand_normal_maps", "Normal Maps", 'TEXTURE') + + # Only show content if available and expanded + if normal_expanded: + draw_normal_maps_section(normal_expanded, props) + + # Presets Section (collapsed by default) - Show with premium indicator + presets_header_row = layout.row(align=True) + + # Gray out header if locked and prevent expansion + if not has_presets(): + presets_header_row.enabled = False + # Don't allow expansion for locked features + presets_expanded = None + # Show lock indicator instead of expandable header + lock_header = presets_header_row.row() + lock_header.label(text="Presets", icon='PRESET') + lock_header.label(text="🔒 PRO", icon='LOCKED') + else: + presets_expanded = draw_collapsible_header(presets_header_row, "expand_presets", "Presets", 'PRESET') + + # Only show content if available and expanded + if presets_expanded: + draw_presets_section(presets_expanded, props) class TEXT_TEXTURE_PT_panel_properties(Panel): + pass """Properties Panel""" bl_label = "Text Texture Generator" bl_idname = "TEXT_TEXTURE_PT_panel_properties" @@ -833,6 +1383,7 @@ class TEXT_TEXTURE_PT_panel_properties(Panel): bl_category = "Tool" def draw(self, context): + pass layout = self.layout props = context.scene.text_texture_props @@ -848,71 +1399,179 @@ class TEXT_TEXTURE_PT_panel_properties(Panel): layout.separator(factor=0.5) draw_generate_button(layout) - # Preview Button at top level + # CREATE SHADER BUTTON - WITH PROPER RESTRICTION + layout.separator(factor=0.5) + create_shader_row = layout.row() + create_shader_row.scale_y = 1.5 + + # Check if shader generation is available + from ..utils.constants import has_shader_generation + if has_shader_generation(): + create_shader_row.operator("text_texture.generate_shader", text="Create Shader", icon='MATERIAL') + else: + # Show locked shader button for free version + lock_row = create_shader_row.row() + lock_row.enabled = False + lock_row.operator("text_texture.generate_shader", text="🔒 Create Shader (Pro Only)", icon='LOCKED') + + # Preview Button - ALWAYS available (core functionality for all users) layout.separator(factor=0.5) preview_row = layout.row() preview_row.scale_y = 1.5 - preview_row.operator("text_texture.view_preview_zoom", text="Open Preview Window", icon='WINDOW') + preview_row.operator("text_texture.preview", text="Preview Texture", icon='HIDE_OFF') layout.separator(factor=1.2) - # COLLAPSIBLE SECTIONS + # COLLAPSIBLE SECTIONS - FREE FEATURES FIRST + + # === FREE FEATURES === # Multiline Section (collapsed by default) multiline_section = draw_collapsible_header(layout, "expand_multiline", "Multiline Text", 'TEXT') if multiline_section: + pass draw_multiline_section(multiline_section, props) - # Image Overlays Section (collapsed by default) - overlay_section = draw_collapsible_header(layout, "expand_image_overlays", "Image Overlays", 'IMAGE_DATA') - if overlay_section: - draw_image_overlays_section(overlay_section, props) - - # Positioning & Layout Section (collapsed by default) - position_section = draw_collapsible_header(layout, "expand_positioning", "Positioning & Layout", 'ANCHOR_CENTER') - if position_section: - draw_positioning_section(position_section, props) - # Font Settings Section (collapsed by default) font_section = draw_collapsible_header(layout, "expand_font_settings", "Font Settings", 'FONT_DATA') if font_section: + pass draw_font_settings_section(font_section, props) + # Positioning & Layout Section (collapsed by default) - FREE FEATURE moved to free group + position_section = draw_collapsible_header(layout, "expand_positioning", "Positioning & Layout", 'ANCHOR_CENTER') + if position_section: + draw_positioning_section(position_section, props) + # Text Fitting Section (collapsed by default) text_fitting_section = draw_collapsible_header(layout, "expand_text_fitting", "Text Fitting", 'FULLSCREEN_ENTER') if text_fitting_section: + pass draw_text_fitting_section(text_fitting_section, props) - # Stroke Section (collapsed by default) - stroke_section = draw_collapsible_header(layout, "expand_stroke", "Stroke & Outlines", 'MOD_OUTLINE') - if stroke_section: - draw_stroke_section(stroke_section, props) - - # Shadow & Glow Section (collapsed by default) - shadow_glow_section = draw_collapsible_header(layout, "expand_shadow_glow", "Shadow & Glow Effects", 'SHADERFX') - if shadow_glow_section: - draw_shadow_glow_section(shadow_glow_section, props) - - # Tiling Section removed - feature was removed from addon - - # Blur Section (collapsed by default) - blur_section = draw_collapsible_header(layout, "expand_blur", "Blur Effects", 'MOD_SMOOTH') - if blur_section: - draw_blur_section(blur_section, props) - - # Batch Processing Section removed - feature was removed from addon - - # Normal Maps Section (collapsed by default) - normal_maps_section = draw_collapsible_header(layout, "expand_normal_maps", "Normal Maps", 'TEXTURE') - if normal_maps_section: - draw_normal_maps_section(normal_maps_section, props) - # Colors Section (collapsed by default) colors_section = draw_collapsible_header(layout, "expand_colors", "Colors", 'COLOR') if colors_section: + pass draw_colors_section(colors_section, props) - # Presets Section (collapsed by default) - presets_section = draw_collapsible_header(layout, "expand_presets", "Presets", 'PRESET') - if presets_section: - draw_presets_section(presets_section, props) \ No newline at end of file + # === PREMIUM FEATURES === + + # Image Overlays Section (collapsed by default) - Show with premium indicator + overlay_header_row = layout.row(align=True) + + # Gray out header if locked and prevent expansion + from ..utils.constants import has_image_overlays + if not has_image_overlays(): + overlay_header_row.enabled = False + # Don't allow expansion for locked features + overlay_expanded = None + # Show lock indicator instead of expandable header + lock_header = overlay_header_row.row() + lock_header.label(text="Image Overlays", icon='IMAGE_DATA') + lock_header.label(text="🔒 PRO", icon='LOCKED') + else: + overlay_expanded = draw_collapsible_header(overlay_header_row, "expand_image_overlays", "Image Overlays", 'IMAGE_DATA') + + # Only show content if available and expanded + if overlay_expanded: + draw_image_overlays_section(overlay_expanded, props) + + + # Stroke Section (collapsed by default) - Show with premium indicator + stroke_header_row = layout.row(align=True) + + # Gray out header if locked and prevent expansion + from ..utils.constants import has_stroke_effects + if not has_stroke_effects(): + stroke_header_row.enabled = False + # Don't allow expansion for locked features + stroke_expanded = None + # Show lock indicator instead of expandable header + lock_header = stroke_header_row.row() + lock_header.label(text="Stroke & Outlines", icon='MOD_OUTLINE') + lock_header.label(text="🔒 PRO", icon='LOCKED') + else: + stroke_expanded = draw_collapsible_header(stroke_header_row, "expand_stroke", "Stroke & Outlines", 'MOD_OUTLINE') + + # Only show content if available and expanded + if stroke_expanded: + draw_stroke_section(stroke_expanded, props) + + # Shadow & Glow Section (collapsed by default) - Show with premium indicator + shadow_glow_header_row = layout.row(align=True) + + # Gray out header if locked and prevent expansion + from ..utils.constants import has_shadow_glow_effects + if not has_shadow_glow_effects(): + shadow_glow_header_row.enabled = False + # Don't allow expansion for locked features + shadow_glow_expanded = None + # Show lock indicator instead of expandable header + lock_header = shadow_glow_header_row.row() + lock_header.label(text="Shadow & Glow Effects", icon='SHADERFX') + lock_header.label(text="🔒 PRO", icon='LOCKED') + else: + shadow_glow_expanded = draw_collapsible_header(shadow_glow_header_row, "expand_shadow_glow", "Shadow & Glow Effects", 'SHADERFX') + + # Only show content if available and expanded + if shadow_glow_expanded: + draw_shadow_glow_section(shadow_glow_expanded, props) + + # Blur Section (collapsed by default) - Show with premium indicator + blur_header_row = layout.row(align=True) + + # Gray out header if locked and prevent expansion + from ..utils.constants import has_blur_effects + if not has_blur_effects(): + blur_header_row.enabled = False + # Don't allow expansion for locked features + blur_expanded = None + # Show lock indicator instead of expandable header + lock_header = blur_header_row.row() + lock_header.label(text="Blur Effects", icon='MOD_SMOOTH') + lock_header.label(text="🔒 PRO", icon='LOCKED') + else: + blur_expanded = draw_collapsible_header(blur_header_row, "expand_blur", "Blur Effects", 'MOD_SMOOTH') + + # Only show content if available and expanded + if blur_expanded: + draw_blur_section(blur_expanded, props) + + # Normal Maps Section (collapsed by default) - Show with premium indicator + normal_header_row = layout.row(align=True) + + # Gray out header if locked and prevent expansion + if not has_normal_maps(): + normal_header_row.enabled = False + # Don't allow expansion for locked features + normal_expanded = None + # Show lock indicator instead of expandable header + lock_header = normal_header_row.row() + lock_header.label(text="Normal Maps", icon='TEXTURE') + lock_header.label(text="🔒 PRO", icon='LOCKED') + else: + normal_expanded = draw_collapsible_header(normal_header_row, "expand_normal_maps", "Normal Maps", 'TEXTURE') + + # Only show content if available and expanded + if normal_expanded: + draw_normal_maps_section(normal_expanded, props) + + # Presets Section (collapsed by default) - Show with premium indicator + presets_header_row = layout.row(align=True) + + # Gray out header if locked and prevent expansion + if not has_presets(): + presets_header_row.enabled = False + # Don't allow expansion for locked features + presets_expanded = None + # Show lock indicator instead of expandable header + lock_header = presets_header_row.row() + lock_header.label(text="Presets", icon='PRESET') + lock_header.label(text="🔒 PRO", icon='LOCKED') + else: + presets_expanded = draw_collapsible_header(presets_header_row, "expand_presets", "Presets", 'PRESET') + + # Only show content if available and expanded + if presets_expanded: + draw_presets_section(presets_expanded, props) \ No newline at end of file diff --git a/src/ui/preview.py b/src/ui/preview.py index 8bb0f36..697869e 100644 --- a/src/ui/preview.py +++ b/src/ui/preview.py @@ -2,14 +2,43 @@ import bpy import os import platform -from PIL import Image, ImageDraw, ImageFont + +# Global flag to track PIL availability +PIL_AVAILABLE = False +Image = None +ImageDraw = None +ImageFont = None + +try: + pass + from PIL import Image, ImageDraw, ImageFont + PIL_AVAILABLE = True +except ImportError: + pass + PIL_AVAILABLE = False + # Create placeholder classes to prevent import errors + class _MockPIL: + pass + def __getattr__(self, name): + pass + raise ImportError( + "PIL (Pillow) is required for preview functionality. " + "Please install it using: pip install Pillow in Blender's Python environment, " + "or use Blender's bundled Python if available." + ) + + Image = _MockPIL() + ImageDraw = _MockPIL() + ImageFont = _MockPIL() def test_font_mixed_case_support(font_path): + pass """ Test if a font properly supports mixed case rendering. Returns True if the font supports mixed case, False if it only renders uppercase. """ try: + pass # Test string with mixed case test_text = "AaBbCc" @@ -20,8 +49,10 @@ def test_font_mixed_case_support(font_path): # Try to load the font try: + pass font = ImageFont.truetype(font_path, 24) except (OSError, IOError): + pass print(f"Text Texture Generator: Could not load font for testing: {font_path}") return False @@ -40,42 +71,54 @@ def test_font_mixed_case_support(font_path): # Compare the two renderings - if they're identical, the font only supports uppercase if pixels == pixels_upper: + pass print(f"Text Texture Generator: Font only supports uppercase rendering: {os.path.basename(font_path)}") return False else: + pass print(f"Text Texture Generator: Font supports mixed case rendering: {os.path.basename(font_path)}") return True except Exception as e: + pass print(f"Text Texture Generator: Error testing font mixed case support: {str(e)}") return False def get_validated_font(font_path, fallback_fonts=None): + pass """ Get a validated font that supports mixed case rendering. Returns the original font path if valid, otherwise returns a fallback font. """ if not font_path or not os.path.exists(font_path): + pass print(f"Text Texture Generator: Font file not found: {font_path}") return get_system_fallback_fonts()[0] if get_system_fallback_fonts() else None # Test if the font supports mixed case if test_font_mixed_case_support(font_path): + pass return font_path else: + pass print(f"Text Texture Generator: Font does not support mixed case, using fallback: {font_path}") # Try fallback fonts if provided if fallback_fonts: + pass for fallback_font in fallback_fonts: + pass if os.path.exists(fallback_font) and test_font_mixed_case_support(fallback_font): + pass print(f"Text Texture Generator: Using fallback font: {fallback_font}") return fallback_font # Use system fallback fonts system_fallbacks = get_system_fallback_fonts() for fallback_font in system_fallbacks: + pass if test_font_mixed_case_support(fallback_font): + pass print(f"Text Texture Generator: Using system fallback font: {fallback_font}") return fallback_font @@ -84,6 +127,7 @@ def get_validated_font(font_path, fallback_fonts=None): return font_path def get_system_fallback_fonts(): + pass """ Get a list of system fallback fonts based on the operating system. Returns a list of font paths that are likely to be available. @@ -92,6 +136,7 @@ def get_system_fallback_fonts(): fallback_fonts = [] if system == "Windows": + pass windows_fonts = [ "C:/Windows/Fonts/arial.ttf", "C:/Windows/Fonts/calibri.ttf", @@ -133,17 +178,25 @@ def get_system_fallback_fonts(): ] for font_dir in common_directories: + pass if os.path.exists(font_dir): + pass for root, dirs, files in os.walk(font_dir): + pass for file in files: + pass if file.lower().endswith(('.ttf', '.otf')): + pass font_path = os.path.join(root, file) if font_path not in fallback_fonts: + pass fallback_fonts.append(font_path) # Limit the number of fallback fonts to prevent excessive scanning if len(fallback_fonts) >= 20: + pass break if len(fallback_fonts) >= 20: + pass break print(f"Text Texture Generator: Found {len(fallback_fonts)} system fallback fonts") diff --git a/src/utils/__pycache__/__init__.cpython-311.pyc b/src/utils/__pycache__/__init__.cpython-311.pyc index 0fc381522bfc69be2b7366e7088f80266ff9367e..35c05c14006767ac0f82fa80496582167d83d6b3 100644 GIT binary patch delta 20 acmey${FRw|IWI340}#YtIJ%Mh6(ayaItF3@ delta 20 acmey${FRw|IWI340}vSV9ofkJiV*-ibp>+( diff --git a/src/utils/__pycache__/constants.cpython-311.pyc b/src/utils/__pycache__/constants.cpython-311.pyc index e9653ea79f8c26400d60bd468d948abf53a4cc51..0412da5502e4a68b18f1868c3d30876c9f6f0f0f 100644 GIT binary patch literal 6975 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 literal 2625 zcmb_eO>7fK6rQ!c_QwB&*8G%&3FOZb*iE24q#-4R0If)h>HtbEtIf{XUa)tq*>!#_ zr^*3U9Jvvxv_~q$rI$+d)=~~VI*||7N=Qf*2h@utR0(nFoAFPAMQDlM>8E8YbL=(>S#oBG@P{j9k*u2JcJa3o|E#T=QOwJd@Bvqs{N$)EOpugf-FfRyD z_&Q^usLY+Yh;JBl4bR|=LFkIc@c2R!W{Z|pz>C@nuHsByBb4DeVsJ!zhI>YpOdB!9 zMp4UA>_9ef$%DvB9iGn_oubiE9TQA~^?HEjbp5p)(|Oo-ePWGl`{Ua8(1k&6TrDj!O}H{t3rg@Ix|XQqJWa-EVkkLvgJZS z=S*}OCxrLcVGyvrVrw6GV7q*kW0$Fe+d)p-C8`@)LkDz4!?9BaiUXgV$fU=O7qxY@ zovvyd73v=08r?D>Q$|fkwMu+1d8&^PJHP`%M{lpBuiKDCn(tX_%hK1Fb&Kkbo#y^d zx1FBObq+(?W_r5p7zH~G?puc`RoZa-bJS7WVAU2z_5p-A2;DvcVhc6>Xke%@GTM~A zVSf{~gzRViu=)-u9#9BU4#VI+azJ((k#6%<1O$01Cobfvej6KEIJ}09;o*lk%P8&8 zmIBIQ`bcuaH|D;{eEIc)y0~<0PB0+iGLFxU8MeypItefi_$c((5fEGGW$4iMr@Nt{ zns4ZTM++ySi<|}xk9rY;X&2!!SRdpz9HuaIy&4=Y!49Z+lc5V9e}o%&A61d< zAyNkQA&1|{cB>*@O&)^XV{R+jrl%?qUyErr>2&o7d=U8|#CY6|!va-L6{+2MkF~$Y zx=&blJ#sv}L%cvc-lZN7z43bS^hOx2qFY@wFz&{xKyE~-gb>>FR=xW%i&@vMgQ<G^$8{%>)2YhfaTu|IO`ii^JBvN6R&E8G88aPXO?Y4P2$(sF{mBH(1e&}IeSWsxQbiY zQyA`W+YnnT{5E5_tBz)vhM9ZUb@$CyK-JkO(J8zL5?k4hsSUvJ_!u0ou~T>qYM)Wz z?!*kPykqDvP6}5G?m=EM)U4q+5KL{k&9$f*KF(G z(p5S^Dq}Y_#*>)Bmo%e533eN=<;X;1@ML{(^5^UIPp<3?Uiou&XJGzburVgQ z4>v|X-XA3Z_Tr^|BFN)?wHq0&MMhtQ<99DMWfUEQ$(w%^6#kGCaBfdLS0c6OXHO&$ z@a!nlb!EEtzUK`z$Vxs3zxD{Q`rWMj#R&R6HtUC(vBUg{-)tPrH{wT|F(u?_A`n}FW&*_%Tj8c(mf!=}6;Y3L Vg1cx7r8^QjWk>?P{zHh$|1bFXx(xsT diff --git a/src/utils/__pycache__/feature_flags.cpython-311.pyc b/src/utils/__pycache__/feature_flags.cpython-311.pyc deleted file mode 100644 index 227a232af46c41872fd276b5b123f436eec78d42..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9490 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 delta 1295 zcmZ8gZ)hAv6rb7M*}J{H|Lxr+O=>12m-PHY6C+8HRvJt%jbbR4en_d{n%Uef-P^0T zn_hD4)ugB#5@=+RMC<+hpIRito6-UwxgLQJ!hC^xk`-%YLy+T zUDXUzd(YG>-tT7lRs84i%P?C(O-Ujb@m?_52z8#;Lp9w*0xTayep@)vL%h{it7XR^ z`MMNDjgyl>@#g@Z4D!E)OqVGhtg(da2z&L4d_WvkrOSS;l_5sBCccDaenU)U0$<=d zvEFw@7JT9#W(o^=5&i1Z^%lgsR6s23XH2mOi+WLkSUZY8mijoA(gX2lvLrvt|Bow8 zp?4nDWys6$Z>1sSCzrs=kM#h5Bu&4Na06RV!8W|@!IIw`+5*{2eUv zv3QuJ_`f78rs3Ns_hc7A(<7aHgg{f#(`jwBqte>lwcoiWr`{zP8E9UZL+4S)m&??~ zr*(%i-2uULMoq(ZXnEm(0ja%gJ9>#Olr6`mRl_OLE1FZZePMI>S7oN874$rKXTRT+ zGeSHGTxWyokT-zw_#)conhMOoD$?o-l zKagm{&DZ%q%CVT70ux|EX$qt}%SS`(1KbU39oXv~+&J45!Gtbpsu0?0#!)=g2)DZm zlOzN`fxTFwp?D7@kfhgE6F~O*R8M{o-Ft3wG#{6DVi=5_cqo5V**PM3^Iq)DkILSB zFq{vJi90Xj{21?7Gvrmc8|!>h9l>4PQ9qMKCVeDy^T%rDFbFq%367yI`AF)rEAU-0 zeZiGo!iY<5qdS5tF}VgD7NnE;74%yqe?L6Ea^?>;`OBf9ucEu^!~=E0JCN7-t;ne~ zCU3x<^KduV-@v`Ch39=*7@mZ_e^d2ZQWU{SX859OJlU8ITd5c?-6Uh z2t=-w4a=bh(=EqXF!V~B-h~-(*oMc=unM$LF&MuX{W?DDvCH}Fnle&GcvtL)w`A", "version": (1, 0, 0), "blender": (4, 0, 0), "location": "3D View > Sidebar (N) > Tool Tab | Shader Editor > Sidebar > Tool Tab", - "description": "Generate image textures from text with accurate dimensions and instant live preview", + "description": "Generate image textures from text with accurate dimensions and instant live preview" + (" - Free Version (Limited Features)" if VERSION_TYPE == "free" else " - Full Version"), "category": "Material", } -# Version information -VERSION_TYPE = "full" # "free" or "full" +# Feature configuration based on version type +ENABLED_FEATURES = ["basic", "preview"] if VERSION_TYPE == "free" else [ + "basic", "preview", "presets", "normal_maps", "batch_processing", + "image_overlays", "basic_utilities", "advanced_utilities", "advanced_styling", + "stroke_effects", "blur_effects", "shadow_glow_effects", "shader_generation" +] # Version-specific limits (applied at build time through file exclusions) def get_version_limits(): """Get version-specific limits.""" - return { - "max_texture_size": 4096, - "max_concurrent_operations": 4 - } + if VERSION_TYPE == "free": + return { + "max_texture_size": 1024, + "max_concurrent_operations": 1 + } + else: + return { + "max_texture_size": 4096, + "max_concurrent_operations": 4 + } def is_free_version(): """Check if this is the free version.""" @@ -31,37 +44,116 @@ def is_full_version(): """Check if this is the full version.""" return VERSION_TYPE == "full" -def sync_margin_values(props, margin_type='all'): - """Synchronize margin values across properties +# UI Helper Functions for Version Display +def get_version_badge_text(): + """Get version badge text for UI display.""" + return "FREE" if is_free_version() else "PRO" + +def get_version_badge_icon(): + """Get appropriate icon for version badge.""" + return 'GIFT' if is_free_version() else 'STAR' + +def get_upgrade_hint_text(feature_name): + """Get contextual upgrade hint text for specific features.""" + if not is_free_version(): + return "" + + return f"Upgrade to Pro version to access {feature_name}" + +# Feature availability functions +def has_feature(feature_name): + """Check if a specific feature is available.""" + return feature_name in ENABLED_FEATURES + +def has_image_overlays(): + """Check if image overlay features are available""" + return has_feature("image_overlays") + +def has_basic_utilities(): + """Check if basic utility operations are available""" + return has_feature("basic_utilities") + +def has_presets(): + """Check if preset features are available""" + return has_feature("presets") + +def has_normal_maps(): + """Check if normal map generation is available""" + return has_feature("normal_maps") + +def has_batch_processing(): + """Check if batch processing is available""" + return has_feature("batch_processing") + +def has_advanced_utilities(): + """Check if advanced utility operations are available""" + return has_feature("advanced_utilities") + +def has_advanced_styling(): + """Check if advanced styling options are available""" + return has_feature("advanced_styling") + +def has_stroke_effects(): + """Check if stroke effects are available""" + return has_feature("stroke_effects") + +def has_blur_effects(): + """Check if blur effects are available""" + return has_feature("blur_effects") + +def has_shadow_glow_effects(): + """Check if shadow and glow effects are available""" + return has_feature("shadow_glow_effects") + +def has_shader_generation(): + """Check if shader generation is available""" + return has_feature("shader_generation") + +def get_max_resolution(): + """Get maximum texture resolution based on version""" + limits = get_version_limits() + return limits.get("max_texture_size", 1024) + +def should_show_feature_lock(feature_name): + """Check if feature lock indicator should be shown.""" + # Map feature names to their availability functions + feature_checks = { + 'presets': has_presets, + 'normal_maps': has_normal_maps, + 'batch_processing': has_batch_processing, + 'advanced_utilities': has_advanced_utilities, + 'advanced_styling': has_advanced_styling, + 'image_overlays': has_image_overlays, + 'basic_utilities': has_basic_utilities, + 'stroke_effects': has_stroke_effects, + 'blur_effects': has_blur_effects, + 'shadow_glow_effects': has_shadow_glow_effects, + 'shader_generation': has_shader_generation + } - Args: - props: Properties object containing margin values - margin_type: Type of margin sync ('all', 'horizontal', 'vertical') - """ - if not props: - return - - try: - # Get base margin value - base_margin = getattr(props, 'text_fitting_margin', 10.0) - - if margin_type == 'all': - # Sync all margins to base margin - if hasattr(props, 'prepend_margin'): - props.prepend_margin = base_margin - if hasattr(props, 'append_margin'): - props.append_margin = base_margin - - elif margin_type == 'horizontal': - # Sync horizontal margins - if hasattr(props, 'prepend_margin') and hasattr(props, 'append_margin'): - avg_margin = (props.prepend_margin + props.append_margin) / 2 - props.prepend_margin = avg_margin - props.append_margin = avg_margin - - elif margin_type == 'vertical': - # Sync vertical margins (if any exist in future) - pass - - except Exception as e: - print(f"Warning: Failed to sync margin values: {e}") \ No newline at end of file + if is_full_version(): + return False + + # For free version, show lock if feature is not available + if feature_name in feature_checks: + return not feature_checks[feature_name]() + + return True # Show lock for unknown features + +def get_feature_availability_text(): + """Get text describing current feature availability.""" + if is_free_version(): + return "Free version - Basic features available" + return "Pro version - All features available" + +def get_version_info_details(): + """Get detailed version information for display.""" + limits = get_version_limits() + info = { + 'version_type': VERSION_TYPE.upper(), + 'max_texture_size': limits.get('max_texture_size', 'Unlimited'), + 'max_concurrent_operations': limits.get('max_concurrent_operations', 'Unlimited'), + 'features_enabled': len(ENABLED_FEATURES), + 'upgrade_available': is_free_version() + } + return info \ No newline at end of file diff --git a/src/utils/feature_flags.py.backup b/src/utils/feature_flags.py.backup deleted file mode 100644 index 7f1db9c..0000000 --- a/src/utils/feature_flags.py.backup +++ /dev/null @@ -1,150 +0,0 @@ -""" -Feature flag system for Text Texture Generator. -Provides runtime checking of enabled features based on build configuration. -""" - -import json -import os -from pathlib import Path -from typing import List, Dict, Any, Optional - -# Import the build-time injected constants -try: - from .constants import ENABLED_FEATURES, VERSION_TYPE, get_version_limits -except ImportError: - # Fallback for development/testing - ENABLED_FEATURES = ["basic", "preview", "advanced_styling", "normal_maps", "presets"] - VERSION_TYPE = "full" - def get_version_limits(): - return {"max_texture_size": 4096, "max_concurrent_operations": 4} - -class FeatureManager: - """Manages feature flags and version-specific limitations.""" - - def __init__(self): - self._enabled_features = set(ENABLED_FEATURES) - self._version_type = VERSION_TYPE - self._limits = get_version_limits() - self._features_config = self._load_features_config() - - def _load_features_config(self) -> Optional[Dict]: - """Load the features configuration file if available.""" - try: - # Try to load from build directory (development) - config_path = Path(__file__).parent.parent.parent / "build" / "features.json" - if config_path.exists(): - with open(config_path, 'r') as f: - return json.load(f) - except Exception: - pass - return None - - def is_feature_enabled(self, feature_name: str) -> bool: - """Check if a feature is enabled in the current build.""" - return feature_name in self._enabled_features - - def get_enabled_features(self) -> List[str]: - """Get list of all enabled features.""" - return list(self._enabled_features) - - def get_version_type(self) -> str: - """Get the version type (free or full).""" - return self._version_type - - def get_limit(self, limit_name: str, default: Any = None) -> Any: - """Get a version-specific limit.""" - return self._limits.get(limit_name, default) - - def get_max_texture_size(self) -> int: - """Get the maximum texture size for this version.""" - return self.get_limit("max_texture_size", 1024) - - def get_max_concurrent_operations(self) -> int: - """Get the maximum concurrent operations for this version.""" - return self.get_limit("max_concurrent_operations", 1) - - def require_feature(self, feature_name: str) -> bool: - """Check if feature is enabled, raise exception if not.""" - if not self.is_feature_enabled(feature_name): - raise FeatureNotAvailableError( - f"Feature '{feature_name}' is not available in {self._version_type} version" - ) - return True - - def get_feature_info(self, feature_name: str) -> Optional[Dict]: - """Get detailed information about a feature.""" - if not self._features_config: - return None - - return self._features_config.get("features", {}).get(feature_name) - - def is_component_enabled(self, component_path: str) -> bool: - """Check if a specific component is enabled based on feature flags.""" - if not self._features_config: - return True # Assume enabled if no config - - features = self._features_config.get("features", {}) - for feature_name, feature_config in features.items(): - if self.is_feature_enabled(feature_name): - components = feature_config.get("components", []) - if component_path in components: - return True - - return False - -class FeatureNotAvailableError(Exception): - """Raised when trying to use a feature that's not available in the current version.""" - pass - -# Global feature manager instance -feature_manager = FeatureManager() - -# Convenience functions -def is_feature_enabled(feature_name: str) -> bool: - """Check if a feature is enabled.""" - return feature_manager.is_feature_enabled(feature_name) - -def require_feature(feature_name: str) -> bool: - """Require a feature to be enabled.""" - return feature_manager.require_feature(feature_name) - -def get_version_type() -> str: - """Get the current version type.""" - return feature_manager.get_version_type() - -def get_max_texture_size() -> int: - """Get maximum texture size for this version.""" - return feature_manager.get_max_texture_size() - -def get_max_concurrent_operations() -> int: - """Get maximum concurrent operations for this version.""" - return feature_manager.get_max_concurrent_operations() - -# Decorators for feature-gated functionality -def feature_required(feature_name: str): - """Decorator to require a feature for a function or method.""" - def decorator(func): - def wrapper(*args, **kwargs): - require_feature(feature_name) - return func(*args, **kwargs) - wrapper.__name__ = func.__name__ - wrapper.__doc__ = func.__doc__ - return wrapper - return decorator - -def feature_gated(feature_name: str, fallback=None): - """Decorator to conditionally execute a function based on feature availability.""" - def decorator(func): - def wrapper(*args, **kwargs): - if is_feature_enabled(feature_name): - return func(*args, **kwargs) - elif fallback is not None: - return fallback(*args, **kwargs) if callable(fallback) else fallback - else: - raise FeatureNotAvailableError( - f"Feature '{feature_name}' is not available in {get_version_type()} version" - ) - wrapper.__name__ = func.__name__ - wrapper.__doc__ = func.__doc__ - return wrapper - return decorator \ No newline at end of file diff --git a/src/utils/system.py b/src/utils/system.py index c7bebf3..cbe1d82 100644 --- a/src/utils/system.py +++ b/src/utils/system.py @@ -8,16 +8,21 @@ import platform import os def install_pillow(): + pass """Install Pillow if not available""" try: + pass import PIL except ImportError: + pass print("Installing Pillow...") subprocess.check_call([sys.executable, "-m", "pip", "install", "pillow>=10.0.0"]) def get_system_fonts(): + pass """Get detailed list of available system fonts with paths""" try: + pass from PIL import ImageFont import glob @@ -25,6 +30,7 @@ def get_system_fonts(): system = platform.system() if system == "Windows": + pass font_dirs = [ "C:/Windows/Fonts/", os.path.expanduser("~/AppData/Local/Microsoft/Windows/Fonts/") @@ -47,35 +53,46 @@ def get_system_fonts(): extensions = ["*.ttf", "*.ttc", "*.otf"] for font_dir in font_dirs: + pass if os.path.exists(font_dir): + pass for ext in extensions: + pass for font_path in glob.glob(os.path.join(font_dir, "**", ext), recursive=True): + pass try: + pass font_name = os.path.splitext(os.path.basename(font_path))[0] font_name = font_name.replace("-", " ").replace("_", " ") fonts[font_name] = font_path except Exception: + pass continue return fonts except ImportError: + pass return {} def get_font_enum_items(self, context): + pass """Dynamic enum items for font selection""" items = [("default", "Default Font", "Use system default font", 0)] if not hasattr(get_font_enum_items, 'cached_fonts'): + pass get_font_enum_items.cached_fonts = get_system_fonts() fonts = get_font_enum_items.cached_fonts for i, (font_name, font_path) in enumerate(sorted(fonts.items())[:50]): + pass items.append((font_path, font_name, f"Font: {font_name}", i + 1)) return items def get_anchor_point_matrix(): + pass """Return a 3x3 matrix of anchor point identifiers for UI positioning grid""" return [ ['TOP_LEFT', 'TOP_CENTER', 'TOP_RIGHT'], diff --git a/tests/__pycache__/pytest_plugins.cpython-311.pyc b/tests/__pycache__/pytest_plugins.cpython-311.pyc deleted file mode 100644 index 88451b2adcf2b93fd4a037ac065f11a846c04839..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3381 zcma)9-Amk96u+5CGGC6hb{wrAV;rqpA5`cK0z_J7zvK%o!Ag@J{A+P4AeOP}`Kn@Mym(Z-p3&+q)sJ?Gqy zoIC!O&887NfA##mMN$a;OF{6&TEfW;2yc*z){x4nF@syxL%1+|s})4G?%z zz)i_A46Wp9>Qwqan&0vSNBAX(TMDxUA>R6)%nNkBE_X_{&usT1j=Nx7%sq99YB4#C za84hrJUXYR8M5b`o)7h*M}*pQhG;M=|B22Jti;c`8^0Tbq-1*Oid9ia!?V_!A z<8A8_moqKL?duqqJ0kSi2z_US{z`@J8kE`p6SHWtD1jEzUI z0%Q3IR%EOY!6q0hMzBf7CL-7rWz{rP{!I1O14mO_T7}EDqd|ej`nJZ(K9-=$ONwEf zum7bp?*UvS2+{L>LQB&dR72pETmAzY!fjy6N*@=dpJUCz(`Cgem37nAjOnM2^#Y1M zo_4iY?j&vC%9z~LOwCbT%KyPBaw$%6b}xz!{Es^Tik6Qd3-{UrIs_LC>VQ^nK_l7&fv%H4V7SQVRy&leVT z>=I$61k)s$>SpF)}=GwBHIO-t2C=fcvxM{KxWRyF~z zxQ=da1`$bS!E{4#?V({Q?kcRe<%G&CQ--kPzGWGwXwIyZL)87izoK=^40TtYT68B* z{dGFpCQJ|ZqGTu-ldg(+PLteHOx1vcKvIy8D%%#UMjBQK({ZLtI$!FhYQ2=piV1Ua zh=AEjiEN6P2yB;#VACBU0^1!Dhn*pb)~*nq?g)wF9ZV9fBQcR8N&l$8o~M0@E^15m zefoOeu`m1H{gE_82OAm0NioENWtixvi0N%91&>2-Sd#NYwx3hyE1>A5i9fo z)Rp^j`=@PhzHznZb$&nZJ^mn~?z}HH`U~FU_k3@ocYyY}#_%ZZ2OE8ZwC@4^>+St* zpBoxESUp^&azEG|Y#%cHU}Nm&+lTKSQvFDyP(0W;+@SJ6LmGQ4z7wfDbT*_k##D`= z5!&}RZcNa=7=|tsDINeC>`VejvK3QzX)T_jr^MASKfoQW&BbQW%37G?}XOxjgeqQqzkPOEUBG z6iQNyONtfJ@{1HgQY%WpL}^i~f_rLSDo{m!ksge*3V5WO3uv$8keIVP?VpQnp{$>Ujj5o7eoNf(M^Y$0~7{Yqz_UHwOT(uJ~J<~ wBtBlRpz;@oO>TZlX-=wL5gX8akmHLbfy4)9Mn=XDOiYX{9~e-GA{L-50NXc6Z~y=R diff --git a/tests/integration/__pycache__/test_text_generation_pipeline.cpython-311-pytest-8.3.5.pyc b/tests/integration/__pycache__/test_text_generation_pipeline.cpython-311-pytest-8.3.5.pyc deleted file mode 100644 index 207278554e04b8878ded05a75bf3866e5465219d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20428 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 X{&PIIGC+N=KD;aNA2!GpJ@fwp^9HBq literal 0 HcmV?d00001 diff --git a/tests/unit/__pycache__/test_text_fitting.cpython-311-pytest-8.3.5.pyc b/tests/unit/__pycache__/test_text_fitting.cpython-311-pytest-8.3.5.pyc deleted file mode 100644 index 3a4f0cee1f524180ee515b5c346a248208a3c74d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 62561 zcmeHw3vgT4nH~V*aRHD7K~fJ&vIV^?LAI&4EIG1bS&knYTZx}Z87ByY3sR6tg1#4$ zO&W9*C%X$f@|4+T*G$H{$RyPdj|b|%gA0jgBPLF=t&cipaLIxQV} z+m5E4_WRF&@3{})lAuICMF{+L9{)M_+;cDf|2yaZ&pChC))w>O^LFRIIMs2?=le^F zm>XXL=X>3!`ucpTUkwcU`vT(smZ4xS;P(xM`oezBiS$K~(=wd&{?cslHPX4t`zG*j*W=R4m-%iqEuf7IZc z0mSjgA7@OoGc?%sB;8ARuzP4#->RY2eXEDo^sO0M+qZV8r>|#dUEjK)^?mF8zJTvR zpBnkTPgR_?^lcD1QRKv2Ik$+MIC5HDIk$?OHsrLsayE*b1adlDIVq8oL{6tGXOqb3 zLQc0UXR{i3!k1n(_U+hH!-Z0+l-Emos=ufq^g=1qUnrFd!vnn|qpAKg!?{wSIIQ=^ zo)k=_Q~6Y`sO1G`%HT|82M3B;p>%3UPZfq!1-&?!rBc1IE7ZX!(k->%qs82_wa`em zlsi?6KAIgUgcBr<-DlQ6*6!ikNrM9}iFsx>ZBc;Mnb}-Xl z94=+_!dSkx+EqYD9Cu&q%$>^Tp5-eS&*rtk>_~03mdCxFHQJLa4)+fhawR=yyhq|c z^R2qtE`BX}U!Nbp82nQDTJpiuA(QoV*bJ$G@B7|F0};KRcYna^)*H!-B>HCy;W%U3fe zf7a-~2>x2|clBFA5>*5E3IEhT?l0MR%6_LX7!Q=Fgz+f{T)8c3U_4lEDF>ew4|Y6M z4(Y!t$;asPotCB5mU5`yuL|z|z?*2nTlnLT8Y)H2Fv`gC&TcyE(|a6s%a^2NzE-Hj z*+R`(f_gxFyh{xG)sPx)vOV36+vDyh&B-TQ;pW4&!;Ts)&KBo=d2j`tTx#Tv;23&o zJgh2ev>Yz@MJ!UXDFR1L@+p)f&YGODm;Gw&1^;*p{z7>E0X5!ZcC2q)QCr8OWu+W_ zRy^PFmU4?e=(vjYPPCO<@J_T7xBH!Nl(70iakj*RE9&G@6K@2>oyXJ;HCc`s{Sa=V zAHvR>oH3&x6zU7yb3pA}>4)*C8iO$F8WX}S^*j_`N-qqJ4CYf(52e&>DVq*mp%>u_ zNgs_~$Sd?K*19M}Z_Wk(wZJ|1T=0+W-nL_8bYEJJy=Vkd_oOJaJFV}a|M#TzbZk6z z52qeX>$R4_{BSJ{b#!L1bRk%40WXm~MD`Imc&$CF>v^ptDmp|(Mn(^v5Es{|85f{$ zkK)=%;{vIpf^&abzYx$ifPo6^chrIlYqugt+)Fy95ir!&5^L<Y?~x1hcH_wWv!^Ua5SU7II%P3)>f-c9bh7@0{PnoS<6gx^neP8@vY#q%##m5q~G zkjjgb*-MFy2=Vu_vT@c7a|&@FG-GBkhm0bNQ(n_$T3v#@ZpkG}P8^hJT+)^xnJO7%vX?dD- zGvJ1b3PN3n2YllZz&52EDl3MHj+Ue2v2s+4Aw6CWm*YlyYq=Hau?fHabvq0^E#VeG zT~vTN)a6#-0CiGr1Jsd77oR#%*X^iVg1>UescZqFByd1|68yRO2*7!8y1;`PHef-U z8c~&Un*j^j%aJKt%guKCeC2j$i~K(H*$*7oviD3YdIq4Y9Ixw{*bVlK3zT^FjEfIF z6LYlI>chD6&eXWkGYPd-Z7U~?p6T%HnGR=pzt3?UbB@GU#+&CHZ5Mn3m?hPAHBn9) z*Xb+~3NSv7n#4Q|Wt3gB7S8Jfm~{Y@5lD^TFN!|`X{~s>L*?)o-m&p6yzAZNu5!2W zu1Crd{dLE+?9VcSXPG4Kx@XyWgU_cZk>Egq)>~yr&dY0YJ8r3f2S*}vM)m7zH zMjy8|(Z_Ahnw&ACk0ayVY6k${>aoqFZyE3vhI5#X&@oexV$hK~n;krp*V7@b6AzDZ z7GXZE8{`W8<^}B1HX(GufL(jjx&gZm0HB4QFQ}zc7Xk#l_7XX8EkfYzE(ULRp;RZu z56e<}P(dxs*XMGFYeAsEz1I|h>Hy|MeE+qC4Giv~3W@Em6J|pIG>wJ;h1P;J(*hK@ zo*GSP!GQwNRP8>xi8uqm?ZmZ%NSeq_BDWK{1Edztj*R4o)mpoW+fEjXgT)c;PU7eV zNw3y+QFu2If!q8q)cm8`7b*S#h#h(D(e_dDT|^F2o#LDs;XivwJ50pV&q*e(6V(1iE=PZ6y^?rEq)uN^0HKXZ1Ou)^RZTp@I?_8@Wn8j&v(d4$NLASblPK`(TTm696m{xt?dz2Gw}|u+`38|^hF z6OdZv<|igxP6cveBB6FHtM*EIXs;x+SLax_rM+nQ46Dh>tft+ATj7y2X)i5Dq=B;1 zRLY=Xp<6Z4&&jQyv^)|Q)*(DLmLt=Jy5@UJQR`RA@T%R z-l^`O7!So7j)&qm*fXwi&JxB$agUyHkB3_2c!v}rOgCn6Q#Kf#1A}R7O{?+FmSGMeE50goGOgO|Dl@NwGbWi+!)+DDi_ZL@JJ zhF0JBilNo_`8Bi(qhQi*6dW0a3vxldQ$Lj*$*b11TsqRoD9Fl2s2yQ46k3SPgW6Fd zjYdBt-W!g7ToZFm4189L$upzHwo%W)rl!y8%8_Zzrg(N#JGbMzpyF9ENZIW>b@r#a z1Xz7I?K5JKVk?!HMmv#it0@`b+?UDJVwucPQ9T3K{Yn;c97iu5Q}6oYB7gE znEeTBK{(MpTMLqBoOU<8{8=LXL{1SoO=J+{f=^sXOblzZmPC+s2po%22ri-A%) zmcS698bGWXD8<%>aA*fzVu!_Grif&|b-HVsH<2F_DD=7dry#HUK8s5c)G!c!q;@L_ zrQ_zpkYnb$kSF0tc?`Y^W4pu%KrBu%nHsGgS!B_`C3u)%eUtwgblkXqJOJZli*xBf zIZ$qS6NB})XxJ`3a1IZu0rFem4=oTV`;El{;c|EZkDIXJSLSzn+&G4H@=9(mFg4J$ z3%{(x;Z^2Z2;mxd$};W+6EMUDRMYqvQOWoiDX`nUQlfXz_&Dyu@{e*2+v_}T$cqU+ zeCXAvt5*X{>eaw?_GtQTpkWU_O zizz~U@PnlHwUxN9uV)?g%4?L}3mMvs$%>fpmopbK#G81tT+<$n-z>acVcc_2?O5pt z(GN*`Ka~CDfbbop=iF+0D=s^pp8KunB<|~bD?U*v_DTNMyWVIKexZu+SyT)O+SbH- zzL?LVkf7o{=T&#B$8Hnqk-Yj+Sa~5tD*4w)4omh2$u!ngLsId`-$u<(H?EopYTEU>piM+q1v zGhED=bu()5Mm4;8!&ZQNBlJ$pvj~^Jo=vZ`RYH}sb6u+{%KORQ=`$Dm-##^yd~i1T zU?uz>mMILJ{|+#CJrd?p&%fBvPavTJJ9 z^y=Tg6@ki2Q@379>_UjYmz7<7`SwQRw_o0K{p3lYm;6TsvcX9LegMC>zHW#=# z<%8NP%+cuu(6-_6WI#ae1qh)lDiOku7lIA_X9X-JG%eN+X4@J(wcIy zRETi*QC>ApxLX443*m+USt8;KF&84%#_6i`qh~NI(C;3KN(ije$~6o4GU)9Rpx5w` zGVXe8FLlsjxYt?cS=6ncf&6zu-*vZ49lyBwoj|qwIDNbBUkC|ro=R4g%>cW}ONq@0 z@%OT_dDaYb3UMGbBQiLf;4raqY%0kyW=EI4h$7n;EP-jtHa~XcB2E4rCEEG|A!`H71aSSpE8uA`8=* zN>Ry~YB`Z6E&8yKrgMJs=IsY3!n#TOK`i{~9J^xyWHE%DU<$McjXAv}z=if*($VLL zybN;X4w~QlIfcGTX%@8j9z}nhNJD5rvvb;S5cy4zhN_+&3e09$vs_}6lEt)rU9nNR z%u%2ohr)lavwL#ybjwA(+WG~2=Q>wS9-rR)c7UVvp_cAgMfsbUuXC5jY#3`-X!u2x zD{;7VD`9|-T($CK*Vd^$Rb?w-fIXKITPY~MmzAxvX4pt176fNRj*(9ofEYPCh3!SG zVhGFXIKV03m9;a!y_Bm6VF01I3`F(PqKFC{z&VV63`7OZ^^qbf##IYwuE+6md1H5i%AhkYHZ*-Eh@mzO~< zvzQhl??gJT4bf{fOr%I;gvfJ5G$J~Ymx$04)LtX1h#6ZLFYXBy@a1VEAH3Gqw!tFJwM(TMbZ>R~CNA@qys$IY z=doRD(^sq8tkqe+GuG#^U(^sq8G}c+aGu9V3Hk1`>;U&5x zhp?|2JN_DS*y$8TTkmDeJ%Ob=*tNE=KKw}U!$aADeD84n`D>jrdcT%^zL!WYD4(fq zo7tZr^>v@!N6gtw@(wHeGkA^oqx#E1%csVc&Ni7KJbl{6eEKD&Pa{;$PFKJ@N%ga+C-gjmDl<&L4sY zm&>zDI)2B*4qJ^qdF(B;e8d*>}5 zAJleW(eANLV#Xi`$Org|jm-y!hY(Jg5)g0@MhAgBCH%ZX8J#6XNj@{1e1?61ggeKk$s8tfu|sQakr`%ifRX2ejTvU_r)teG zPv-1%#R$8mi#cUUGtAPUkeWPMRZ?i=$xDe8Lj1j~q-M=9rw|81Ga`et$uWl5I5v5b zW6X{)2PEYb@XETG-(E^GbATE-I7{Z3ol`nUHL+z%zm(WXLGit;?3^{jMk28wI3se5 z`~<8u#K_SpY=vtTLs(YF0Zsw0teyGorCdd*p+mDYXS8!VKzWI+(}Ber3Kp<(t!6_p zrDQd9I$*yf!mg`w%90w&HSqLILumCXeg&TrXhl1=tUxQ?Quik82cb$k#zp+uawtWW8ETMC z>+GSHHXIYN)~PS@59lhtEhcdm(>t4{0~eoo=XkZ}A$%Qr=bJOhZ_OsZWz##Kc9AZW zh#&Br+G1o9l(#va11WW_!$4}>8AxQ{eG%~V=6$5u-dCKc{2X=n1K(UH9NTlCBNJM{SNvu@!oR%g=XUF5*&2C5C(k$sVePTNYdmCt`M||9T*v=vD!Ra=W zdW}-on05Pnj+$sPNPNnRnMrAsicz_$7(FS>&3tw?ws~V(jF3K(aL9`=u0_R8Kf>+jp zixD;1K+^#nD4Er>rIM!o3B~E0%_5FRBD-WQNbMBqI<<*Rf@})JsRV4c#QD-{K9le7 z$H|Xlk9&x8o{@D(b7~6q07akl9^s=hkAkbd&cDWG^<5z6eD9{VPY0*_XHo}dQwOSD z_e|{n(cy{1Z||+#KZo6?pPSq|mHhFJsr##4>1u15rxRgbksk_+PNhSboSo_vl;Z)~ zA;j`qDbqRP{uA(Hq-Vsvgu}jZ%=ak~(f%=#b;^_7lN5$m>by*UHiyG6N5{T$V|`Os z;s2nElK4c&bglW}!5> z*JhJn!=SHcT~%3EIXjtr6=#}K7~jieN;krsLJSDam>J9=qq0optW0ATTY_Yo#4_xskAOZGEH_# zCax3I{zWeHHghuR+}fV1(t{fi=cP~>-^)tRtO#(_V8DMfW(IS}s4PPrZj@yfTY_Yw z^H8h{AdDRQms3z)wuAZYrR@9&*9%XHou{B_oWc|O2x*oFsofROey+FJkikI0E*u=d zV(J#!cFu6@gF_*u48qyxz#QZ*7=-30 zm#Bn+IZkDV)j@y@a>o~lW3ZSdu#w8sG1K~3`vU|+hKnWSo*7n!RYLnS^tq)Xw0}y` zb4182alxh{jB}JcRYbfN`N|XGKx^%vQTZiF`Up$$j9umb53Z~KPY_6T#kcK_>K#XBw%#+l^`6hWfM!X};vtCpH$PH5{8gQJ zaQ^t3^%Y*g+nDyAY_ZUp{U^GBhzUc)L@-E?7$Qa~D-bbq2<-43MxGEcw0@~x#I%_= zjWY75>by%KV(fKW0>Sk&hKRBA$#B`lCq#_Q#g%2)Jt&tR$x$Iq%5 zL;Z5hDO8*>L&PYUv|tgljebMCy&9JNjHXJ$4l2epx#F;1(y}-fUQgRSFJHy=S(Xqk zWl8g03jGcdr=-z-Mrq=w@5Y|3E^{Je z?u)4rfIX$QmSl><74C@~EUUuqe8~)RW;Q^(46Kda~^X zd(wqzmVkZQK2<&GbO2oJodkDXPqyD+PrBBsdG@4>5AS6Ar=}-qAF;%k*z_KUBVr;r zWeO!zR$=?zx`jKu*N1?QKY(BoXmY@ii1+fLRzE|u78k29q>&l8g##Wp}KUww@yCKp_U^Zvev=UUfHuAjFaJ#f|uUdm)bZN<46`-_+stxV&MrV%hU~{EBiPbI?;;-D-Or{VA zB65c_=8#cj8QXr8Wm;W=WSYdf7;95Dgym&8z$qxtRUnc1?WJUqxfE9IY^jV-wJf}y z8ga-SUl8Ce(d;{+aY|)erqw0LVpA=WiR%Qlf04_)&2PL`KeMocKz!~qF+8RHrx}5? zEeisDTb{P_^WD+58i+5B;cA;S3Lv#7tBeMo)dV)#ZC znrndP2@PC)pTdD4Zz$#v!{T(~+!;+sVzq!x9drx{ohAk0Ya)mk6Q09E6hlNq(#|g? zk(VHd*|=CQh{f^5R`bTb=rtYZH6U|Ua1>%#UtnlT08QfiyCvhyysWD1$i!8yiumJ97AwAChbIx%yWi%@|S0 z7u`8A-xtXTt!=SRhedrLYD*ob$G&a#1{bd-1!~=r2e19o>K83wuQN7gbPMTuDxZ6n zrn0^Q)AC?;#PDYk+niAr8!>px#t~`6$l+CzABk9p(TlVND*n-tFgOqf-uPCSLPq*P z%edd}cu;nEHjM}D$E&!#fZ%%r4;*Y{7BmbNa8%HLW%qzO4bujHu;Eza!XM5L57`vGeZ>@BklHZJt! z>{U2Mw86~Mg1^vsP;JAscKg^ydW+=_RJ!XFx)Gfnu+QPjJfZzEg)S2zqmQx901YDZ^d+W zU{43v0dFJvnD7HR?)t$p)RD)!jp$F3m_G%A9aI%xSNG(msocfKtF4Fdr5&!1VZ&>! z+Ily>bDdq2Tde4OB)BS8iNbxh`?jgVOxNz&uHD#YHPJb7=#}yFypTuzn{}6J>B@tU7 z6HVzDwxr_?OT!jY$@aJ< z-4Y~X+##VX*?D~uu~kY$I_6(oPcVPW8g#->q2!teuGeg1;6h45w`;Eft2vIi=+F`yy5N7?JykybFRInT7rH zKU2bjx`mHe`Zn>2B^fjWW2zeb&)TN5L@P3APS!YT0s2O4zS3CVI2f~}adrX>HPkqK z#Ddf~4h6KLafEf2RT%UZ>no|+kmCAcLq4RyeQ<4c;lq81!>o_1rxgtjWh}XM z8#+_SNm_U8If{V86J(~+a1_xjYX5k2CJtu!#B^rNLw{VK&WuZ)*{ZfJtInhq9EQ%c zJ8Qeq!niVkt*_MtY16U-t%^5fJchSK!LBm#G7bhsT5Gu#X|V~v{&mOQ+x0EIJDpTV zlls=xUuxG4K2ewc_C!H0K8y>y)NVuHwyCSs)#Wy0EZ1I+PRW^k=o9%Vw>!sdepqw# zA9(gmD|%)P^<N?*GS3q;PT+COD!q&!DNil^1%+tgyY@X0K>6Lto*El4tGG*gXhh;`(+M#>m2 z97yZ6He;NU8Oatj9i?ue0{0O)PULd!t!%o`f^s5;=k@ z(!l60piBF}=!t6UVSMM@XV1|%DRA*5=dKKlmN+nackTMghko4lMq8zAPB~Upj#bVI zGI>iiaT`MR?=&S$-*)lQ>P|=cjB@OS|q@)ns^ zmmr%078&M%O!=j9?2^cp)i48BW-le~!`Z&++hi%P_Fa6`#bQg4waeDjr_{I4-ZELY zR}Em6{LCzi$ZIVSSb4&q_b1E`e}P$o2cT`7%cKTeHaJ)qMau$7UbT=pPo>CfnmRWA5rF&#|J54`aoUV?1Nc9=pyx^WbuidBeu& zABR<8-o@I#Vexrhm+NuesE0;70sPS(4pGeY#*C#g!XY(3G*TM1!N0)*)>jLt)5Lr( zPs|z{Dl9lBt5f$FfP?WykC=cJmzO7s=^48GSg$waXvF^=LLGZSux>d_ro59gUFq4b zbVYf0#gykd1l=_=xq!fVS_mAwa|2Fw{kWVMF!2NdL}$0vJ26C>{K0=b0ti3WCfnmB zfL!9jQvkX6$bq6c(c;17c4COi=|`9$mv#D)99vD39j(o48xzM4vg?A5)xb~ z=)TabDAL5HSoN+2*i#P19=?(kHoRAKV{?y-_18(lJRl?tb~M;<%haA}{q2+0RmbSt zeP1Q|Zr3);fnY`I7-koju#r(Ab(-Ar_&w+i?Y|KDZ$$n(k$EC6ufH0l5qXTB)jJ5B zlmF&|z13i^xue2-AV$Zig2)XKM9NxoLxlOjqUWr(h3@l{d)-_v&b$OhUs@82)EWQ5376y;!_2S^!Jo)_{FAnz)7IG!swuOPl+3fp=Jyn=N zdjE~>(6wA|uBhcP?}052`U|BJq*iaP&S5Y|$jST=!g_b0C1 z>cQz!p&neW#gl5Ap=hz)k(ww+4Eu)C#7=E4dsQ9M?;wY3v!Hj4zHKn&2*H~#)ybnau;@^{&zc^YGq2?O5`>ow-OOPLUHYPDNNg}EX^I& zIMlRLY~spt+OQF5!huLM!^=699fFC9MZ@0`OkIW{IRKgeucXUl0 zeC5US*syiuWEKP`e`YTwHX_8|%gV-CGt4Q(fzXVZ!5lJ*EKVV}RikWOg1v6ZB}+~m zlxbYjmLQpETL!A0IVQ8R2sjYtD@e*I;AM8^L1}v_$;<&d<}-VKw5sfwe0VDR#v=$+ zMkgP+l-Pj~e=jRLX3a3C5C=jtB7?IlqxRV3BOGIPggGE7r=X;)oB8dfBr^v-GY#eD z@^nH&4D=_k3Xzx7fZu_FYzMc)%PCB&DCiLP1}F#v?Ioh1XwxVtCQwkkAqr|1b@Vti zokSMimK?${ESy52Zfr0`qjc<=PtMP%4fr2_e zauj6x`o@K??{cCb>JtkEt+x6^U1Npe`?VG*sX1i$dG3=QN1qUKyb1e6O#&IN8@tmJ z8J-+X4HeJQrq5VPS1Mrn^5Cd&+h{n4_ZI}GMu{vBHq@R*U004!g-;RrkjUSFc)+!F zVh+%RUS^9kxP$!IzUJ_nqtH(Yu;no(7l7@Kz0*+l9+1u95$+AyXyMTcU~@q=j=uL< z0c=J?C%ewSgvHHMogkH$raBkdI}03O-zD-g zNa0I>CCdu42wYtmrur8{SbWHliLk7@VT>hC248$m%vJ0kIi|(jD9g5%o7_wHO?SN= zsJ4Cq-xZW~9n)f;O_a5D3P(0=1I_ zWEDbKR>lEN0k5o^`R%1#ML4>EmyL$^+wImzi{xE^#E&H^;_NuXWW&pyr9lwWM{?4Q zj^~3)3DQY(Zj%jM)+Bten*r-gdfj$cLTh6Vw^JcZX!bTp6QP?=ILkHWba}M^q6Ng_ zpP+Dq3mS%?P$>ZkWPBXgv;4g~dOlq*}%Uh6|jSqL)Suhrt7Df>RCM;;hlD zFlzh^?P+|BNR%xW+lgTZku;H=L~bW?2a!98^n#?rya0gIWy8IjsbAT5j$P;0IEJ$9 z5+@wzgcWCT8EcjJS0rA^aZk9;!G;i+K7O%lCV5~sd7u)W>%rL_>){aI`Dz&f{$5tr z&zfOQAqIqI%nas`QDkunxse-X>k=f>B!)WH1rX+INXjWF&s89u`R%17GY6L9Zi2Cd zPy1g)t`YgaME)NUm%GWYD2>Q#)Pv-9bWZ+jc!0-&nb5)6(82jYdni5c10k=EyO1Ki zKZ2B#GTlRr<27Wl4%*ZSq`nyCZ1pOZ2yL79fz%hHoXuu2@>bbE-YOepb7YE0W@i+- z;q(@51pn2PlV=KpaD&jr%U$cqjEvH2*~{xuU^(p7^3Q99Qoa_INwtWP#@os~NA=Ji z=_OH;Y;}D8j(7^RNEu6IM)g_%TciFd#lpB8rkZNO)5XGYEut2*;p|YprexrfF__O} zYTf;X7fNRUL3;J<&Ys!(KqIza9e^hJ3n}M%SzZC%>-8hQbz~~=_ac9o_-W#z zGLt$yn>zgV7iPL1ob7sW#`n;y@1d&iq4#{7tLAq;p!fsxzK?>eKH}do@2e*tP}+x9 z#=U_P` zX=cHXy~Uk;zBsgV7sD=}kL8alR?4SHC7(;F%2kvQap|Ar_xjEBbocInt0Q%i94?m+J+I$;-P6<4+x_0_*Z-`$yEB5%AMN<}7yjF> zNaTOeh5w0pi4X1|@!g0OIUBK}R?9^6Y>W8YIvE>niAE;lXWOEj(|)#H9<;*k7WDue;kcOI-i)HoUscR3RBMb zr9!4yaEeZ5Y}(FTD%j5W^i;+g9~&#!g{k6rzDU>OQ<>t0Lgw&9Vah7lV7O8gVAd{V z&KIT%cD@Miyk$*K4R>~)7V?PU_1U~-&(2H~woK%&g3&4z3!_4!bJZypCiiuY3};Rk zCTAw{#ljX(Qo+iMPER?-{1j@n>4l@uJ%9X}leyC`o;tcYGw$TZ>_Q>uRdiE!bLQyD zLx)csJ(4?i^w8-u&mDa}+qr!>^V}@zK3T|&PtHu+MWx)aiTrs;Jq5y}r=QOqdg0LV z6U4tcbLRB%6UR@#n0w~b(dQ1Ge&#tPGuydiIP-$nHWTBM<5aQpg`!`L9OE`+Gj^dk zYfm}20o%r%W-jF?W(!WXbLVj8%<;^H!o&<(VQh9vGy;SK4ep=~u4eL6R%Wb_hY&O@ z+J5SMw)3adhfZc&Yq6)NM=#dmGx_4^g<8kc`SatWgvmy0olj3&vlE5WS7!>NUY8Sp z@T|q>gKhZp-AEyFHi{09j()b)YWZg5Y|Lr}j9W3lHtM+O>TQ4ts~w#sQR}Bi>D~Ad z|JjwYgv~g!!%Ta`g8w68u4{!Piv+iIptJ#-oBiUQGKuk%%TsjC_<=j_We?t8YZD?kmx`*0~m| z8;`oD^r(2K>f0vYLcDdE8G9OCcj9WsDHMlo8rab7k}7#EMsFtI?8L;|(OS#+R5n^# z^6;jatJ|^;jUbfVk#%gU;oB{>R%8&^Q5sgoWSk4rvlCY4)k0>71P)~|vJ8>Xq0O1| z)5XltN712=dOHv19jAa-o22f_IwWb=$!u3G?bxHkv&HcV^ge$S5k=NIAD_8eM9rHY3}^Q^sD)a+*E$D zQ0vG!c^V{VNE!yu$vh;BI!!v~YdwcV5`*Va+n%;-X`iHU1uy!P7*?}wwU|?w7~|@7 zQTZ%jdri3*;R`Ndr+Ts5p{|<|pO4OMJ%jdewoc~l(aG_tVqs$IDSP@g3_{LU8n3qy zz_8+vveP!u827i%PK_5uQjTWE=s!GjwYE|;oHttKiWp~de(RRDQqM65iQ#aDepK4Q zPi*9k$nC^VSO4D1Y<#QDeQ2wjc>P+L{-!9&--X2MRX@!bCP}nIvX16@GxGh-iZ(4PauBEMZtv2gykc zk2Jx+-~*CiIGbxN%HakteQQw;CXt9f1(=JKWAjmGO|gqg<=~IYOy*miiz!9Te-E| z=B3-q?ap6nQoDVrR#&+lKiNs5uKUUUG@oa8lh0G{BeHLo+w_(W?Mnt7o?W-q<4pt- zTARtY#Y&Z1F%jr3w*mH*TL72*LAzd_l@@Pke>ov11^43#w_6>U5G*Y{M#Fmjgj>#D z>*l(zPESu1@>AaIprw{FChOv7u9P*fm^6F8_&j&c(F0-FgeBe0sl0D%<*RuWi6 zU=4w_1gJ-3JM4!j9mshUm+Bm2hdQm;W$z^FK}o|?j~x#6e4cFp+8Zc;jrf^2njz;e za7x?5rv_m>bSEFYf}cQ|9UOHVeQ)<=<{zr`t*iE}yVmxT)QamHzqg^18mgv-+{9@& zHB?EQUPzp-BAq>rG$j(00A##GO3b%Q62~M8f`o>1Uf*bNDu~57QE9(&T&9AM1@i6i zEdU7)&hl%aglf0BK?^gyKknm*AllyObtTc(Q&Lrg+CLR59{zM&9zI{ z2l)E^I%<`_K9{*vnroNpsqU7q)eGlfI zN?Sabd*c0wM1;CXooNU2zk69rJeYeL>p>`_I_B!=6Px2#C4TE~I!is7sf#L3=mR?jb-EKKn5O+W`DI z-WLgRALe+098vGRBz7MGIj6I~fXl__a>`9KJ(p|5LsLZi0D-#S8X8@Mp5t%uZq@5K zG@V9t5uFB9#sA;yH0ssX>Ubr8-5RK^KQB5BW2BWjjac#SD_%n%qOYOFsMA`cMU(e2 zTWKzCMWN}4L(>6`98~wvbfCwzYwPwNpWEsbx{fZQuhVrTo76Yxnlbr%Q>68s_tEdd z);Z)859fdmB6-icw0r1xTI z(66W$!I%=q|Ms~yt8cEe+*yvokQFb-oj=mfPxJX% z{ht1+ORH<3zglY4b(v`M<>f9^;{L$-TFx#U6~?r7qkWj!{-#b_2@QEVv*7S2B< z8)WJ5F|0dHQ9{b(G+d~P7xGh?*JfeIVxi<-<=wF?Ae&+<3v~u%1^xtGHfv@15)lhi zOCOtB0%5@lJOd)t=(0<_&>TDVsOFfxD%m!B9Tl~nz;gtiB|xScUe;5;lO`HJ!KNB^ z;V5ty$oWqMu4_(lF&B=83}pRseiOe>WTZLfHv0ZmYK6PJTg~jFYP6i(?uU16~iAg58Qr<;xrJlL_j8bU^qB-3nKa@m#*7 z@#Kj+2AL#gG8Mv})w|qHoVj)x&(KXhf+T+z5@)J@nlVh0I8zZhoPX^yUx9)V@={_% z-|%F4LaG0l(t!zBHhYBt!+0cQ8bUldeqHLNf%sNxz)ie(?W((avzywCB!3qYFIN3D zW0)lIVnyU|{lBnlba2 ziKMq&@pG6&ULuPz^Oq&A4xtK{RFV?~NTgQ*1;f06pb1#^m0kO)3n;#looSdw(t9%f zdyvFZ@}1V8ReqL;^e=ap4^`5es_9K`(tc|Mz)f#@Yvg8nGbNLDC221t?W#yK#wX!S z&S4VJNr{QH5`aBIfJkOqc)<_zCB(!XUPV}Xe zr$gz`*2jSkrMKKpzI1gulqEukk}h|6I+S=5nn`0tuCJtpO(F5Ezb?00o#f5LpSgr) z(@GS{dCU74e!9>eJAWH;7yWu!)>2XX&zP64R`)=Ukud7MtjV6LTQ{VpMQg$4e2djK z*ErCFmW9;X)VeSCtPNdF^ph_Aj`b@~gPnl(y4C8s?>%KeFYQ53;g?Nsl~BvsXSJua zSSwifJ|DG@86!;6S_%JbY!dQ(vb)RO&aZ2|I`{=y1?@Yu-b8Kwf@mDU+iO^3LZ}RD ztF^`(mE#(HFe;7iE(vR0H`+^o_mbA& z=dKOc>G$x9Z$q*c=+AL3X|4a=~Yz0$<6q@JCP3|s3n8n}{K<6@U@-o6@`9I&rYlu?UOuDu1n zNhfQ)lldz-Z+l+O87~!TD+mLd$mlG#5l%_2w` zW)WBbcpC;RCbSKs6%w+%<3jA05LOXkcPI^q-eAKzhz%M)jlcm%c;5Ne2eO5Dn>4Uz z!ncKxTV3hVMm7dv7#P{Medo*-e%8FB)Udw5pkp70jGtnL7%4S0RBcBk9 z{Ar|DR3gkev2)wr-FhW{O5R1K-J$l3oefJvgVUZ^--huLWGFU_m+7)u8wQy#>^uO3 ziR~hRuyd3p4H>m=znkUI%^dp`${i#?o`v=q0>VJ?6-s3Yya3?P<~iRa;r(UHga|;8 ztAD2g-@{$u`$!gHBNL8u0y7x0QatH4`rhta@Aj>)^gUedd$?{d$yu@a;_O1=Y&CJ# z&7Q4rB0&i^;o&7xV#q=vID8TX0U}8!sU(KBj%b_;VsTE|I-+qZ2q~U#hwepu!NFPH z4T}3hu9U=N(7mtR>~49|9XNii`&K#w)osUVQs4q!d*W7l+4YU{2i)ZI{9Q;sU-i?B zncs1JBT^MVgDK=CvKTWDrKHDOhfrlpD#?jf(m53ba{_`yGPvuWe{Tl>ecR_twgcJGj5P@_?I| zrF%c{Cr%~xL^bsUu3Z5a60=pLkIq&&k)Q-}8849%L$@k8d=dpAiBOP1B`+lnYN88L z5RwRWlvF?>%~@Wp1qRhuX+XeVJo)5+uyRzo25+Td@7!EXZ@CkRbRTYcx1ayI=`HW} z-%Rh~D-~iN59qM_3Q)zXX~E5i_Z4!L6(nI<@Aj)Xs$9l1MimCkV~1PZ!bNxDYt_Qn z`I2G1C{+lHVyNO-xKRXdSe2%xRXK~NMSJh6rgszBOD*rw{x>eGzU@V zOJyX!hR%*Ax{TmkjAqEJ)ONlARTyX*;<*r`C{U`>)U+z6`Lr-Fp3HXC5;+kECYP&q z=5muF{u0v3Tn<66CMZG-uG(`sYkD-7vmd5e@O}c16L^upYXEQa))+AlwrNj{0DVX} zmjK?7|8B*0xUn5~Vy$r-b1wyUcEo9KsREZNX~r*G5_iC*z)j2I)U=JExJ6NsQPe?x zXgNfm4`^fgV!@=~=ohc8q$#@>hU>V9!W9IN8WxN5Ab^y;Xtb`7C{l000ZcXLMzuId z<*2rq6SVK(KYtKsszq!XlBNVWaLV{ip@C4O@)XoE#LLV(Zh8pRYT&($_O+}wu75f_A6$SwN`}0V)P?2 zuSu-{)HgyGAcRT{fJ2>`mhilOUsc4bo`YW{VuLxk{H6T(1o@ffs0m8D!^Z)R5!IM4 zhZ3Ca=p3|m&${{-F(2HOkADAjCAGbp+U_P^a#PzYiI)}9vvEXnB9f%#&iwTqNM;&o_FmT14VUTNlZ^%ZPdft)Y#m_AFrp z`sW@|1$Wqp`mh*LDT?8E@v5i&pSJ79R>UPw$=kv=$$G$j(00A##GN(>FE9wCz`2of63>8{*mu&S8Fc~Nb> zR0Sc6schxqqfbUoe}poDt$ zwhd&wA9+C}g4S}dT79ZcQ!DYaa~o4Xb*df~%Md;d|JDlyt4B37rg8|MDktC+^H86j zVb^V}9QS-6;8lmI{=7EL3#R(COPhkpg5LCkz_hZt3AR3E{lq4@R-(!Jg#CCKZ3y2w zb?PhA=Cm0T>_iBq>Fe=XYq5@dY%!x>g|?VMhZgGyX)(iZ(CYmCHW8aqU&6p*#UDgY zL=?a}kC>$Jh=KQ~=Mj_8JYwXN#DXS>>}dGj>2C}cG(GT!iQ#`6_EYy5dq0hy1CQ0z zTnFBn4lg1CzhnA61n(GP7~&o4Bl@~`Y)O+(DC`|u>9HS5te-R-?5N> zWke^i(w>iEr$!(2tHxfW%gUWzk7{qCM;ZRVi}a`tt97oc+$BPLBL*!ZBFN`~J{t6t zQfoBMe0Kn(vWOLh+J&zb{B4$S&Bs@nQxa^2{sqkh}_UK1WQ19Z890R32Tcvf;QR${bL3m^`dP_zY)|Cfm<1EB8z-NWuR zzeH#eu%2VHj*Qr+iTniuUm>uLz40#UZiTDIcrIVkc=E&@ zgG>@LnF?>ESGvh_^LyO2!)|&QN&YS*&sF_2W0)j)t|D?ce|`^Nfr1h8Qeq%iB0ZrB zhD8H{CScj-6(aFWCTV2lShAGQWDIYmSG&nq=J&dpZEkuSlKfprzEbtmjA4@GD;1H$ z`SW}E3KWcxml6ZH66pz5Ff1AnGy%(YuMml6GD#yV$C9OdCR5=ey7%WW(fw|uLpk?` z)gYQG<3XrH{A4T=a@7c$6S_grp!2MjK|zm@q|F4Hw7=K7*ms5xk!qU}2Uy{ZEtuG|ZHj4L+=jF!)uIK5w%GBM zpQrm-G2QnXD98Q>Ie8wRu+=*`(Lkj`A=Ut%^EV^nt0>(0LkuWyMDE1m9ZyGZC7*H| zeSeZ(}j3ab8s0uO64GAY_qzLwplJf`hZXTND`NN=aO#XqUZzva^zYyqbR8O*(G+@k-KJ zNIF%d4?9RxB1s8A#!IBcP&0UhOrju2XgH_47E>*aRmCjMi)!nv3PKjiH^esqBse(B zyG4OPu9U<@GW@%rq*l@fgr{K@3*b+ZOTP7Gci_uEiUQzUNuH=CPq>Y~e}@l3u0Q}1 z&H#GrH;(T+-0{@29igXcul3UVedahF0y;f~ z?PX4BGhq}nAI_?zWAQ|;+&^ZGi!&;98=rJ7xr1p>Gd8{l=au1cMe5uJ!=@m{4|M1h z#Gv~eO?XW!qVJ}6f8ub&v?!)VD~AwqWZvA>fF@so%FNfV=WPu(lk+; z{Y&Er+SIgveTt{)h1AfnJBLsiu_X{)oX;%m*63+U%PkcaUA)_~rQNR>5nW^v7f(J( zi;we&Y>jB+IQLL_+DSa^r17+2^Dg&t3@7xBgtNk0G1sSY;Rx^Y5`!yU?klIk)nD!> z`yEE2f2G~O+EZvGT1lle*;CBuz@NRIV)Oy+&VwG`tG}JrDzB&XPz3LCkN5nSmQy!+ z{obPfEgL;2iZ|8y=UQ26EBB+7*V0pIvX%Rc+I{v~d5K;_?aqT%PU}?GLtZN{voh9T zd70PB%R^gvxz3NR8S2Q}=Rc8h%37z#z8a4Rf0zM{K6vlf>vF9P!iN5EdBCgDiY7*T zBT~LD(HrgQy-)e?KK@dym|pzv!~cH#Uow{#?*AK02gJ---Gu&X-a20p;T#YXf)4=F z!&(ah?0LW9AEez^BFb0>jXcL-rPwT*iWbzpVw?-6*K$B7H49KwyplEZziAKiP z(%QpRZ&5AZ9afD{9T-__v1892Jt|DC*=XsAcmAdylX{PDbw(nvI-3zA5FlcBb_PK$ zI_EaQ5{+F3!{`%1da2>#66SL3PiZ zIN{fnVtIQg@jkr#+5f}O1+nj;lE3%-vcNmL@OX1g|KmtptPq1hK?%rirWqg zIG0B$ufP#$q^FdRgn3>sQ%Kz@;BEmQBe;DVm%E)JZ`1X5dyFbGqS>z-Hyav;4_2qf z87k?<`t5X{ItkpBUBa=ke~0c=n5G}0)O7+)YOvV8Pp0r%Ts-nx8)Cc{rmR|ze3G#gzK3`Qk5nn_^2-RmTuHpVka)R@ z^yZh5rbL1gfQ*+&i6NJ7kB~_e1PKl2yiUb&5zMI|7Ux8zbxs8#3*_5j`9Ok$v%DJ= z801PxTqI(6&+q%SuYc?7ZsN#|K7eaqztMLywGJtKD~Tfui6d2!W{gL{d7Qx%AeD5C zq43=vt`5P%!Z_#m8Jr3NoXmFZ>rBK~H~J(C*Gt`jd4k3xCDvDUtMKXZ~>40*?tANBKA=4;<3tUPO&J(sKQ&>gvt3L-|Mxg$YOe?$yK9)x(UL-{r0*4*|XkgYRzSocErp z?mxjvcmD}|EBjAY_n&0UyBpp8C*AGdRlTV9MHOg+^%1eB+^=UnrKl96nThst9}zp_ zeb)J1Y7Wz?qU&?(X|6w2Po>IelwU6e4XTP!1)yowEaqj%cokEhuBZ7wq8O&+azrgDK5qiPcp+oszhlmqqA6}^E$pdGCy?vC)56a zZgs(2yxfa$!HSn0{#^DCkIGFMDBh(9s z8$&0OQ5=a{U)|>ATF>}o9=f$@Y@C?Dk!H2tSM$Zu3y3^1O;I$U`l|KktxNf-(Sntm znRdpDz)AH_pZHh`ZN8#18eSwc!4q0KoU zj)H@B$)>Tiw9fOG&rl2b;&Ip;wcZpCG_w($wNC_R9aD}%3-%x!D{X8f!BfaJkfOvH z^-mQ4msIIZ1cc7!<9HqMQD#Zj$4EJ~UObw~@!3iL`8$02m}nhCO*2U4273@_C{|S) ztkzglq_`nr&!!}z1l21x!sx$)GMuL{0U_1Kvu>j=0`{#gMcq|b>6;k1wR{LZ7abGP z_XWK6rCUq4xJ$RZb-uE6cXjFRYscV)bA9Z4RweytHT|fYyy&JMtt2llBrjHx-g5Cr zLje2*nsCXBNB}a4L`n>~-*|*fq990qIOlcviffz-VsTE?U*}X1vOvBwz6Bt`!CBtD z3Jh|kBrfuHYAqBBsZ2G6sGzOg6VY3P54*i%^OtV)|JK)#;O|23Sk+H6h6s?V_!&$g zFOkKV`AZU4hmb@!`Xv#|lsWFu*pI#hz+Xv)1W5SHX$WWeEC98Y#FCP;ZVzsBdoQB? zOMmO@Nbq-|_hQvgGlmF|s`wd9Auo}|7`j1^w+hCFatH#Ay$Vsn{R8vfM=`(>Spse61;vkKi^{iP;&(`+HS5Lu0RPKsFd(r zfiT-*JZ%=eQ-UpL?HzQ^?xSp>X~es7^qAd~}7pu1^L^iQBShCF>^Jr+V`u2Oou zJ`%P{N$IU+^@_2fPi|_zAFGp2tWMf_g;H)U$Kb4yEGM16(C$EWAV^|Wm?p})Uee!W zF9~}p#t0iaMi_MH5&fTLkFb_{+vLMsdzKkbak+Sk17+W}=l+ZkNjT)tVfFL`ao`$4 z5~#m7>Jw}ijy9k)pPOX?2yqUDj%&t=EXQ zURzF$M}-?kZxf@j;gF+CggXVjF+CVvJB^?sMtzm>7uyC94SDhL_aQOI6Uf zW=m};+05&tuoF&F6yq(*OT!H*8=L`C=aJJPYsX#`ThP}}Hgw2&MDu>(RWBcq?%x23 z5)M0oa}Gy9I0d1#q^ElkQHuGTn9XOni86xI6FG?;0ML`*tn`8(wjx8}i-Ut=;74Om z+cK;|X=pJqAoX_)1o{kLVX}=BCeV_wBMdp?u;K(d6cLyL9yD+V^3QaEA53YR=FFnD zLSb^Icr{bk7XDEvKSUcS>W`n#WhLt!=N<_AGZOo!1PqObh`(@#G6KgGp#>3#qI`ui z0)0pyE>(SqNV8Iek7;k%zelcBoT#+UsUX0~()e~hZfI4-i{br9yyK-Pf>X?2{mp~FeGmtM+&Ba< zfAGeko9Tz;@gefy5P4DvQQ$%zA;J{$5?PF)cm~h&DJpdc78b@if7Rer5K{d7K_+4$ zHxMVnD~#(V>D-~9s-WSLRT>t+7*to4BZ)b2EB(-oy)GSI9UjJDKPdbt21ta(Etuag z!wN8of?)AsoHwwe%?qBuBnm=S3?#B}cle&-9o$L}xXDxVyRIL1sau@l??Uoa)lV}< zP$W-PL(O-^0}*SXdY*b#;wXL4cFl zsP7XIU)^|CvT)rbox6!{gX@L_m#osT0LFk!5=%-Wv4|?|f5qxmX_HW-MI)q2dlTdM zJNVBZDAHu~Geo|O73HK3sw+&Kl@0v9B25}8VOW-?_0S;q^vmRZUXvy1br3=BRjINL znp#f_cmUOf${`n9;T)L0Qjq)R>eH`Dm-R8H#8^Bvtc6L3vgvH{=E~OZOqQ)ZndaI; z@|tQ$F_mvzb5oJ#@1AQbuJE5Dg50aTGHve2(x>m_39oO|H(NW7^o>{rIHb8UWM$&i*@De&lX;&CcgLPZOvpbV&HEnUY;uZ*Q zynNgALcw-uT}4b3FM{sPHw8D|S%G*FOCitx4+Mnemg>#s+mCgWhh6mNm`_A{(h{I z+EPtzaT8;1YD*y#K!Ss_yc-l4AQ9XM3!hICWzLz)fXtjND9Zpk(4yC2?vYajGiP zjPXf0lXI8^w33i9JTg~j1#reITZvrnGIECD!#liB6+xS0pUiv5w08(T(nB# z$rJYrGD$2cjf9w-oy;a0Mtb-Gy)6Hlz`rB#BLdR|HWJuC;J1D750P*zfH&m7Te014 zZ115T)5uG>+iDjqvE7=JT^wdbZadt*$|vuhLt{ zS}oc<2w{+FqRkDD2VJ7q=G}Nd;}6=~TC2CtC)(Z^F|!Yqo)i-(5tk5CrkM!@f8TPx zFjc@r3-RBvgWW0S5qwJ=pW_EEq$BfyLC|>0N3=p1=XjB8e+`41{cQp#2s}ezguvGc zyg}eFfnO%@4S*7khvH)WWv2+wj`ReD#5xCZrA<~XYMEn|oGX#E#z^@Y! z7DZu9G^~fc$Th8pY&_(1!;0Qx&*i7AoFA@H_NURSrGsIUe%?BMH~t@@eCOM3yku`2 z*8NuEnA_+JlVfs~8t{E0EZ{rsZadqe#C&Ij4ExTP4Br>DKNIjn0o7bJ&Fm_dO0P;_ znQAU`sWji&QBQTZe6=L**iLYuR_EBaF}OO?X17^-+~D7Cp%8t!%V zl2vKVq|MO#7RA%Ig3$XGt}01uA!${CR=*s2pE5azN#rGH8AIkejYL7Pz%Wh`XkTMh zF|q&=dY{Nx4u#(Lm_UMbKsX%KmhoJ^q+tQf1$n9*rj$^G-aj9N-aqdq>AR3TUlj?y z1oT4hpRb4<&Zp4(9vvg(WgvnA?Q1k57{4hUGy%&tuMl7ukAzI4AXzzZ@UNY0in?Q+6EMP8Hk`j`x=c1#_wTRG$3dKmhHiw62>!` zq*;6eij;}OA{N#Gu_4(Hy*~%V6CDBWJX0j~l=pc}D2V97GF%hoCIgjPrp-m3=Q`P{ z+G(|NP#6A~q2BcgDyHu(b1hTD2%jw%IFSg#XAi;#ESFu{(z>0t37V(HiSXGibFr_* zrV@~dzd`V9D*?rtqDI1Y?6_e?rUlyHX!Q@;$yoAPoni^or5>*xD8L#h%48ejF62EH zm-y||U=go!$aFXd7Sdga%(*m>xJZ5awY!w{X>d@wEE)BdX_F3RCpC!yrJ*1Sp)$5P z$ViylIeQv!(6bE1Qhm>YQ*Im!!=8J3{>Jk2!Sjdz1r*c#`CL5z7A#bIJr`QBP@Sax zs?!L}mM`MGVg2+n#EhRPkhdsSuUJ`Rtm&ztVrJCNI~RC~dh&E>{nL3Yt0t$dSu8RA zL+&{?KG{44u@S8eKA$4`TWNIzUcpeX_GQF6tdxCj7S5`ZVk7PBgq6WY;moP$jy`|% z^z*qxFC034;?Us}N5$dMr9ImDV~A*G#-LMtPB;6zcj;8L_6vC@Uqr0DSv*KYrt8HY zBRm}+2C$<+WfnVRn%4H&Zaxw~EF;MWfe!>Dg{@8Ynp(%vE2D)Oa>%uRgOKwC{tE$p zy$NHLI4_JUsRTgtf{fX}N$3oLGX&NHIHXzgmyNt!TwA0Kxb)1GGp6xai#dK!SV}iG z4)HoMl+mT)-o_6F!v7~@9s6%3mtA{`{@qG1casO_ak$sPh2+6%@}Ro{4p}0Rq=dxJ zBZRz(_BaF~SS10N_=~Xc0~8Q`e*nCg@cZ6<-5uD6ZzcJKYVr$i-PaEYzn=obCn9UM zA<);iFV8#f>aCU3wrYy5T>-twVB3S3U@~Z!5h%=ybaq(=5356PG%-d?Dj*p%4|A&) z`b5$z^>fD4J&t4QyE zy~2qEC6LQ_iIkXcdwRMt$(_4`Qet77+}MFTu^wUlRbZwkZVT&gBgn!@ZYf8`$Hoda zx&8~wZD96&jEFMMOks3|u(HWwwB#x%Q!_p{bwx)oL0mFgBEdP~Tj zW%jGk7Bl)ETCDjPMtuCY-oo;^;HZHH3p&d`{yX6a-wa(G;-*NnJ zZxjh>uCv@}r2J+ErYAE^UGR)Qs1#S-m$))Kd8MXH|VEExNvJp!+oW) zH7qlvb-!4wE|vRAdzxrP!(CDzcd%fmR;2hvakNgSwM=WV<)6D28_@61`h;7Ij-99f z*%hUI;x~v+pqVHXu`5l9kt2U_GMC4T7c%*Y3B=f@U3Q~1XZH8h%vO`6>bW-L)-%Qs#MH&|JvFDRpGq_MJ0V#4fy9SnVdpC&!C+foP8t z$PpMLu1y*jnfFF#5iSWk_;|TRuA+k5kKZHgP{ILJ+`$_p>El?Run{geQ8qzqvBwI-hPZ5# zyL70sG+SMoy>^V^39sB-O(UA{i|_UWxalqL_TNnJqNMmPBwwuhX%9&xNLECSm!C%L zUn1nocX7H~z=kx)J;FU69}Dm#3Smqm%VP6)f<|Ykh_svEO3Y!}m9$TSvLxT=; z!#1t9!FTiO>(*we#s-i#@$2vo{_{s{Q&U?}&g_e!n}0$h?1XaQ ziKeXHChHT{n~W7iXm2v;P2$ZF$y$n^B%mPs~@ztwMK89PqMx7`_`(DS#!PRUTdwg zZtSrh!v2LG98d^tI`%Kndl0tLj*0gmL)1Utdl2%r7{)|v(Ce{%8hy}X*BLFiUi8=v zFG{c%Sm ziFcOyLh`AqpZ1VMf@HK)x z5Y$*zj4VL73=$d3A(ugq2_#4dgu_8?8PDZQ8WzA@kf+LFN(qI_;JLtMFg(o79{Bnp z!VHI^@gms3Wl#%DBQJwcG~_a<(Fm7857Rh$&9-}rUc)kl%it@4%it?+lD-ScSE?ex zmw=wj;42l8!};Vg=+QAkUIrq_Wl*CLE`z3Y&;%^oy+VLtJQ6aEf@I}bvXswc4F8_$ zkS>r8=|Z4Gxl$1Tii)xcBwsuKefcGv*Q!R@hL~R;?!2;X0DR%dzcRkg#LcmE?l<9i-lT;MAh0o z9RE)L2FYo~9_m{6?9_Nsu#*>!4V8Apju9XyzFNF^b*A9hw8pg)1YRdlCh*$?eoR2z zv)&FLCHAxL)j|8egM{1zoIeD-(;AIN@3bVMF(~Q)`XkY3;F4gZy5A80ZbdrZ5dUsP zwz=WoTaneS{=L)oWHfpXZt~%O-sijzb&?lax}r09AdTVs0{@|!yLuq{H0T?{_XYk# xHFx)}C~c>24Br>{57pe6=wOt*F&e}71^z=d_en5Nmud`&VLVC 0 + + def test_constants_has_version_type(self): + """Test that constants.py includes VERSION_TYPE.""" + from src.utils import constants + + assert hasattr(constants, 'VERSION_TYPE') + assert constants.VERSION_TYPE in ['free', 'full'] + + def test_version_helper_functions(self): + """Test that version helper functions work correctly.""" + from src.utils import constants + + assert hasattr(constants, 'is_free_version') + assert hasattr(constants, 'is_full_version') + assert hasattr(constants, 'get_version_limits') + + # Functions should return appropriate types + assert isinstance(constants.is_free_version(), bool) + assert isinstance(constants.is_full_version(), bool) + assert isinstance(constants.get_version_limits(), dict) + + +class TestPackageIntegrity: + """Test that built packages have correct structure and no import issues.""" + + def test_free_package_excludes_correct_files(self): + """Test that free version package excludes the right files.""" + from pathlib import Path + free_package = Path("dist/text_texture_generator_v1.0.0_free.zip") + if not free_package.exists(): + pytest.skip("Free package not found - run build first") + + with zipfile.ZipFile(free_package, 'r') as zip_file: + file_list = zip_file.namelist() + + # Files that should be excluded from free version + excluded_files = [ + 'operators/preset_ops.py', + 'operators/utility_ops.py', + 'core/normal_maps.py' + ] + + for excluded_file in excluded_files: + matching_files = [f for f in file_list if excluded_file in f] + assert len(matching_files) == 0, f"Free version should not contain {excluded_file}" + + def test_full_package_includes_all_files(self): + """Test that full version package includes all files.""" + from pathlib import Path + full_package = Path("dist/text_texture_generator_v1.0.0.zip") + if not full_package.exists(): + pytest.skip("Full package not found - run build first") + + with zipfile.ZipFile(full_package, 'r') as zip_file: + file_list = zip_file.namelist() + + # Files that should be included in full version + required_files = [ + 'operators/preset_ops.py', + 'operators/utility_ops.py', + 'core/normal_maps.py' + ] + + for required_file in required_files: + matching_files = [f for f in file_list if required_file in f] + assert len(matching_files) > 0, f"Full version should contain {required_file}" + + def test_constants_contains_enabled_features_in_packages(self): + """Test that both packages have constants.py with ENABLED_FEATURES.""" + from pathlib import Path + + packages = [ + ("dist/text_texture_generator_v1.0.0_free.zip", "Free"), + ("dist/text_texture_generator_v1.0.0.zip", "Full") + ] + + for package_path, package_type in packages: + package = Path(package_path) + if not package.exists(): + continue # Skip if package doesn't exist + + with zipfile.ZipFile(package, 'r') as zip_file: + file_list = zip_file.namelist() + constants_files = [f for f in file_list if 'utils/constants.py' in f] + assert len(constants_files) > 0, f"{package_type} package should contain constants.py" + + # Read constants file and check for ENABLED_FEATURES + constants_file = constants_files[0] + constants_content = zip_file.read(constants_file).decode('utf-8') + assert 'ENABLED_FEATURES' in constants_content, f"{package_type} package constants.py should contain ENABLED_FEATURES" + + +class TestBuildSystemRobustness: + """Test that the build system handles edge cases properly.""" + + def test_build_system_handles_missing_git_tags(self): + """Test that build system works even without git tags.""" + from build.sync_version import get_latest_git_tag + + # Should return a version or None (both are acceptable) + version = get_latest_git_tag() + # Either a valid version string or None is acceptable + if version is not None: + assert isinstance(version, str) + assert len(version) > 0 + + def test_template_system_has_all_required_variables(self): + """Test that template system includes all required variables.""" + from build.sync_version import create_template_variables, load_config + + config = load_config() + variables = create_template_variables(config, "1.0.0", "full") + + required_vars = [ + 'VERSION', + 'VERSION_TYPE', + 'ENABLED_FEATURES' + ] + + for var in required_vars: + assert var in variables, f"Template variables should include {var}" \ No newline at end of file diff --git a/tests/unit/test_version_differentiation.py b/tests/unit/test_version_differentiation.py new file mode 100644 index 0000000..a56c035 --- /dev/null +++ b/tests/unit/test_version_differentiation.py @@ -0,0 +1,258 @@ +""" +Comprehensive tests for version differentiation in the Blender text texture generator addon. + +Tests the quadruple-layer detection system: +1. Template-generated constants (VERSION_TYPE, is_free_version(), ENABLED_FEATURES) +2. Runtime import detection flags (PRESETS_AVAILABLE, UTILITY_OPERATORS_AVAILABLE) +3. Version limits (get_version_limits() returns correct values) +4. UI helper functions (version display and feature gating) +""" + +import pytest +import sys +from unittest.mock import Mock, patch, MagicMock +from types import ModuleType + + +class TestVersionDetection: + """Test suite for version detection methods.""" + + def test_version_type_constant(self): + """Test VERSION_TYPE constant is properly set.""" + from src.utils.constants import VERSION_TYPE + assert VERSION_TYPE in ["free", "full"], f"VERSION_TYPE should be 'free' or 'full', got '{VERSION_TYPE}'" + + def test_is_free_version_function(self): + """Test is_free_version() function returns correct boolean.""" + from src.utils.constants import is_free_version, VERSION_TYPE + + result = is_free_version() + assert isinstance(result, bool), "is_free_version() should return a boolean" + + # Result should match VERSION_TYPE + expected = VERSION_TYPE == "free" + assert result == expected, f"is_free_version() returned {result}, expected {expected} based on VERSION_TYPE='{VERSION_TYPE}'" + + def test_is_full_version_function(self): + """Test is_full_version() function returns correct boolean.""" + from src.utils.constants import is_full_version, VERSION_TYPE + + result = is_full_version() + assert isinstance(result, bool), "is_full_version() should return a boolean" + + # Result should match VERSION_TYPE + expected = VERSION_TYPE == "full" + assert result == expected, f"is_full_version() returned {result}, expected {expected} based on VERSION_TYPE='{VERSION_TYPE}'" + + def test_enabled_features_list(self): + """Test ENABLED_FEATURES contains appropriate features for version.""" + from src.utils.constants import ENABLED_FEATURES, VERSION_TYPE + + assert isinstance(ENABLED_FEATURES, list), "ENABLED_FEATURES should be a list" + assert len(ENABLED_FEATURES) > 0, "ENABLED_FEATURES should not be empty" + + # Basic features should always be present + assert "basic" in ENABLED_FEATURES, "Basic features should always be enabled" + assert "preview" in ENABLED_FEATURES, "Preview features should always be enabled" + + if VERSION_TYPE == "free": + # Free version should have limited features - only basic and preview + expected_free_features = ["basic", "preview"] + for feature in ENABLED_FEATURES: + assert feature in expected_free_features, f"Free version should only have basic features, found '{feature}'" + else: + # Full version should have additional features beyond basic and preview + assert len(ENABLED_FEATURES) > 2, "Full version should have more than just basic and preview features" + + def test_get_version_limits_function(self): + """Test get_version_limits() returns correct technical limits.""" + from src.utils.constants import get_version_limits, VERSION_TYPE + + limits = get_version_limits() + assert isinstance(limits, dict), "get_version_limits() should return a dictionary" + + # Check required keys + required_keys = ["max_texture_size", "max_concurrent_operations"] + for key in required_keys: + assert key in limits, f"Missing required key '{key}' in version limits" + + # Check value types + assert isinstance(limits["max_texture_size"], int), "max_texture_size should be an integer" + assert isinstance(limits["max_concurrent_operations"], int), "max_concurrent_operations should be an integer" + + # Check version-specific limits + if VERSION_TYPE == "free": + assert limits["max_texture_size"] == 1024, f"Free version should have 1024px limit, got {limits['max_texture_size']}" + assert limits["max_concurrent_operations"] == 1, f"Free version should have 1 concurrent operation, got {limits['max_concurrent_operations']}" + else: + assert limits["max_texture_size"] == 4096, f"Full version should have 4096px limit, got {limits['max_texture_size']}" + assert limits["max_concurrent_operations"] == 4, f"Full version should have 4 concurrent operations, got {limits['max_concurrent_operations']}" + + +class TestRuntimeImportDetection: + """Test suite for runtime import detection flags.""" + + def test_presets_available_flag(self): + """Test PRESETS_AVAILABLE flag is properly set based on import success.""" + from src import PRESETS_AVAILABLE + assert isinstance(PRESETS_AVAILABLE, bool), "PRESETS_AVAILABLE should be a boolean" + + def test_utility_operators_available_flag(self): + """Test UTILITY_OPERATORS_AVAILABLE flag is properly set based on import success.""" + from src import UTILITY_OPERATORS_AVAILABLE + assert isinstance(UTILITY_OPERATORS_AVAILABLE, bool), "UTILITY_OPERATORS_AVAILABLE should be a boolean" + + +class TestFeatureAvailability: + """Test suite for feature availability and gating.""" + + def test_feature_gating_based_on_version(self): + """Test that features are properly gated based on version type.""" + from src.utils.constants import is_free_version, ENABLED_FEATURES + + if is_free_version(): + # Free version should only have basic and preview features + expected_features = ["basic", "preview"] + assert set(ENABLED_FEATURES) == set(expected_features), f"Free version should only have {expected_features}" + else: + # Full version should have more features + assert len(ENABLED_FEATURES) > 2, "Full version should have more than just basic features" + assert "basic" in ENABLED_FEATURES, "Full version should include basic features" + assert "preview" in ENABLED_FEATURES, "Full version should include preview features" + + def test_runtime_import_detection_flags_exist(self): + """Test that runtime import detection flags exist.""" + try: + from src import PRESETS_AVAILABLE, UTILITY_OPERATORS_AVAILABLE + assert isinstance(PRESETS_AVAILABLE, bool), "PRESETS_AVAILABLE should be a boolean" + assert isinstance(UTILITY_OPERATORS_AVAILABLE, bool), "UTILITY_OPERATORS_AVAILABLE should be a boolean" + except ImportError as e: + pytest.fail(f"Runtime detection flags should be available: {e}") + + +class TestUIComponents: + """Test suite for UI component version display.""" + + def test_get_version_badge_text(self): + """Test get_version_badge_text() returns appropriate badge text.""" + from src.utils.constants import get_version_badge_text, is_free_version + + badge_text = get_version_badge_text() + assert isinstance(badge_text, str), "get_version_badge_text() should return a string" + assert len(badge_text) > 0, "Badge text should not be empty" + + if is_free_version(): + assert "FREE" in badge_text.upper(), "Free version badge should contain 'FREE'" + else: + assert "PRO" in badge_text.upper() or "FULL" in badge_text.upper(), "Full version badge should contain 'PRO' or 'FULL'" + + def test_should_show_feature_lock(self): + """Test should_show_feature_lock() returns correct values for features.""" + from src.utils.constants import should_show_feature_lock, is_free_version + + # Test premium features from the actual implementation + premium_features = ["presets", "utility_operators", "image_overlays", "batch_processing", "advanced_positioning"] + for feature in premium_features: + result = should_show_feature_lock(feature) + assert isinstance(result, bool), f"should_show_feature_lock('{feature}') should return boolean" + + if is_free_version(): + assert result == True, f"Feature lock should be shown for '{feature}' in free version" + else: + assert result == False, f"Feature lock should not be shown for '{feature}' in full version" + + def test_get_upgrade_hint_text(self): + """Test get_upgrade_hint_text() returns appropriate upgrade messages.""" + from src.utils.constants import get_upgrade_hint_text, is_free_version + + # The function requires a feature_name parameter + feature_name = "Test Feature" + hint_text = get_upgrade_hint_text(feature_name) + assert isinstance(hint_text, str), "get_upgrade_hint_text() should return a string" + + if is_free_version(): + assert len(hint_text) > 0, "Free version should have upgrade hint text" + assert feature_name in hint_text, "Upgrade hint should mention the feature name" + assert "Pro" in hint_text, "Upgrade hint should mention Pro version" + else: + assert len(hint_text) == 0, "Full version should have empty upgrade hint text" + + def test_get_version_info_details(self): + """Test get_version_info_details() returns comprehensive version information.""" + from src.utils.constants import get_version_info_details, is_free_version, get_version_limits + + info_details = get_version_info_details() + assert isinstance(info_details, dict), "get_version_info_details() should return a dictionary" + assert len(info_details) > 0, "Version info details should not be empty" + + +class TestIntegrationScenarios: + """Test suite for cross-system integration validation.""" + + def test_template_and_runtime_consistency(self): + """Test that template-generated constants and runtime flags are consistent.""" + from src.utils.constants import is_free_version, ENABLED_FEATURES + from src import PRESETS_AVAILABLE, UTILITY_OPERATORS_AVAILABLE + + # In a properly built system, runtime availability should align with template configuration + if is_free_version(): + # Free version should have limited features in template + expected_features = ["basic", "preview"] + assert set(ENABLED_FEATURES) == set(expected_features), f"Template should only enable basic features in free version, got {ENABLED_FEATURES}" + else: + # Full version should have more features enabled in template + assert len(ENABLED_FEATURES) > 2, "Template should enable more features in full version" + assert "basic" in ENABLED_FEATURES, "Template should enable basic features in full version" + assert "preview" in ENABLED_FEATURES, "Template should enable preview features in full version" + + def test_mock_operators_dont_crash(self): + """Test that mock operators are properly implemented and don't crash.""" + # This test ensures that when premium features are missing, + # the system gracefully handles it with mock implementations + + # Test that we can import the main module without crashes + try: + import src + assert True, "Main module import should not crash" + except Exception as e: + pytest.fail(f"Main module import crashed: {e}") + + # Test that runtime flags are properly set + assert hasattr(src, 'PRESETS_AVAILABLE'), "Runtime should set PRESETS_AVAILABLE flag" + assert hasattr(src, 'UTILITY_OPERATORS_AVAILABLE'), "Runtime should set UTILITY_OPERATORS_AVAILABLE flag" + + +class TestVersionDifferentiationScenarios: + """Test specific free vs full version difference scenarios.""" + + def test_free_version_complete_scenario(self): + """Test complete free version scenario with all restrictions.""" + from src.utils.constants import is_free_version, get_version_limits, ENABLED_FEATURES + + if is_free_version(): + # Feature restrictions - free version should only have basic features + expected_features = ["basic", "preview"] + assert set(ENABLED_FEATURES) == set(expected_features), f"Free version should only have {expected_features}" + + # Technical limitations + limits = get_version_limits() + assert limits["max_texture_size"] == 1024, "Should have 1024px texture limit" + assert limits["max_concurrent_operations"] == 1, "Should have 1 concurrent operation limit" + + def test_full_version_complete_scenario(self): + """Test complete full version scenario with all features enabled.""" + from src.utils.constants import is_free_version, is_full_version, get_version_limits, ENABLED_FEATURES + + if not is_free_version(): + # Version detection + assert is_full_version() == True, "Should detect full version" + + # Full version should have more features than free + assert len(ENABLED_FEATURES) > 2, "Full version should have more than just basic features" + assert "basic" in ENABLED_FEATURES, "Full version should include basic features" + assert "preview" in ENABLED_FEATURES, "Full version should include preview features" + + # Generous technical limits + limits = get_version_limits() + assert limits["max_texture_size"] == 4096, "Should have 4096px texture limit" + assert limits["max_concurrent_operations"] == 4, "Should have 4 concurrent operations limit" \ No newline at end of file

    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 delta 57653 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 delta 36051 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 0: - print(f"[TTG DEBUG] Migrated {migration_result['migrated_count']} presets during registration") + pass + print(f"Migrated {migration_result['migrated_count']} presets during registration") # Initialize presets using the simplified system initialize_presets() - print("[TTG DEBUG] Preset initialization completed successfully") + print("Preset initialization completed successfully") except Exception as e: - print(f"[TTG DEBUG] Error during preset initialization: {e}") + pass + print(f"Error during preset initialization: {e}") # Don't fail registration if preset initialization fails import traceback traceback.print_exc() @@ -162,47 +282,63 @@ def register(): # Add handler to reload presets when blend file changes @bpy.app.handlers.persistent def on_file_load(dummy): + pass """Reload presets when a blend file is loaded""" try: - print("[TTG] Reloading presets after file load...") + pass + + print("Reloading presets after file load...") # Use reliable synchronous refresh instead of timer success = refresh_presets_sync() if success: - print("[TTG] Presets reloaded successfully after file load") + pass + print("Presets reloaded successfully after file load") else: - print("[TTG] Warning: Preset reload failed after file load") + pass + print("Warning: Preset reload failed after file load") except Exception as e: - print(f"[TTG] Error reloading presets: {e}") + pass + print(f"Error reloading presets: {e}") import traceback traceback.print_exc() # Register the file load handler if on_file_load not in bpy.app.handlers.load_post: + pass bpy.app.handlers.load_post.append(on_file_load) - print("[TTG DEBUG] ========== ADDON REGISTRATION END ==========") def unregister(): + pass # Clean up preview try: + pass if bpy.context.scene: + pass props = bpy.context.scene.text_texture_props if props and props.preview_image: + pass bpy.data.images.remove(props.preview_image) except Exception: pass + pass if "TextTexturePreview" in bpy.data.images: + pass bpy.data.images.remove(bpy.data.images["TextTexturePreview"]) # Remove file load handler try: + pass for handler in bpy.app.handlers.load_post[:]: + pass if handler.__name__ == 'on_file_load' and 'TTG' in str(handler): + pass bpy.app.handlers.load_post.remove(handler) except Exception as e: + pass print(f"Error removing file load handler: {e}") # Remove from menus @@ -212,12 +348,26 @@ def unregister(): bpy.types.NODE_MT_node.remove(menu_func_node_editor) if hasattr(bpy.types, 'IMAGE_MT_image'): + pass bpy.types.IMAGE_MT_image.remove(menu_func_image) - for cls in reversed(classes): - bpy.utils.unregister_class(cls) + # Only unregister classes that were successfully registered + + for cls in reversed(_registered_classes): + pass + try: + pass + bpy.utils.unregister_class(cls) + except Exception as e: + pass + print(f"Failed to unregister {cls.__name__}: {e}") + # Continue with other classes even if one fails + + # Clear the registered classes list + _registered_classes.clear() del bpy.types.Scene.text_texture_props if __name__ == "__main__": + pass register() \ No newline at end of file diff --git a/src/__pycache__/__init__.cpython-311.pyc b/src/__pycache__/__init__.cpython-311.pyc index a00fb7516efd8697d7f7fce7774a68638b93bf47..71d5f3e520d755e7469f6fa6f17747678701a46f 100644 GIT binary patch literal 12324 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 literal 10255 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 delta 405 zcmaKmze~eF6vyA?@~chK#!aarjvbstbg0fwE^ZGb6cc0MULw~BPU^o-w zVnB#lYzRgM8cb_0IGJdYh1T4g!X^hDa?zy*HmHeB^3Wq6eF`v`_gJDuZERDBAq{Uj zA%hfR`>NPX9lnh3*NjKjHPx@u(%>3uJCKfstI^QVyGyTn(E z$rITxvm%d@Jk6B(4VwC8a$ZKqSy^Uz`cI;c^%bVA`M{|xj*`Kkkkvb152sEP^@WI{ zTbQXHs2B0dwzu3S}bOx;08vF41i+ThHrLBj{O{R~@wPi%)>OYUT^P C4O(6R diff --git a/src/core/__pycache__/generation_engine.cpython-311.pyc b/src/core/__pycache__/generation_engine.cpython-311.pyc index 1e0491b8ff07eef9afa328588b41d2c4594275e7..cdf83cc1ace315fa7779fccc2d3ec2f2135d3e82 100644 GIT binary patch literal 21555 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 F{{enIuU`NF diff --git a/src/core/__pycache__/normal_maps.cpython-311.pyc b/src/core/__pycache__/normal_maps.cpython-311.pyc index 31969aa13d95b7dbcab51e019d7842b93c4ce3bc..37462944ca86c0fda7d15bec12070cf3d176fa71 100644 GIT binary patch delta 1502 zcmah|O>7%Q6rR~1+Z%g#y@~VV{H`Era19Zn3ayGlNCice5)|44KT1`c&c@wzy*6Vz zZlhg7p`d^wk!UJ4;!rAtf>0ua#2JYkI3TrBKpd=9D)qu42c$*f;2XSM*KMizdAsw? z+i%|cX7mCE zs%687Y8c`x(H4Uc;kPhC+t8Z!9DD-MqfT@&*7}^|5OQO#n)FDn#8$vVHFT3F0Q9 z1Vb$igq+ylOYkB(`>LzKI%Oy6B#F2Tp&xLL=4eD@oBT)o={19ugfwVsq@BGbWXGZ3 zg2HTv#R-&M)!}IrG?X}U6-NbB9ffoRS|qg$1tpzqUwBS_lo^*(5ZAQ~HFQ+c#TLW~ z+`+yOM{y^+B|gjaNE&yuqlAHeD|2XgVN{C&H>}JSX&FMLV;6OYRtgmTsAcoyR z358qT>kc_XH~Ye!`6-%OLGIu?g9M#HB=lU`pS6r!+0ae9 zT*+CMZdUY_YT2~&$Mup~x=%Mn;Zr|h88$KKXjz}VT)9xRb;DjVY0)kicI8PO=Ba|( zgP{4UO)OZ!yrW$W1evO!=Q6+%D%Wz+Dq02n1vrs}bT&qZPu}a;cj{-HA7h2&@JLiT>LY+hB}Ex{fTrX($@7oJ14=Z0f0(~eY(IUu+ab7@yg_y_@Up?%nn8(LZ+!6iI1OpfoLDX^PRd7TO{Y#0U@D+vV=y{yDeT z!n$XTXo5-%WK1MM;>8EVz=P-ukCb3kB$)57|b7C6H*8+TZs>L<5h8xOR=28B75!>LRmwuMc^a8%(siZ`)jKf6AG&+@R2* zb>8YCH@NfsVkJU$@rka5O4O39h!xS0NkhKNSy3yhA(H0b+jjI~(vwE)$pkEaHOlf1 z087?3Y2BpNH+Y<`*J+h+x|L>GRKgZdD=vpXMYh5e!yeY24Xm=(%d}{HCheRSXIO1@ z>-&LkOWS|H>y_H6j)aK-u)F!r$Yo1-7#kxJa-wbhD0?N+!Ky}OdI64f3z zX(`aY!dK#N#x?#J&|GU7E`ziDt6&)p^qUJ)dS1t>p)0DAFW{V-RdQ-kIfV;3Ma?Uj zoSM?NmlQQyOsh&Vt>*K3Rwsh3<`4m@khUd02uq2tG@mMNFaXFPtB#bP!?^|MG# z0nK9y=|iJr357`#wXH}rAPzHN)&Tbf=Ne6j&@IQOYbkvII=);%)6LDE7%!;VWI0P~ ztSKqRuIG&sX8&5(-;8?>uM2WBF43}J4y*u>qQ~j@W#ZvqbaAyjk;!9G)*88_CyfFw zIXo?zxz7`2tre`*k2N4!$Q83X(^)DfSvgOYv)Oazj#-cIix?O1KW?;z|K6MZ%0REf zFU%aq?6Zme6dWO6&K1u&LJ?>325$ZOcT|Nvn0<&Hh#i=6;GzR{JVZB23I%ezrH9;Y z5y+1%<+MdVl5c^AFA0VARkXu?+FV85_WJw{Nw!?Lizu*p^u(Hx$~BD|D%MfaM#Y97 zh~bN4mzI7C_1U35azC_dr|Ud*qr;Y`==`#?xl*j5={lOW(KPewx)ObV;=%-x#BE&< z#HigmedhoLI&0!gU7WGS8L~(GA>j`mYyb)uCLeTkUm3Z4;-eD{AV_qR!X-!)>10=k znvY}nBk93NTj?7ejS!b@w58)~>F?yZY^ZE-L8UiviZ3LdSY zu{s*F(OAPzGID>P5FDj3f}@_mK8bEpAm7PDF@PCMpP>R0+2 diff --git a/src/core/__pycache__/text_fitting.cpython-311.pyc b/src/core/__pycache__/text_fitting.cpython-311.pyc index 9b611644423174a08a1b528e2246f27cadfd1f4c..51b67c66b7cd60865afa880c841aa3599ec121ae 100644 GIT binary patch delta 5510 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 literal 14690 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 BN~8b) diff --git a/src/core/__pycache__/text_processor.cpython-311.pyc b/src/core/__pycache__/text_processor.cpython-311.pyc index db7bca9cd9ebd88e534461337901075d829a1f78..1aba4a39fadf2f0c353d732a01589ec78bf15d32 100644 GIT binary patch delta 5819 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 0: + pass # Add spacing after prepend text text_parts_with_spacing.append(part + " " * prepend_spaces) elif i == len(full_text_parts) - 1 and append_spaces > 0: # Add spacing before append text text_parts_with_spacing.append(" " * append_spaces + part) else: + pass text_parts_with_spacing.append(part) full_text = " ".join(text_parts_with_spacing) layout_mode = "horizontal" else: + pass # Vertical layout - keep parts separate for individual positioning full_text = "\n".join(full_text_parts) layout_mode = "vertical" - print(f"[TTG DEBUG] Step 2c: Full text composed: '{full_text}' (layout: {layout_mode})") # Determine font size (with text fitting if enabled) font_size = props.font_size if props.enable_text_fitting: - print(f"[TTG DEBUG] Step 2d: Text fitting enabled, calculating optimal size") + pass # Try to load font for sizing try: + pass if props.use_custom_font and props.custom_font_path: + pass test_font_path = props.custom_font_path elif props.font_path != "default": test_font_path = props.font_path else: + pass test_font_path = None # Binary search for optimal font size @@ -121,9 +129,12 @@ def generate_texture_image(props, width, height): optimal_size = min_size for test_size in range(min_size, max_size + 1, 2): # Step by 2 for performance try: + pass if test_font_path: + pass test_font = ImageFont.truetype(test_font_path, test_size) else: + pass test_font = ImageFont.load_default() bbox = draw.textbbox((0, 0), full_text, font=test_font) @@ -131,35 +142,38 @@ def generate_texture_image(props, width, height): test_height = bbox[3] - bbox[1] if test_width <= target_width and test_height <= target_height: + pass optimal_size = test_size else: + pass break except (OSError, IOError): + pass break font_size = optimal_size - print(f"[TTG DEBUG] Step 2d: Optimal font size calculated: {font_size}") except Exception as e: - print(f"[TTG DEBUG] Step 2d: Font fitting failed, using default size: {e}") + pass font_size = props.font_size # Load final font try: + pass if props.use_custom_font and props.custom_font_path: + pass font = ImageFont.truetype(props.custom_font_path, font_size) - print(f"[TTG DEBUG] Step 2e: Using custom font: {props.custom_font_path}, size: {font_size}") elif props.font_path != "default": font = ImageFont.truetype(props.font_path, font_size) - print(f"[TTG DEBUG] Step 2e: Using system font: {props.font_path}, size: {font_size}") else: + pass font = ImageFont.load_default() - print(f"[TTG DEBUG] Step 2e: Using default font, size: {font_size}") except (OSError, IOError) as e: - print(f"[TTG DEBUG] Step 2e: Font loading failed, using default: {e}") + pass font = ImageFont.load_default() # Calculate text position and draw if layout_mode == "vertical" and props.prepend_append_layout == 'VERTICAL': + pass # Draw each part separately for vertical layout y_offset = 0 total_height = 0 @@ -167,6 +181,7 @@ def generate_texture_image(props, width, height): # Calculate total height first part_heights = [] for part in full_text_parts: + pass bbox = draw.textbbox((0, 0), part, font=font) part_height = bbox[3] - bbox[1] part_heights.append(part_height) @@ -174,6 +189,7 @@ def generate_texture_image(props, width, height): # Add spacing between parts if len(full_text_parts) > 1: + pass spacing = font_size // 4 # 25% of font size as line spacing total_height += spacing * (len(full_text_parts) - 1) @@ -185,18 +201,19 @@ def generate_texture_image(props, width, height): text_color = tuple(int(c * 255) for c in props.text_color[:4]) for i, part in enumerate(full_text_parts): + pass bbox = draw.textbbox((0, 0), part, font=font) part_width = bbox[2] - bbox[0] x = (width - part_width) // 2 # Center horizontally draw.text((x, current_y), part, font=font, fill=text_color) - print(f"[TTG DEBUG] Step 2f: Drew part '{part}' at ({x}, {current_y})") current_y += part_heights[i] if i < len(full_text_parts) - 1: # Add spacing except after last part current_y += font_size // 4 else: + pass # Horizontal layout or single text text_bbox = draw.textbbox((0, 0), full_text, font=font) text_width = text_bbox[2] - text_bbox[0] @@ -209,57 +226,85 @@ def generate_texture_image(props, width, height): # Draw text text_color = tuple(int(c * 255) for c in props.text_color[:4]) draw.text((x, y), full_text, font=font, fill=text_color) - print(f"[TTG DEBUG] Step 2f: Full text '{full_text}' drawn at ({x}, {y}) with size {font_size}") + print(f"DEBUG: Text drawn at position ({x}, {y}) with color {text_color}") else: - print(f"[TTG DEBUG] Step 2d: No text provided, using solid background") + pass + print("DEBUG: No text provided, using solid background") # Convert PIL image to Blender format (flip vertically to fix mirroring) - print(f"[TTG DEBUG] Step 2f: Converting PIL to Blender format with vertical flip") pil_img = pil_img.transpose(Image.FLIP_TOP_BOTTOM) # Fix mirroring issue pil_pixels = list(pil_img.getdata()) blender_pixels = [] for r, g, b, a in pil_pixels: + pass blender_pixels.extend([r/255.0, g/255.0, b/255.0, a/255.0]) + print(f"DEBUG: Converting {len(pil_pixels)} PIL pixels to Blender format") blender_img.pixels[:] = blender_pixels - print(f"[TTG DEBUG] Step 2g: Pixel data conversion completed with flip correction") - - except ImportError: - print(f"[TTG DEBUG] Step 2: PIL not available, falling back to solid color") - # Fallback to solid color if PIL is not available - pixel_pattern = [props.background_color[0], props.background_color[1], props.background_color[2], 1.0] + print(f"DEBUG: Texture generation completed successfully") + except ImportError as e: + pass + # PIL not available - should not happen with proper Blender manifest dependencies + print(f"ERROR: PIL dependency not available despite manifest declaration: {e}") + print(f"ERROR: Check if Blender properly installed addon dependencies from manifest") + # Create a visible yellow error pattern to indicate dependency issue + error_pattern = [1.0, 1.0, 0.0, 1.0] # Yellow color to indicate dependency problem total_pixels = width * height - blender_img.pixels[:] = pixel_pattern * total_pixels + blender_img.pixels[:] = error_pattern * total_pixels + print(f"DEBUG: Yellow error texture created (dependency issue)") + + except ImportError as e: + pass + # PIL installation or import failed - create visible error pattern + print(f"ERROR: PIL import error: {e}") + # Create a visible red error pattern instead of black + error_pattern = [1.0, 0.0, 0.0, 1.0] # Red color to indicate PIL failure + total_pixels = width * height + blender_img.pixels[:] = error_pattern * total_pixels + print(f"DEBUG: Red error texture created (PIL import error)") + except Exception as e: + pass + print(f"ERROR: Texture generation failed: {e}") + import traceback + traceback.print_exc() + # Create a visible error pattern instead of black + error_pattern = [1.0, 0.0, 1.0, 1.0] # Magenta color for general errors + total_pixels = width * height + blender_img.pixels[:] = error_pattern * total_pixels + print(f"DEBUG: Magenta error texture created (general error)") - print(f"[TTG DEBUG] Step 3: Packing image...") blender_img.pack() - print(f"[TTG DEBUG] Step 3: Image packing completed") # Generate normal map if enabled normal_map_img = None if props.generate_normal_map: - print(f"[TTG DEBUG] Step 4: Normal map generation enabled, starting...") - from ..core.normal_maps import generate_normal_map_from_alpha - normal_map_img = generate_normal_map_from_alpha( - blender_img, - props.normal_map_strength, - props.normal_map_blur_radius, - props.normal_map_invert - ) - print(f"[TTG DEBUG] Step 4: Normal map generation completed") + pass + try: + pass + from ..core.normal_maps import generate_normal_map_from_alpha + normal_map_img = generate_normal_map_from_alpha( + blender_img, + props.normal_map_strength, + props.normal_map_blur_radius, + props.normal_map_invert + ) + except ImportError: + pass + normal_map_img = None else: - print(f"[TTG DEBUG] Step 4: Normal map generation disabled, skipping") + pass + print("Normal map generation disabled, skipping") - print(f"[TTG DEBUG] All steps completed successfully") return blender_img, normal_map_img except Exception as e: - print(f"[TTG ERROR] Failed to generate texture image: {e}") + pass import traceback traceback.print_exc() return None, None def generate_preview(props, preview_width=512, preview_height=512): + pass """ Generate a preview version of the texture at lower resolution. @@ -272,20 +317,20 @@ def generate_preview(props, preview_width=512, preview_height=512): Blender image object or None on error """ try: - print(f"[TTG DEBUG] Generating preview at {preview_width}x{preview_height}px") + pass # Generate at preview resolution result = generate_texture_image(props, preview_width, preview_height) if result and result[0]: + pass preview_img = result[0] - print(f"[TTG DEBUG] Preview generation completed successfully") return preview_img else: - print(f"[TTG ERROR] Preview generation failed") + pass return None except Exception as e: - print(f"[TTG ERROR] Failed to generate preview: {e}") + pass import traceback traceback.print_exc() return None diff --git a/src/core/normal_maps.py b/src/core/normal_maps.py index 646e7a7..25ec0d6 100644 --- a/src/core/normal_maps.py +++ b/src/core/normal_maps.py @@ -1,25 +1,35 @@ def generate_normal_map_from_alpha(img, strength=1.0, blur_radius=1.0, invert=False): + pass """Generate a normal map from an image's alpha channel using height-based algorithm""" try: - from PIL import Image, ImageFilter + pass + try: + pass + from PIL import Image, ImageFilter + except ImportError: + pass + raise ImportError( + "PIL (Pillow) is required for normal map generation. " + "Please install it using: pip install Pillow in Blender's Python environment, " + "or use Blender's bundled Python if available." + ) import numpy as np - print(f"[Normal Map] Starting generation with strength={strength}, blur={blur_radius}, invert={invert}") # Extract alpha channel as height map if img.mode != 'RGBA': + pass img = img.convert('RGBA') # Get alpha channel alpha_channel = img.split()[3] # Alpha is the 4th channel width, height = alpha_channel.size - print(f"[Normal Map] Processing {width}x{height} alpha channel") # Apply blur if specified if blur_radius > 0: + pass alpha_channel = alpha_channel.filter(ImageFilter.GaussianBlur(radius=blur_radius)) - print(f"[Normal Map] Applied blur with radius {blur_radius}") # Convert to numpy array for gradient calculations height_map = np.array(alpha_channel, dtype=np.float32) / 255.0 @@ -41,14 +51,15 @@ def generate_normal_map_from_alpha(img, strength=1.0, blur_radius=1.0, invert=Fa grad_y = np.zeros_like(height_map) for i in range(height): + pass for j in range(width): + pass # Extract 3x3 neighborhood neighborhood = padded_height[i:i+3, j:j+3] # Apply Sobel operators grad_x[i, j] = np.sum(neighborhood * sobel_x) grad_y[i, j] = np.sum(neighborhood * sobel_y) - print(f"[Normal Map] Calculated gradients") # Convert gradients to normal vectors # Normal map RGB values are calculated as: @@ -62,9 +73,9 @@ def generate_normal_map_from_alpha(img, strength=1.0, blur_radius=1.0, invert=Fa # Apply invert if specified if invert: + pass grad_x = -grad_x grad_y = -grad_y - print(f"[Normal Map] Applied inversion") # Calculate normal map channels # Red channel: X gradient mapped to 0-1 @@ -79,24 +90,24 @@ def generate_normal_map_from_alpha(img, strength=1.0, blur_radius=1.0, invert=Fa grad_z = np.sqrt(np.maximum(0, 1.0 - grad_magnitude_sq)) normal_b = (grad_z * 255).astype(np.uint8) - print(f"[Normal Map] Calculated normal vectors") # Create the normal map image normal_map = Image.new('RGB', (width, height)) # Combine channels for y in range(height): + pass for x in range(width): + pass r = int(normal_r[y, x]) g = int(normal_g[y, x]) b = int(normal_b[y, x]) normal_map.putpixel((x, y), (r, g, b)) - print(f"[Normal Map] Normal map generation completed successfully") return normal_map except Exception as e: - print(f"[Normal Map] Error generating normal map: {e}") + pass import traceback traceback.print_exc() return None \ No newline at end of file diff --git a/src/core/text_fitting.py b/src/core/text_fitting.py index b35b92a..f66f984 100644 --- a/src/core/text_fitting.py +++ b/src/core/text_fitting.py @@ -1,13 +1,15 @@ def calculate_available_text_area(width, height, overlays): + pass """Calculate available area for text placement, avoiding overlay positions""" try: - print(f"[Text Fitting] Calculating available area for {width}x{height} with {len(overlays)} overlays") + pass # Create a boolean mask for available areas (True = available, False = blocked) available_mask = [[True for _ in range(width)] for _ in range(height)] # Mark overlay positions as unavailable for overlay in overlays: + pass overlay_x = int(overlay.get('x', 0)) overlay_y = int(overlay.get('y', 0)) overlay_width = int(overlay.get('width', 0)) @@ -22,8 +24,11 @@ def calculate_available_text_area(width, height, overlays): end_y = min(height, overlay_y + overlay_height + padding) for y in range(start_y, end_y): + pass for x in range(start_x, end_x): + pass if 0 <= y < height and 0 <= x < width: + pass available_mask[y][x] = False # Find largest available rectangular area @@ -34,11 +39,15 @@ def calculate_available_text_area(width, height, overlays): heights = [0] * width for row in range(height): + pass # Update heights array for col in range(width): + pass if available_mask[row][col]: + pass heights[col] += 1 else: + pass heights[col] = 0 # Find largest rectangle in histogram @@ -46,10 +55,10 @@ def calculate_available_text_area(width, height, overlays): area = rect[2] * rect[3] # width * height if area > max_area: + pass max_area = area best_rect = (rect[0], row - rect[3] + 1, rect[2], rect[3]) - print(f"[Text Fitting] Found best rectangle: x={best_rect[0]}, y={best_rect[1]}, w={best_rect[2]}, h={best_rect[3]}, area={max_area}") return { 'x': best_rect[0], @@ -60,24 +69,28 @@ def calculate_available_text_area(width, height, overlays): } except Exception as e: - print(f"[Text Fitting] Error calculating available text area: {e}") + pass import traceback traceback.print_exc() return {'x': 0, 'y': 0, 'width': width, 'height': height, 'area': width * height} def largest_rectangle_in_histogram(heights): + pass """Find the largest rectangle in a histogram using stack-based algorithm""" stack = [] max_area = 0 best_rect = (0, 0, 0, 0) # x, y, width, height for i, h in enumerate(heights): + pass start = i while stack and stack[-1][1] > h: + pass idx, height = stack.pop() area = height * (i - idx) if area > max_area: + pass max_area = area best_rect = (idx, 0, i - idx, height) start = idx @@ -86,20 +99,23 @@ def largest_rectangle_in_histogram(heights): # Process remaining heights in stack while stack: + pass idx, height = stack.pop() area = height * (len(heights) - idx) if area > max_area: + pass max_area = area best_rect = (idx, 0, len(heights) - idx, height) return best_rect def calculate_text_position(text, font, available_area, alignment='center'): + pass """Calculate optimal text position within available area""" try: + pass from PIL import ImageFont, ImageDraw, Image - print(f"[Text Fitting] Calculating text position for alignment: {alignment}") # Create temporary image to measure text temp_img = Image.new('RGBA', (1, 1)) @@ -115,10 +131,10 @@ def calculate_text_position(text, font, available_area, alignment='center'): area_width = available_area['width'] area_height = available_area['height'] - print(f"[Text Fitting] Text size: {text_width}x{text_height}, Available area: {area_width}x{area_height}") # Calculate position based on alignment if alignment == 'center': + pass x = area_x + (area_width - text_width) // 2 y = area_y + (area_height - text_height) // 2 elif alignment == 'top-left': @@ -146,6 +162,7 @@ def calculate_text_position(text, font, available_area, alignment='center'): x = area_x + area_width - text_width y = area_y + area_height - text_height else: + pass # Default to center x = area_x + (area_width - text_width) // 2 y = area_y + (area_height - text_height) // 2 @@ -154,7 +171,6 @@ def calculate_text_position(text, font, available_area, alignment='center'): x = max(area_x, min(x, area_x + area_width - text_width)) y = max(area_y, min(y, area_y + area_height - text_height)) - print(f"[Text Fitting] Calculated position: ({x}, {y})") return { 'x': x, @@ -164,7 +180,7 @@ def calculate_text_position(text, font, available_area, alignment='center'): } except Exception as e: - print(f"[Text Fitting] Error calculating text position: {e}") + pass import traceback traceback.print_exc() return { @@ -175,11 +191,12 @@ def calculate_text_position(text, font, available_area, alignment='center'): } def find_optimal_font_size(text, font_path, max_width, max_height, min_size=8, max_size=200): + pass """Find the largest font size that fits within the given constraints""" try: + pass from PIL import ImageFont, ImageDraw, Image - print(f"[Text Fitting] Finding optimal font size for constraints: {max_width}x{max_height}") # Binary search for optimal font size low, high = min_size, max_size @@ -189,39 +206,43 @@ def find_optimal_font_size(text, font_path, max_width, max_height, min_size=8, m draw = ImageDraw.Draw(temp_img) while low <= high: + pass mid = (low + high) // 2 try: + pass font = ImageFont.truetype(font_path, mid) bbox = draw.textbbox((0, 0), text, font=font) text_width = bbox[2] - bbox[0] text_height = bbox[3] - bbox[1] if text_width <= max_width and text_height <= max_height: + pass best_size = mid low = mid + 1 else: + pass high = mid - 1 except Exception as font_error: - print(f"[Text Fitting] Font size {mid} failed: {font_error}") + pass high = mid - 1 - print(f"[Text Fitting] Optimal font size: {best_size}") return best_size except Exception as e: - print(f"[Text Fitting] Error finding optimal font size: {e}") + pass import traceback traceback.print_exc() return min_size def calculate_text_scaling(text, font, target_width, target_height): + pass """Calculate scaling factors to fit text within target dimensions""" try: + pass from PIL import ImageDraw, Image - print(f"[Text Fitting] Calculating scaling for target: {target_width}x{target_height}") # Measure current text size temp_img = Image.new('RGBA', (1, 1)) @@ -231,6 +252,7 @@ def calculate_text_scaling(text, font, target_width, target_height): current_height = bbox[3] - bbox[1] if current_width == 0 or current_height == 0: + pass return 1.0, 1.0, 1.0 # Calculate scaling factors @@ -240,74 +262,79 @@ def calculate_text_scaling(text, font, target_width, target_height): # Use uniform scaling (smaller factor to ensure fit) uniform_scale = min(width_scale, height_scale) - print(f"[Text Fitting] Current size: {current_width}x{current_height}") - print(f"[Text Fitting] Scale factors: width={width_scale:.2f}, height={height_scale:.2f}, uniform={uniform_scale:.2f}") return width_scale, height_scale, uniform_scale except Exception as e: - print(f"[Text Fitting] Error calculating text scaling: {e}") + pass import traceback traceback.print_exc() return 1.0, 1.0, 1.0 def check_text_overlap(text_positions): + pass """Check for overlapping text positions and resolve conflicts""" try: - print(f"[Text Fitting] Checking overlap for {len(text_positions)} text positions") + pass overlapping = [] for i, pos1 in enumerate(text_positions): + pass for j, pos2 in enumerate(text_positions[i+1:], i+1): + pass # Check if rectangles overlap x1, y1, w1, h1 = pos1['x'], pos1['y'], pos1['width'], pos1['height'] x2, y2, w2, h2 = pos2['x'], pos2['y'], pos2['width'], pos2['height'] # Check overlap conditions if not (x1 + w1 <= x2 or x2 + w2 <= x1 or y1 + h1 <= y2 or y2 + h2 <= y1): + pass overlapping.append((i, j)) - print(f"[Text Fitting] Overlap detected between positions {i} and {j}") return overlapping except Exception as e: - print(f"[Text Fitting] Error checking text overlap: {e}") + pass import traceback traceback.print_exc() return [] def resolve_text_conflicts(text_positions, canvas_width, canvas_height): + pass """Resolve overlapping text positions by repositioning""" try: - print(f"[Text Fitting] Resolving text conflicts for {len(text_positions)} positions") + pass resolved_positions = text_positions.copy() overlaps = check_text_overlap(resolved_positions) # Simple conflict resolution: move overlapping text for i, j in overlaps: + pass pos1 = resolved_positions[i] pos2 = resolved_positions[j] # Move the second text down or to the right new_y = pos1['y'] + pos1['height'] + 10 if new_y + pos2['height'] <= canvas_height: + pass resolved_positions[j]['y'] = new_y else: + pass # Try moving to the right new_x = pos1['x'] + pos1['width'] + 10 if new_x + pos2['width'] <= canvas_width: + pass resolved_positions[j]['x'] = new_x else: + pass # If can't fit, reduce to smaller area - print(f"[Text Fitting] Could not resolve conflict for position {j}") - print(f"[Text Fitting] Resolved {len(overlaps)} conflicts") return resolved_positions except Exception as e: - print(f"[Text Fitting] Error resolving text conflicts: {e}") + pass import traceback traceback.print_exc() return text_positions \ No newline at end of file diff --git a/src/core/text_processor.py b/src/core/text_processor.py index cab9b9e..2824558 100644 --- a/src/core/text_processor.py +++ b/src/core/text_processor.py @@ -1,9 +1,10 @@ def process_multiline_text(text, font, max_width, max_height): + pass """Process multiline text with automatic wrapping and fitting""" try: + pass from PIL import ImageFont, ImageDraw, Image - print(f"[Text Processor] Processing multiline text with constraints: {max_width}x{max_height}") # Split text into lines lines = text.split('\n') @@ -16,7 +17,9 @@ def process_multiline_text(text, font, max_width, max_height): total_height = 0 for line in lines: + pass if not line.strip(): + pass # Empty line bbox = draw.textbbox((0, 0), "A", font=font) line_height = bbox[3] - bbox[1] @@ -28,12 +31,13 @@ def process_multiline_text(text, font, max_width, max_height): wrapped_lines = wrap_text_to_width(line, font, max_width) for wrapped_line in wrapped_lines: + pass bbox = draw.textbbox((0, 0), wrapped_line, font=font) line_height = bbox[3] - bbox[1] # Check if adding this line exceeds max height if total_height + line_height > max_height and processed_lines: - print(f"[Text Processor] Height limit reached at {total_height + line_height} > {max_height}") + pass break processed_lines.append(wrapped_line) @@ -41,9 +45,9 @@ def process_multiline_text(text, font, max_width, max_height): # Break if height limit reached if total_height >= max_height: + pass break - print(f"[Text Processor] Processed {len(processed_lines)} lines, total height: {total_height}") return { 'lines': processed_lines, @@ -52,17 +56,20 @@ def process_multiline_text(text, font, max_width, max_height): } except Exception as e: - print(f"[Text Processor] Error processing multiline text: {e}") + pass import traceback traceback.print_exc() return {'lines': [text], 'total_height': 0, 'line_count': 1} def wrap_text_to_width(text, font, max_width): + pass """Wrap text to fit within specified width""" try: + pass from PIL import ImageDraw, Image if not text.strip(): + pass return [""] # Create temporary image for measurements @@ -74,6 +81,7 @@ def wrap_text_to_width(text, font, max_width): text_width = bbox[2] - bbox[0] if text_width <= max_width: + pass return [text] # Split into words and wrap @@ -82,55 +90,68 @@ def wrap_text_to_width(text, font, max_width): current_line = "" for word in words: + pass test_line = current_line + (" " if current_line else "") + word bbox = draw.textbbox((0, 0), test_line, font=font) test_width = bbox[2] - bbox[0] if test_width <= max_width: + pass current_line = test_line else: + pass if current_line: + pass lines.append(current_line) current_line = word else: + pass # Single word is too long, force wrap lines.append(word) current_line = "" if current_line: + pass lines.append(current_line) return lines except Exception as e: - print(f"[Text Processor] Error wrapping text: {e}") + pass return [text] def render_text_with_stroke(draw, position, text, font, fill_color, stroke_color=None, stroke_width=0): + pass """Render text with optional stroke/outline""" try: + pass x, y = position if stroke_color and stroke_width > 0: + pass # Render stroke by drawing text at offset positions for dx in range(-stroke_width, stroke_width + 1): + pass for dy in range(-stroke_width, stroke_width + 1): + pass if dx != 0 or dy != 0: + pass draw.text((x + dx, y + dy), text, font=font, fill=stroke_color) # Render main text draw.text((x, y), text, font=font, fill=fill_color) - print(f"[Text Processor] Rendered text with stroke: width={stroke_width}") except Exception as e: - print(f"[Text Processor] Error rendering text with stroke: {e}") + pass import traceback traceback.print_exc() def calculate_text_metrics(text, font): + pass """Calculate comprehensive text metrics""" try: + pass from PIL import ImageDraw, Image # Create temporary image for measurements @@ -151,31 +172,34 @@ def calculate_text_metrics(text, font): # Get additional font metrics if available try: + pass metrics['ascent'] = font.getmetrics()[0] metrics['descent'] = font.getmetrics()[1] except: + pass metrics['ascent'] = metrics['height'] metrics['descent'] = 0 - print(f"[Text Processor] Text metrics: {metrics}") return metrics except Exception as e: - print(f"[Text Processor] Error calculating text metrics: {e}") + pass import traceback traceback.print_exc() return {'width': 0, 'height': 0} def apply_text_effects(img, text_position, text_size, effects): + pass """Apply various text effects like shadow, glow, etc.""" try: + pass from PIL import Image, ImageFilter, ImageEnhance - print(f"[Text Processor] Applying text effects: {list(effects.keys())}") result_img = img.copy() if 'shadow' in effects: + pass shadow_config = effects['shadow'] offset_x = shadow_config.get('offset_x', 2) offset_y = shadow_config.get('offset_y', 2) @@ -187,13 +211,14 @@ def apply_text_effects(img, text_position, text_size, effects): # Draw shadow text (this would need the actual text rendering logic) if blur_radius > 0: + pass shadow_img = shadow_img.filter(ImageFilter.GaussianBlur(radius=blur_radius)) # Composite shadow result_img = Image.alpha_composite(result_img, shadow_img) - print(f"[Text Processor] Applied shadow effect") if 'glow' in effects: + pass glow_config = effects['glow'] glow_color = glow_config.get('color', (255, 255, 255, 128)) glow_radius = glow_config.get('radius', 3) @@ -203,33 +228,34 @@ def apply_text_effects(img, text_position, text_size, effects): # Apply glow rendering logic here result_img = Image.alpha_composite(result_img, glow_img) - print(f"[Text Processor] Applied glow effect") if 'gradient' in effects: + pass gradient_config = effects['gradient'] start_color = gradient_config.get('start', (255, 255, 255)) end_color = gradient_config.get('end', (0, 0, 0)) direction = gradient_config.get('direction', 'horizontal') # Apply gradient to text - print(f"[Text Processor] Applied gradient effect") return result_img except Exception as e: - print(f"[Text Processor] Error applying text effects: {e}") + pass import traceback traceback.print_exc() return img def optimize_text_layout(text_blocks, canvas_width, canvas_height): + pass """Optimize layout of multiple text blocks to minimize overlap""" try: - print(f"[Text Processor] Optimizing layout for {len(text_blocks)} text blocks") + pass optimized_blocks = [] for i, block in enumerate(text_blocks): + pass x = block.get('x', 0) y = block.get('y', 0) width = block.get('width', 0) @@ -238,6 +264,7 @@ def optimize_text_layout(text_blocks, canvas_width, canvas_height): # Check for overlaps with previous blocks overlap_found = False for prev_block in optimized_blocks: + pass if rectangles_overlap( (x, y, width, height), (prev_block['x'], prev_block['y'], prev_block['width'], prev_block['height']) @@ -246,27 +273,27 @@ def optimize_text_layout(text_blocks, canvas_width, canvas_height): break if overlap_found: + pass # Find new position new_x, new_y = find_non_overlapping_position( width, height, optimized_blocks, canvas_width, canvas_height ) x, y = new_x, new_y - print(f"[Text Processor] Moved block {i} to avoid overlap: ({x}, {y})") optimized_block = block.copy() optimized_block.update({'x': x, 'y': y}) optimized_blocks.append(optimized_block) - print(f"[Text Processor] Layout optimization complete") return optimized_blocks except Exception as e: - print(f"[Text Processor] Error optimizing text layout: {e}") + pass import traceback traceback.print_exc() return text_blocks def rectangles_overlap(rect1, rect2): + pass """Check if two rectangles overlap""" x1, y1, w1, h1 = rect1 x2, y2, w2, h2 = rect2 @@ -274,32 +301,40 @@ def rectangles_overlap(rect1, rect2): return not (x1 + w1 <= x2 or x2 + w2 <= x1 or y1 + h1 <= y2 or y2 + h2 <= y1) def find_non_overlapping_position(width, height, existing_blocks, canvas_width, canvas_height): + pass """Find a position that doesn't overlap with existing blocks""" # Try positions from top-left, moving right then down for y in range(0, canvas_height - height, 20): + pass for x in range(0, canvas_width - width, 20): + pass rect = (x, y, width, height) overlap = False for block in existing_blocks: + pass if rectangles_overlap(rect, (block['x'], block['y'], block['width'], block['height'])): + pass overlap = True break if not overlap: + pass return x, y # If no position found, return original or default return 0, 0 def validate_text_rendering(img, text_positions): + pass """Validate that text rendering was successful""" try: - print(f"[Text Processor] Validating text rendering for {len(text_positions)} positions") + pass validation_results = [] for i, pos in enumerate(text_positions): + pass result = { 'position_index': i, 'x': pos.get('x', 0), @@ -312,31 +347,34 @@ def validate_text_rendering(img, text_positions): # Check bounds if pos.get('x', 0) < 0 or pos.get('y', 0) < 0: + pass result['valid'] = False result['issues'].append('Negative position') if pos.get('x', 0) + pos.get('width', 0) > img.width: + pass result['valid'] = False result['issues'].append('Exceeds image width') if pos.get('y', 0) + pos.get('height', 0) > img.height: + pass result['valid'] = False result['issues'].append('Exceeds image height') # Check for zero dimensions if pos.get('width', 0) == 0 or pos.get('height', 0) == 0: + pass result['valid'] = False result['issues'].append('Zero dimensions') validation_results.append(result) valid_count = sum(1 for r in validation_results if r['valid']) - print(f"[Text Processor] Validation complete: {valid_count}/{len(text_positions)} positions valid") return validation_results except Exception as e: - print(f"[Text Processor] Error validating text rendering: {e}") + pass import traceback traceback.print_exc() return [] \ No newline at end of file diff --git a/src/operators/__init__.py b/src/operators/__init__.py index a3e9c11..e0cc52b 100644 --- a/src/operators/__init__.py +++ b/src/operators/__init__.py @@ -4,67 +4,88 @@ Contains all Blender operators for text texture generation and management. """ from .generation_ops import * -from .preset_ops import * -from .utility_ops import * __all__ = [] # Import all operator classes and add to __all__ from .generation_ops import ( TEXT_TEXTURE_OT_generate, + TEXT_TEXTURE_OT_preview, TEXT_TEXTURE_OT_generate_shader, ) -from .preset_ops import ( - TEXT_TEXTURE_OT_save_preset, - TEXT_TEXTURE_OT_save_preset_enter, - TEXT_TEXTURE_OT_load_preset, - TEXT_TEXTURE_OT_delete_preset, - TEXT_TEXTURE_OT_refresh_presets, - TEXT_TEXTURE_OT_export_presets, - TEXT_TEXTURE_OT_import_presets, - TEXT_TEXTURE_OT_show_migration_report, -) +# Conditional imports for modules that might not exist in free version +try: + pass + from .preset_ops import * + from .preset_ops import ( + TEXT_TEXTURE_OT_save_preset, + TEXT_TEXTURE_OT_save_preset_enter, + TEXT_TEXTURE_OT_load_preset, + TEXT_TEXTURE_OT_delete_preset, + TEXT_TEXTURE_OT_refresh_presets, + TEXT_TEXTURE_OT_export_presets, + TEXT_TEXTURE_OT_import_presets, + TEXT_TEXTURE_OT_show_migration_report, + ) + PRESET_OPERATORS = [ + 'TEXT_TEXTURE_OT_save_preset', + 'TEXT_TEXTURE_OT_save_preset_enter', + 'TEXT_TEXTURE_OT_load_preset', + 'TEXT_TEXTURE_OT_delete_preset', + 'TEXT_TEXTURE_OT_refresh_presets', + 'TEXT_TEXTURE_OT_export_presets', + 'TEXT_TEXTURE_OT_import_presets', + 'TEXT_TEXTURE_OT_show_migration_report', + ] +except ImportError: + pass + # preset_ops module not available (free version) + PRESET_OPERATORS = [] -from .utility_ops import ( - TEXT_TEXTURE_OT_refresh_preview, - TEXT_TEXTURE_OT_view_preview_zoom, - TEXT_TEXTURE_OT_open_panel, - TEXT_TEXTURE_OT_refresh_fonts, - TEXT_TEXTURE_OT_add_overlay, - TEXT_TEXTURE_OT_remove_overlay, - TEXT_TEXTURE_OT_set_anchor_point, - TEXT_TEXTURE_OT_sync_margins, - TEXT_TEXTURE_OT_duplicate_overlay, - TEXT_TEXTURE_OT_delete_overlay, - TEXT_TEXTURE_OT_move_overlay, -) +try: + pass + from .utility_ops import * + from .utility_ops import ( + TEXT_TEXTURE_OT_refresh_preview, + TEXT_TEXTURE_OT_view_preview_zoom, + TEXT_TEXTURE_OT_open_panel, + TEXT_TEXTURE_OT_refresh_fonts, + TEXT_TEXTURE_OT_add_overlay, + TEXT_TEXTURE_OT_remove_overlay, + TEXT_TEXTURE_OT_set_anchor_point, + TEXT_TEXTURE_OT_sync_margins, + TEXT_TEXTURE_OT_duplicate_overlay, + TEXT_TEXTURE_OT_delete_overlay, + TEXT_TEXTURE_OT_move_overlay, + ) + UTILITY_OPERATORS = [ + 'TEXT_TEXTURE_OT_refresh_preview', + 'TEXT_TEXTURE_OT_view_preview_zoom', + 'TEXT_TEXTURE_OT_open_panel', + 'TEXT_TEXTURE_OT_refresh_fonts', + 'TEXT_TEXTURE_OT_add_overlay', + 'TEXT_TEXTURE_OT_remove_overlay', + 'TEXT_TEXTURE_OT_set_anchor_point', + 'TEXT_TEXTURE_OT_sync_margins', + 'TEXT_TEXTURE_OT_duplicate_overlay', + 'TEXT_TEXTURE_OT_delete_overlay', + 'TEXT_TEXTURE_OT_move_overlay', + ] +except ImportError: + pass + # utility_ops module not available (free version) + UTILITY_OPERATORS = [] __all__.extend([ # Generation operators 'TEXT_TEXTURE_OT_generate', + 'TEXT_TEXTURE_OT_preview', 'TEXT_TEXTURE_OT_generate_shader', - - # Preset operators - 'TEXT_TEXTURE_OT_save_preset', - 'TEXT_TEXTURE_OT_save_preset_enter', - 'TEXT_TEXTURE_OT_load_preset', - 'TEXT_TEXTURE_OT_delete_preset', - 'TEXT_TEXTURE_OT_refresh_presets', - 'TEXT_TEXTURE_OT_export_presets', - 'TEXT_TEXTURE_OT_import_presets', - 'TEXT_TEXTURE_OT_show_migration_report', - - # Utility operators - 'TEXT_TEXTURE_OT_refresh_preview', - 'TEXT_TEXTURE_OT_view_preview_zoom', - 'TEXT_TEXTURE_OT_open_panel', - 'TEXT_TEXTURE_OT_refresh_fonts', - 'TEXT_TEXTURE_OT_add_overlay', - 'TEXT_TEXTURE_OT_remove_overlay', - 'TEXT_TEXTURE_OT_set_anchor_point', - 'TEXT_TEXTURE_OT_sync_margins', - 'TEXT_TEXTURE_OT_duplicate_overlay', - 'TEXT_TEXTURE_OT_delete_overlay', - 'TEXT_TEXTURE_OT_move_overlay', -]) \ No newline at end of file +]) + +# Add preset operators if available +__all__.extend(PRESET_OPERATORS) + +# Add utility operators if available +__all__.extend(UTILITY_OPERATORS) \ No newline at end of file diff --git a/src/operators/__pycache__/__init__.cpython-311.pyc b/src/operators/__pycache__/__init__.cpython-311.pyc index 2070c31172f978ea08399b37538577bd9d8d1bee..0e6542a41fc3f5ee06759f351f808853429701e9 100644 GIT binary patch literal 2181 zcmc(gyKf^!7{F(|>$l^)oY=ABWTPWUM#>q{!XX7@p@>JK#E~x3j8@)BvS?@5n%%sv zLNxpXNE9^a3J{{Brb$EXldd>zbrKa4#iFB#uHyUbwJ&%T1O;L}{_S|a=X~?y{;d0AiuS!6|7VkJ4lW@MR_DfqJV$CS;w<$FZTy+%|9{Xm4-BjV~8 ziWjVo^|Gk!uy^Zn6~R1rsEjzIl{GsGQ5e_F<1rY41vy0M$T-{ zHB8qsr0JR`LJBvyU-%Vg*P3YD~XJkuK6H>d3_;fP@3K9Qah#}N25HuuA| zX?taAk3P`5G}Rx(iu9K}3_??;{|!}@eE zSaX;6O;y}pkEI3}Dc zyxmk9kpYB-(GEl%mBrBEzn3v9;@u7*Bm(P|Dck61t~A}WX_#4@6as3BGW;rwBD=cAqO(XAL6IL2_szb7w1 zfxP^%edr72x1qW{3B?yqdrx_vieBKi@x*V%$9^j=gs0lR>a{2LLz{EkwFA!Y_pe^& z-jTyQ+7;q)|$vlt%T4APR)84M=m;o)=1=WuuHvQxW+$DA#HInVx&5 zHwWVurSj=aME+Y;@->VRW1v@{-9#0VTg1uLUjdSPUR@a}0HZ8OZ*Fd)f4lD1Tm#NO}&Ia*W?e znreR_U<@!9s|{w?pAh3IF$TnlGLu2$3E6l`HU{J7&@Kuo7#1o0{;Yy9AK0H delta 643 zcmZn_oWjSqoR^o20SJuwj%3_ppU5X+a{$Pj&XB?o#gM|7!j!`p0->3pG;;_;Izto- zP>wl=F_$%pHJ2@ljS-}VC5LHZl9C{h#hSyM%OAy`D-b1+D;PC#x1t#%Ln_MxDUe0z zAe|wFZ4u)#Mh1q}KnwvuCDtDq>gIJE@K<}NMC z%*iaNocxwW2Bv%RX4Z6WC7^L2ClpsQPF7`;ufM?{c>#uQFi2g1p$A+%4eTG-m>HQq zFc2oVI6>;jAnHlbC&mL(OkE$NHa2j0TDnkWTHp` G7}Nme)r<)M diff --git a/src/operators/__pycache__/generation_ops.cpython-311.pyc b/src/operators/__pycache__/generation_ops.cpython-311.pyc index 07c8c27e473615e9648c0bf10e36bd49a998e446..8bfd30862e922581f7e60b282aa1be17e66716f4 100644 GIT binary patch literal 23465 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