wip
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -2,26 +2,39 @@
|
||||
Constants and metadata for the Text Texture Generator addon.
|
||||
"""
|
||||
|
||||
# Version information
|
||||
VERSION_TYPE = "free" # "free" or "full"
|
||||
|
||||
bl_info = {
|
||||
"name": "Text Texture Generator",
|
||||
"name": "Text Texture Generator" + (" Free" if VERSION_TYPE == "free" else " Full"),
|
||||
"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",
|
||||
"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}")
|
||||
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
|
||||
@@ -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
|
||||
@@ -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'],
|
||||
|
||||
Reference in New Issue
Block a user