Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
f6ff723
Try to cycle through overlapping entities
rcarmo May 3, 2025
165dda1
Debugging tasks
rcarmo May 4, 2025
30bd0b5
Add safe_batch_for_shader wrapper to correctly handle parameters
rcarmo May 4, 2025
f91fb44
Revert "StateOps: Push an undo step if OP finishes from preselection"
hlorus Feb 18, 2025
b0b6fb3
[Ui] Change icon name
hlorus Mar 3, 2025
eda9ee6
go deeper on batch_for_shader replacement
rcarmo May 4, 2025
42c529b
2 more batch_for_shader replacements
rcarmo May 4, 2025
0c33451
correctly handle box selection coordinates (they're 2D, not 3D...)
rcarmo May 4, 2025
a5ce841
Make sure workplanes are rendered to the selection buffer
rcarmo May 4, 2025
714b3dc
Try to prioritize occluded workplane edges (and allow workplanes to b…
rcarmo May 4, 2025
479dda3
tweak tolerances
rcarmo May 4, 2025
2cc1e1f
Tweak tolerances
rcarmo May 4, 2025
c073d78
Bonus: unregistration logging to catch errors during frequent restarts
rcarmo May 4, 2025
a238ab2
Linux testing
rcarmo May 5, 2025
e7b1079
linux: change loading order foor 4.4.1
rcarmo May 5, 2025
4646366
handle case where preferences are not yet defined (like when debugging)
rcarmo May 5, 2025
8efe77b
tweak launch tasks
rcarmo May 5, 2025
929ea51
color constants
rcarmo May 5, 2025
f4b8e69
Create mock preferences object for fallback
rcarmo May 5, 2025
125305f
constant cleanup
rcarmo May 5, 2025
1ef413c
selection and drawing constants
rcarmo May 5, 2025
33ee68a
more constant cleanup
rcarmo May 5, 2025
302412b
More constant shuffling (remove circular import)
rcarmo May 5, 2025
93f0b9a
Better handle unregistration so it doesn't crash upon debugger reload
rcarmo May 5, 2025
afacd7f
Remove workplane selection by plane
rcarmo May 5, 2025
0710a92
Try to handle unregistration better for debugging scenarios
rcarmo May 5, 2025
8710b8c
refactor: shader handling, instrumentation, selection workflow
rcarmo May 5, 2025
bcb0f8d
shared data: group items and helpers
rcarmo May 5, 2025
0fa3940
tasks: fix Blender not syncing to debugger
rcarmo May 5, 2025
ee86353
imports: hoist
rcarmo May 5, 2025
1f01dbe
imports: hoist
rcarmo May 5, 2025
2174e0f
imports: keep inline
rcarmo May 5, 2025
6b09465
refactor: move recalc_pointers to data_handling
rcarmo May 5, 2025
245de38
imports: hoist
rcarmo May 5, 2025
dcf3b9b
imports: hoist
rcarmo May 5, 2025
f9f0142
refactor: selection behavior
rcarmo May 5, 2025
af94fa2
refactor: move make_coincident to data_handling
rcarmo May 5, 2025
08f5d8b
shaders: use dedicated to type
rcarmo May 5, 2025
d372e04
refactor: break out solver constants
rcarmo May 5, 2025
2a5266e
refactor: replace solver constants
rcarmo May 5, 2025
ba418f1
refactor: replace solver constants
rcarmo May 5, 2025
d3368c1
refactor: replace solver constants
rcarmo May 5, 2025
e666ace
refactor: replace solver constants
rcarmo May 5, 2025
1cceaec
refactor: replace solver constants
rcarmo May 5, 2025
fd0a33a
refactor: replace solver constants
rcarmo May 5, 2025
c14e7bf
refactor: replace solver constants
rcarmo May 5, 2025
2dd561b
refactor: missed an import
rcarmo May 5, 2025
791be1f
fixes: typos, hoisted imports that generate loops
rcarmo May 5, 2025
d2ea427
refactor: hunt down and kill to_list's evil twin, typos
rcarmo May 5, 2025
eb8ad86
cleanups: global state
rcarmo May 5, 2025
7636d2f
revert: global_data handling
rcarmo May 5, 2025
3f32c4a
revert: more obvious global_data handling
rcarmo May 5, 2025
170d570
refactor: use safe variants
rcarmo May 5, 2025
8d7ce0a
logs: debugging
rcarmo May 5, 2025
58d7c86
icon manager: checks and fallbacks
rcarmo May 6, 2025
6dfd976
workplanes: do not render in sketch
rcarmo May 6, 2025
ffd2220
constants: hoist
rcarmo May 6, 2025
782ce19
typo
rcarmo May 6, 2025
92387e4
workplane: clarify visibility/rendering logic.
rcarmo May 8, 2025
c670d47
workplanes: ensure edges are not drawn inside sketch
rcarmo May 17, 2025
41628dc
select: add logging
rcarmo May 17, 2025
8a9706f
select: add logging
rcarmo May 17, 2025
671ac24
Merge branch 'fix/50' of github.com:rcarmo/CAD_Sketcher into fix/50
rcarmo May 17, 2025
f415978
select: overlapping lines work
rcarmo May 17, 2025
809b8b8
select: cleanup logging
rcarmo May 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 22 additions & 12 deletions .vscode/debugpy_port.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,29 @@
import debugpy
import sys
import os

# Initialize color variables safely
YELLOW, GREEN, RED, RESET = [""] * 4
try:
from colorama import Fore

YELLOW, GREEN, RED, RESET = Fore.YELLOW, Fore.GREEN, Fore.RED, Fore.RESET
except ImportError:
YELLOW, GREEN, RED, RESET = [""] * 4
print("colorama not found, proceeding without colors.") # Optional: inform the user

# Print the current Python path for debugging
print(f"{YELLOW}Current Python path:{RESET}")
for path in sys.path:
print(f"{GREEN}{path}{RESET}")

# https://code.visualstudio.com/docs/python/debugging#_debugging-by-attaching-over-a-network-connection

# 5678 is the default attach port in the VS Code debug configurations. Unless a host and port are specified, host defaults to 127.0.0.1
debugpy.listen(5678)
print(f"{YELLOW}Waiting for debugger attach{RESET}")
debugpy.wait_for_client()
if debugpy.is_client_connected():
print(f"{GREEN}Debugger attached to client{RESET}")
else:
print(f"{RED}Failed to connect to client{RESET}")
# Start debugpy
try:
import debugpy
debugpy.listen(('localhost', 5678))
print(f"{YELLOW}Waiting for debugger attach{RESET}")
debugpy.wait_for_client()
if debugpy.is_client_connected():
print(f"{GREEN}Debugger attached to client{RESET}")
else:
print(f"{RED}Failed to connect to client{RESET}")
except ImportError as e:
print(f"{RED}Failed to import debugpy: {e}{RESET}")
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"host": "localhost",
"port": 5678
},
"preLaunchTask": "Run Blender interactively",
"preLaunchTask": "Run Blender interactively (Linux)",
"pathMappings": [
{
"localRoot": "${workspaceFolder}",
Expand Down
36 changes: 33 additions & 3 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,40 @@
}
},
{
"label": "Run Blender interactively",
"detail": "Starts remote debugpy session and Blender gui with solely this addon activated",
"label": "Run Blender interactively (Linux)",
"detail": "Starts remote debugpy session and Blender GUI",
"command": "blender",
"args": ["--addons", "CAD_Sketcher", "--python","./.vscode/debugpy_port.py", "--", "--log_level=DEBUG", "--interactive"],
"args": ["--addons", "CAD_Sketcher","--python","./.vscode/debugpy_port.py","--","--log_level=DEBUG", "--log-stdout", "--interactive"],
"type": "shell",
"isBackground": true,
"problemMatcher": {
"owner": "python",
"source": "python",
"fileLocation": ["relative", "${workspaceFolder}"],
"pattern": {
// Example error that we want to regex-catch
// File "/abs/path/to/CAD_Sketcher/class_defines.py", line 3467, in register
// bpy.utils.register_class(cls)
// ValueError: bpy_struct "SlvsPoint3D" registration error: 'slvs_index' PointerProperty could not register (see previous error)
"regexp": "^ *File \"([\/\\w.]+)\", line (\\d+), in (\\w+)\n *(.*)\\n(\\w+: .*)$",
"file": 1,
"line": 2,
// "message": 3, // The function name
"code": 4,
"message": 5
},
"background": {
"activeOnStart": true,
"beginsPattern": "^blender.*$",
"endsPattern": "Waiting for debugger attach", // See debugpy_port.py
}
}
},
{
"label": "Run Blender interactively (Mac)",
"detail": "Starts remote debugpy session and Blender GUI",
"command": "/Applications/Blender.app/Contents/MacOS/Blender", // Use full path
"args": ["--addons", "CAD_Sketcher", "--python","./.vscode/debugpy_port.py", "--", "--log_level=DEBUG", "--log-stdout", "--interactive"],
"type": "shell",
"isBackground": true,
"problemMatcher": {
Expand Down
82 changes: 73 additions & 9 deletions __init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
import bpy
import addon_utils
from bpy.app import background
import bpy.app

# Define logger earlier for use in initial import attempts
logger = logging.getLogger(__name__)

bl_info = {
"name": "CAD Sketcher",
Expand All @@ -18,6 +21,27 @@
"tracker_url": "https://github.com/hlorus/CAD_Sketcher/discussions/categories/announcements",
}

# Import conditionally to avoid errors when in background mode
_icon_manager_module_for_init = None
if not background:
try:
# Print for immediate console feedback, as logger might not be fully configured.
print("CAD Sketcher INIT: Attempting to import .icon_manager module.")
from . import icon_manager as im_actual_module
_icon_manager_module_for_init = im_actual_module
# If import succeeds, this will be visible in console.
print(f"CAD Sketcher INIT: Successfully imported .icon_manager: {_icon_manager_module_for_init}")
# Use logger as well, it might catch it even if not fully configured.
logger.info(f"Successfully imported .icon_manager: {_icon_manager_module_for_init}")
except ImportError as e_imp:
print(f"CAD Sketcher CRITICAL IMPORT_ERROR: Failed to import .icon_manager: {e_imp}. Icons will be unavailable.")
logger.error(f"Failed to import .icon_manager due to ImportError: {e_imp}", exc_info=True)
except Exception as e_gen: # Catch any other exception during the import process
print(f"CAD Sketcher CRITICAL GENERAL_EXCEPTION: Failed during .icon_manager import: {e_gen}. Icons will be unavailable.")
logger.error(f"Failed during .icon_manager import due to a general Exception: {e_gen}", exc_info=True)
# _icon_manager_module_for_init remains None

icon_manager = _icon_manager_module_for_init # This is what `from .. import icon_manager` will get.


def get_addon_version_tuple() -> tuple:
Expand Down Expand Up @@ -52,6 +76,7 @@ def get_min_blender_version() -> tuple:
f"{get_min_blender_version()} or greater.\n"
)

# Keep module import for global_data since we modify module variables
from . import global_data
from .registration import register_base, unregister_base, register_full, unregister_full
from .utilities.install import check_module
Expand All @@ -61,23 +86,43 @@ def get_min_blender_version() -> tuple:


# Globals
logger = logging.getLogger(__name__)
_load_post_handler_added = False


def register():
def initialize_logger_level():
global _load_post_handler_added
# Ensure it runs only once and cleans up
if initialize_logger_level in bpy.app.handlers.load_post:
bpy.app.handlers.load_post.remove(initialize_logger_level)
_load_post_handler_added = False # Reset flag after execution or removal
try:
update_logger(logger)
logger.debug("Logger level updated from preferences.")
except Exception as e:
logger.error(f"Error updating logger level from preferences: {e}")


def register():
global _load_post_handler_added
# Setup root logger
setup_logger(logger)
setup_logger(logger) # Now logger is fully configured.

# Register base
ensure_addon_presets()
register_base()

update_logger(logger)

# Load icons using the (hopefully) valid icon_manager module reference
if not background:
from . import icon_manager
icon_manager.load()
if icon_manager: # Check if the module was successfully imported
try:
logger.info("Register: icon_manager module appears to be imported. Attempting icon_manager.load().")
icon_manager.load() # Call load() on the imported module
logger.info("Register: icon_manager.load() called successfully.")
except Exception as e:
logger.error(f"Error calling icon_manager.load() during register: {e}", exc_info=True)
else:
# This log is crucial. If it appears, the import of icon_manager.py failed.
logger.warning("Register: icon_manager module is None. Skipping icon_manager.load(). This usually means the import of .icon_manager failed earlier.")

logger.info("Enabled CAD Sketcher base, version: {}".format(get_addon_version()))

Expand All @@ -94,11 +139,30 @@ def register():
"Solvespace module isn't available, only base modules registered\n" + str(e)
)

# Add handler to update logger level after Blender loads fully
if not _load_post_handler_added and initialize_logger_level not in bpy.app.handlers.load_post:
bpy.app.handlers.load_post.append(initialize_logger_level)
_load_post_handler_added = True


def unregister():
global _load_post_handler_added
# Remove the handler if it was added
if _load_post_handler_added and initialize_logger_level in bpy.app.handlers.load_post:
try:
bpy.app.handlers.load_post.remove(initialize_logger_level)
_load_post_handler_added = False
except ValueError:
_load_post_handler_added = False # Ensure flag is reset
pass # Ignore error if handler not found

if not background:
from . import icon_manager
icon_manager.unload()
if icon_manager: # Check if the module exists
try:
icon_manager.unload()
logger.info("icon_manager.unload() called successfully.")
except Exception as e:
logger.error(f"Error calling icon_manager.unload(): {e}", exc_info=True)

if global_data.registered:
unregister_full()
Expand Down
28 changes: 28 additions & 0 deletions base/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""
Constants used by the base module and imported throughout the codebase.
"""

# Size and scale constants
DEFAULT_WORKPLANE_SIZE = 0.4
DEFAULT_ENTITY_SCALE = 1.0
DEFAULT_GIZMO_SCALE = 15.0
DEFAULT_TEXT_SIZE = 15
DEFAULT_ARROW_SCALE = 1.0

# Precision constants
DEFAULT_DECIMAL_PRECISION = 3
DEFAULT_ANGLE_PRECISION = 0

# Boolean preference defaults
DEFAULT_SHOW_DEBUG_SETTINGS = False
DEFAULT_HIDE_INACTIVE_CONSTRAINTS = True
DEFAULT_ALL_ENTITIES_SELECTABLE = False
DEFAULT_FORCE_REDRAW = True
DEFAULT_AUTO_HIDE_OBJECTS = True
DEFAULT_USE_ALIGN_VIEW = True
DEFAULT_SHOW_THEME_SETTINGS = False

# Solver group constants
SOLVER_GROUP_FIXED = 1
SOLVER_GROUP_3D = 2
SOLVER_START_SKETCH_GROUPS = 3
49 changes: 33 additions & 16 deletions base/preferences.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,30 @@
)

from . import theme
from .. import global_data, units
from .constants import (
DEFAULT_SHOW_DEBUG_SETTINGS,
DEFAULT_SHOW_THEME_SETTINGS,
DEFAULT_HIDE_INACTIVE_CONSTRAINTS,
DEFAULT_ALL_ENTITIES_SELECTABLE,
DEFAULT_FORCE_REDRAW,
DEFAULT_DECIMAL_PRECISION,
DEFAULT_ANGLE_PRECISION,
DEFAULT_AUTO_HIDE_OBJECTS,
DEFAULT_ENTITY_SCALE,
DEFAULT_WORKPLANE_SIZE,
DEFAULT_GIZMO_SCALE,
DEFAULT_TEXT_SIZE,
DEFAULT_ARROW_SCALE,
DEFAULT_USE_ALIGN_VIEW,
)
# Keep module import for global_data since we need to access a variable that may be modified
from .. import global_data
from .. import units
from ..declarations import Operators
from ..utilities.register import get_path, get_name
from ..utilities.view import update_cb
from ..utilities.install import check_module


log_levels = [
("CRITICAL", "Critical", "", 0),
("ERROR", "Error", "", 1),
Expand Down Expand Up @@ -105,12 +122,12 @@ class Preferences(AddonPreferences):

show_debug_settings: BoolProperty(
name="Show Debug Settings",
default=False,
default=DEFAULT_SHOW_DEBUG_SETTINGS,
)
show_theme_settings: BoolProperty(
name="Show Theme Settings",
description="Expand this box to show various theme settings",
default=False,
default=DEFAULT_SHOW_THEME_SETTINGS,
)
package_path: StringProperty(
name="Package Filepath",
Expand All @@ -126,17 +143,17 @@ class Preferences(AddonPreferences):
default=2,
)
hide_inactive_constraints: BoolProperty(
name="Hide inactive Constraints", default=True, update=update_cb
name="Hide inactive Constraints", default=DEFAULT_HIDE_INACTIVE_CONSTRAINTS, update=update_cb
)
all_entities_selectable: BoolProperty(
name="Make all Entities Selectable", update=update_cb
name="Make all Entities Selectable", default=DEFAULT_ALL_ENTITIES_SELECTABLE, update=update_cb
)
force_redraw: BoolProperty(name="Force Entity Redraw", default=True)
force_redraw: BoolProperty(name="Force Entity Redraw", default=DEFAULT_FORCE_REDRAW)

decimal_precision: IntProperty(
name="Decimal Precision",
description="Number of digits after the comma",
default=3,
default=DEFAULT_DECIMAL_PRECISION,
min=0,
soft_max=7,
)
Expand All @@ -145,30 +162,30 @@ class Preferences(AddonPreferences):
name="Angle Precision",
min=0,
max=5,
default=0,
default=DEFAULT_ANGLE_PRECISION,
description="Angle decimal precision",
)

auto_hide_objects: BoolProperty(
name="Auto Fade Objects",
description="Fade curves/meshes while in sketch mode",
default=True,
default=DEFAULT_AUTO_HIDE_OBJECTS,
)
entity_scale: FloatProperty(
name="Entity Scale", default=1.0, min=0.1, soft_max=3.0, update=theme.update
name="Entity Scale", default=DEFAULT_ENTITY_SCALE, min=0.1, soft_max=3.0, update=theme.update
)
workplane_size: FloatProperty(
name="Workplane Size", default=0.4, soft_min=0.1, soft_max=1.0
name="Workplane Size", default=DEFAULT_WORKPLANE_SIZE, soft_min=0.1, soft_max=1.0
)
gizmo_scale: FloatProperty(
name="Icon Scale", default=15.0, min=1.0, soft_max=25.0, update=theme.update
name="Icon Scale", default=DEFAULT_GIZMO_SCALE, min=1.0, soft_max=25.0, update=theme.update
)
text_size: IntProperty(name="Text Size", default=15, min=5, soft_max=25)
arrow_scale: FloatProperty(name="Arrow Scale", default=1, min=0.2, soft_max=3)
text_size: IntProperty(name="Text Size", default=DEFAULT_TEXT_SIZE, min=5, soft_max=25)
arrow_scale: FloatProperty(name="Arrow Scale", default=DEFAULT_ARROW_SCALE, min=0.2, soft_max=3)
use_align_view: BoolProperty(
name="Align View",
description="Automatically align view to workplane when activating a sketch.",
default=True,
default=DEFAULT_USE_ALIGN_VIEW,
)

def draw(self, context):
Expand Down
Loading