diff --git a/model/sketch.py b/model/sketch.py index c0b5a81a..1180f8ad 100644 --- a/model/sketch.py +++ b/model/sketch.py @@ -100,8 +100,8 @@ def is_visible(self, context): def get_solver_state(self): return bpyEnum(global_data.solver_state_items, identifier=self.solver_state) - def solve(self, context): - return solve_system(context, sketch=self) + def solve(self, context, report=True, update_entities=True): + return solve_system(context, sketch=self, report=report, update_entities=update_entities) @classmethod def is_sketch(cls): diff --git a/operators/add_line_2d.py b/operators/add_line_2d.py index eca58bee..5590e4d0 100644 --- a/operators/add_line_2d.py +++ b/operators/add_line_2d.py @@ -12,7 +12,7 @@ from ..solver import solve_system from .base_2d import Operator2d from .constants import types_point_2d -from .utilities import ignore_hover +from .utilities import ignore_hover, safe_constraints logger = logging.getLogger(__name__) @@ -54,16 +54,16 @@ def main(self, context: Context): self.target.construction = True # auto vertical/horizontal constraint - constraints = context.scene.sketcher.constraints vec_dir = self.target.direction_vec() if vec_dir.length: angle = vec_dir.angle(Vector((1, 0))) - threshold = 0.1 - if angle < threshold or angle > HALF_TURN - threshold: - constraints.add_horizontal(self.target, sketch=self.sketch) - elif (QUARTER_TURN - threshold) < angle < (QUARTER_TURN + threshold): - constraints.add_vertical(self.target, sketch=self.sketch) + + with safe_constraints(context, sketch=self.sketch) as constraints: + if angle < threshold or angle > HALF_TURN - threshold: + constraints.add_horizontal(self.target, sketch=self.sketch) + elif (QUARTER_TURN - threshold) < angle < (QUARTER_TURN + threshold): + constraints.add_vertical(self.target, sketch=self.sketch) ignore_hover(self.target) return True diff --git a/operators/utilities.py b/operators/utilities.py index 495a219c..a3a4aa62 100644 --- a/operators/utilities.py +++ b/operators/utilities.py @@ -1,4 +1,5 @@ import logging +from contextlib import contextmanager import bpy from bpy.types import Context, Operator @@ -7,6 +8,9 @@ from .. import global_data from ..declarations import GizmoGroups, WorkSpaceTools from ..converters import update_convertor_geometry +from ..model.base_constraint import GenericConstraint +from ..model.group_constraints import SlvsConstraints +from ..model.sketch import SlvsSketch from ..utilities.preferences import use_experimental, get_prefs from ..utilities.data_handling import entities_3d @@ -201,3 +205,33 @@ def select_target_ob(context, sketch): if target_ob.name in context.view_layer.objects: target_ob.select_set(True) context.view_layer.objects.active = target_ob + + +@contextmanager +def safe_constraints( + context: Context, + sketch: SlvsSketch = None, + constraints: SlvsConstraints = None, + remove_only_failed=False, +): + sketch = sketch or context.scene.sketcher.active_sketch + constraints = constraints or context.scene.sketcher.constraints + + solvable = sketch.solver_state == "OKAY" + old: set[GenericConstraint] = set(constraints.all) + + try: + yield constraints + + finally: + if not solvable: + return + + if not sketch.solve(context, update_entities=False): + new: set[GenericConstraint] = set(constraints.all) - old + + for constraint in new: + if constraint.failed or not remove_only_failed: + constraints.remove(constraint) + + sketch.solve(context, update_entities=False) diff --git a/solver.py b/solver.py index f95645c6..09ee6580 100644 --- a/solver.py +++ b/solver.py @@ -192,7 +192,7 @@ def needs_update(self, e): # TODO: skip entities that aren't in active group return True - def solve(self, report=True): + def solve(self, report=True, update_entities=True): self.report = report self._init_slvs_data() @@ -249,11 +249,12 @@ def _get_msg_failed(): logger.debug(_get_msg_failed()) # Update entities from solver - for e in self.entities: - if not self.needs_update(e): - continue + if update_entities: + for e in self.entities: + if not self.needs_update(e): + continue - e.update_from_slvs(self.solvesys) + e.update_from_slvs(self.solvesys) def _get_msg_update(): msg = "Update entities from solver:" @@ -269,6 +270,6 @@ def _get_msg_update(): return self.ok -def solve_system(context, sketch=None): +def solve_system(context, sketch=None, report=True, update_entities=True): solver = Solver(context, sketch) - return solver.solve() + return solver.solve(report=report, update_entities=update_entities)