deployment

This commit is contained in:
2025-09-11 14:07:55 +03:00
parent c1ac5297f2
commit 9c2c879483
54 changed files with 1859 additions and 26 deletions

13
src/utils/__init__.py Normal file
View File

@@ -0,0 +1,13 @@
"""
Utility modules for the Text Texture Generator addon.
"""
from .constants import bl_info
from .system import install_pillow, get_system_fonts, get_font_enum_items
__all__ = [
'bl_info',
'install_pillow',
'get_system_fonts',
'get_font_enum_items'
]

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

32
src/utils/constants.py Normal file
View File

@@ -0,0 +1,32 @@
"""
Constants and metadata for the Text Texture Generator addon.
"""
bl_info = {
"name": "Text Texture Generator",
"author": "Marc Mintel <marc@mintel.me>",
"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",
"category": "Material",
}
# Version information
VERSION_TYPE = "full" # "free" or "full"
# 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
}
def is_free_version():
"""Check if this is the free version."""
return VERSION_TYPE == "free"
def is_full_version():
"""Check if this is the full version."""
return VERSION_TYPE == "full"

View File

@@ -0,0 +1,150 @@
"""
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

84
src/utils/system.py Normal file
View File

@@ -0,0 +1,84 @@
"""
System utility functions for the Text Texture Generator addon.
"""
import subprocess
import sys
import platform
import os
def install_pillow():
"""Install Pillow if not available"""
try:
import PIL
except ImportError:
print("Installing Pillow...")
subprocess.check_call([sys.executable, "-m", "pip", "install", "pillow>=10.0.0"])
def get_system_fonts():
"""Get detailed list of available system fonts with paths"""
try:
from PIL import ImageFont
import glob
fonts = {}
system = platform.system()
if system == "Windows":
font_dirs = [
"C:/Windows/Fonts/",
os.path.expanduser("~/AppData/Local/Microsoft/Windows/Fonts/")
]
extensions = ["*.ttf", "*.ttc", "*.otf"]
elif system == "Darwin": # macOS
font_dirs = [
"/System/Library/Fonts/",
"/Library/Fonts/",
os.path.expanduser("~/Library/Fonts/")
]
extensions = ["*.ttf", "*.ttc", "*.otf", "*.dfont"]
else: # Linux
font_dirs = [
"/usr/share/fonts/",
"/usr/local/share/fonts/",
os.path.expanduser("~/.local/share/fonts/"),
os.path.expanduser("~/.fonts/")
]
extensions = ["*.ttf", "*.ttc", "*.otf"]
for font_dir in font_dirs:
if os.path.exists(font_dir):
for ext in extensions:
for font_path in glob.glob(os.path.join(font_dir, "**", ext), recursive=True):
try:
font_name = os.path.splitext(os.path.basename(font_path))[0]
font_name = font_name.replace("-", " ").replace("_", " ")
fonts[font_name] = font_path
except Exception:
continue
return fonts
except ImportError:
return {}
def get_font_enum_items(self, context):
"""Dynamic enum items for font selection"""
items = [("default", "Default Font", "Use system default font", 0)]
if not hasattr(get_font_enum_items, 'cached_fonts'):
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]):
items.append((font_path, font_name, f"Font: {font_name}", i + 1))
return items
def get_anchor_point_matrix():
"""Return a 3x3 matrix of anchor point identifiers for UI positioning grid"""
return [
['TOP_LEFT', 'TOP_CENTER', 'TOP_RIGHT'],
['MIDDLE_LEFT', 'MIDDLE_CENTER', 'MIDDLE_RIGHT'],
['BOTTOM_LEFT', 'BOTTOM_CENTER', 'BOTTOM_RIGHT']
]