From 06f1d402b0647671b4dd62d43f705e67199d5c2c Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 17 Apr 2024 21:18:42 +0200 Subject: [PATCH 1/4] Checks for duplicate constraints. On constraint creation checks are added to see if it exists on the selected entity or entitiies. The exists method was added to GenericConstraintOp for this. Line distances have been replaced for point to point distances. --- operators/add_angle.py | 17 ++-- operators/add_diameter.py | 15 ++-- operators/add_distance.py | 29 +++++-- operators/add_geometric_constraints.py | 112 ++++++++++++++----------- operators/base_constraint.py | 38 +++++++++ 5 files changed, 144 insertions(+), 67 deletions(-) diff --git a/operators/add_angle.py b/operators/add_angle.py index 13760182..6740a3b9 100644 --- a/operators/add_angle.py +++ b/operators/add_angle.py @@ -9,6 +9,8 @@ from ..stateful_operator.utilities.register import register_stateops_factory from .base_constraint import GenericConstraintOp +from ..model.angle import SlvsAngle + logger = logging.getLogger(__name__) @@ -45,13 +47,14 @@ class VIEW3D_OT_slvs_add_angle(Operator, GenericConstraintOp): property_keys = ("value", "setting") def main(self, context): - self.target = context.scene.sketcher.constraints.add_angle( - self.entity1, - self.entity2, - sketch=self.sketch, - init=not self.initialized, - **self.get_settings() - ) + if not self.exists(context, SlvsAngle): + self.target = context.scene.sketcher.constraints.add_angle( + self.entity1, + self.entity2, + sketch=self.sketch, + init=not self.initialized, + **self.get_settings() + ) return super().main(context) diff --git a/operators/add_diameter.py b/operators/add_diameter.py index 3a0b18d9..3393a386 100644 --- a/operators/add_diameter.py +++ b/operators/add_diameter.py @@ -7,6 +7,8 @@ from ..stateful_operator.utilities.register import register_stateops_factory from .base_constraint import GenericConstraintOp +from ..model.diameter import SlvsDiameter + logger = logging.getLogger(__name__) @@ -30,12 +32,13 @@ class VIEW3D_OT_slvs_add_diameter(Operator, GenericConstraintOp): property_keys = ("value", "setting") def main(self, context): - self.target = context.scene.sketcher.constraints.add_diameter( - self.entity1, - sketch=self.sketch, - init=not self.initialized, - **self.get_settings(), - ) + if not self.exists(context, SlvsDiameter): + self.target = context.scene.sketcher.constraints.add_diameter( + self.entity1, + sketch=self.sketch, + init=not self.initialized, + **self.get_settings(), + ) return super().main(context) diff --git a/operators/add_distance.py b/operators/add_distance.py index 6aa7df99..74394c5c 100644 --- a/operators/add_distance.py +++ b/operators/add_distance.py @@ -8,6 +8,9 @@ from ..declarations import Operators from ..stateful_operator.utilities.register import register_stateops_factory +from ..model.distance import SlvsDistance +from ..model.line_2d import SlvsLine2D +from ..model.point_2d import SlvsPoint2D logger = logging.getLogger(__name__) @@ -33,13 +36,25 @@ class VIEW3D_OT_slvs_add_distance(Operator, GenericConstraintOp): property_keys = ("value", "align", "flip") def main(self, context): - self.target = context.scene.sketcher.constraints.add_distance( - self.entity1, - self.entity2, - sketch=self.sketch, - init=not self.initialized, - **self.get_settings(), - ) + if type(self.entity1) == SlvsLine2D and self.entity2 == None: + dependencies = self.entity1.dependencies() + if type(dependencies[0]) == SlvsPoint2D and type(dependencies[1]) == SlvsPoint2D: + # for loop changes the values of self.entity1 and self.entity2 from a line to its endpoints + for i in range(0,2): + self.state_index = i # set index to access i state_data + self.state_data["hovered"] = -1 + self.state_data["type"] = type(dependencies[i]) + self.state_data["is_existing_entity"] = True + self.state_data["entity_index"] = dependencies[i].slvs_index + + if not self.exists(context, SlvsDistance): + self.target = context.scene.sketcher.constraints.add_distance( + self.entity1, + self.entity2, + sketch=self.sketch, + init=not self.initialized, + **self.get_settings(), + ) return super().main(context) def fini(self, context: Context, succeede: bool): diff --git a/operators/add_geometric_constraints.py b/operators/add_geometric_constraints.py index 3306878c..d3487e21 100644 --- a/operators/add_geometric_constraints.py +++ b/operators/add_geometric_constraints.py @@ -12,6 +12,15 @@ from ..utilities.view import refresh from ..solver import solve_system +from ..model.coincident import SlvsCoincident +from ..model.equal import SlvsEqual +from ..model.vertical import SlvsVertical +from ..model.horizontal import SlvsHorizontal +from ..model.parallel import SlvsParallel +from ..model.perpendicular import SlvsPerpendicular +from ..model.tangent import SlvsTangent +from ..model.midpoint import SlvsMidpoint +from ..model.ratio import SlvsRatio logger = logging.getLogger(__name__) @@ -52,11 +61,12 @@ def main(self, context: Context): if self.handle_merge(context): return True - self.target = context.scene.sketcher.constraints.add_coincident( - self.entity1, - self.entity2, - sketch=self.sketch, - ) + if not self.exists(context, SlvsCoincident): + self.target = context.scene.sketcher.constraints.add_coincident( + self.entity1, + self.entity2, + sketch=self.sketch, + ) return super().main(context) @@ -70,11 +80,12 @@ class VIEW3D_OT_slvs_add_equal(Operator, GenericConstraintOp): type = "EQUAL" def main(self, context): - self.target = context.scene.sketcher.constraints.add_equal( - self.entity1, - self.entity2, - sketch=self.sketch, - ) + if not self.exists(context, SlvsEqual): + self.target = context.scene.sketcher.constraints.add_equal( + self.entity1, + self.entity2, + sketch=self.sketch, + ) return super().main(context) @@ -89,11 +100,12 @@ class VIEW3D_OT_slvs_add_vertical(Operator, GenericConstraintOp): type = "VERTICAL" def main(self, context): - self.target = context.scene.sketcher.constraints.add_vertical( - self.entity1, - entity2=self.entity2, - sketch=self.sketch, - ) + if not self.exists(context, SlvsVertical): + self.target = context.scene.sketcher.constraints.add_vertical( + self.entity1, + entity2=self.entity2, + sketch=self.sketch, + ) return super().main(context) @@ -108,11 +120,12 @@ class VIEW3D_OT_slvs_add_horizontal(Operator, GenericConstraintOp): type = "HORIZONTAL" def main(self, context): - self.target = context.scene.sketcher.constraints.add_horizontal( - self.entity1, - entity2=self.entity2, - sketch=self.sketch, - ) + if not self.exists(context, SlvsHorizontal): + self.target = context.scene.sketcher.constraints.add_horizontal( + self.entity1, + entity2=self.entity2, + sketch=self.sketch, + ) return super().main(context) @@ -127,11 +140,12 @@ class VIEW3D_OT_slvs_add_parallel(Operator, GenericConstraintOp): type = "PARALLEL" def main(self, context): - self.target = context.scene.sketcher.constraints.add_parallel( - self.entity1, - self.entity2, - sketch=self.sketch, - ) + if not self.exists(context, SlvsParallel): + self.target = context.scene.sketcher.constraints.add_parallel( + self.entity1, + self.entity2, + sketch=self.sketch, + ) return super().main(context) @@ -146,11 +160,12 @@ class VIEW3D_OT_slvs_add_perpendicular(Operator, GenericConstraintOp): type = "PERPENDICULAR" def main(self, context): - self.target = context.scene.sketcher.constraints.add_perpendicular( - self.entity1, - self.entity2, - sketch=self.sketch, - ) + if not self.exists(context, SlvsPerpendicular): + self.target = context.scene.sketcher.constraints.add_perpendicular( + self.entity1, + self.entity2, + sketch=self.sketch, + ) return super().main(context) @@ -165,11 +180,12 @@ class VIEW3D_OT_slvs_add_tangent(Operator, GenericConstraintOp): type = "TANGENT" def main(self, context): - self.target = context.scene.sketcher.constraints.add_tangent( - self.entity1, - self.entity2, - sketch=self.sketch, - ) + if not self.exists(context, SlvsTangent): + self.target = context.scene.sketcher.constraints.add_tangent( + self.entity1, + self.entity2, + sketch=self.sketch, + ) return super().main(context) @@ -184,11 +200,12 @@ class VIEW3D_OT_slvs_add_midpoint(Operator, GenericConstraintOp): type = "MIDPOINT" def main(self, context): - self.target = context.scene.sketcher.constraints.add_midpoint( - self.entity1, - self.entity2, - sketch=self.sketch, - ) + if not self.exists(context, SlvsMidpoint): + self.target = context.scene.sketcher.constraints.add_midpoint( + self.entity1, + self.entity2, + sketch=self.sketch, + ) return super().main(context) @@ -211,13 +228,14 @@ class VIEW3D_OT_slvs_add_ratio(Operator, GenericConstraintOp): property_keys = ("value",) def main(self, context): - self.target = context.scene.sketcher.constraints.add_ratio( - self.entity1, - self.entity2, - sketch=self.sketch, - init=not self.initialized, - **self.get_settings(), - ) + if not self.exists(context, SlvsRatio): + self.target = context.scene.sketcher.constraints.add_ratio( + self.entity1, + self.entity2, + sketch=self.sketch, + init=not self.initialized, + **self.get_settings(), + ) return super().main(context) diff --git a/operators/base_constraint.py b/operators/base_constraint.py index 49ee73bf..df9ba29d 100644 --- a/operators/base_constraint.py +++ b/operators/base_constraint.py @@ -14,6 +14,12 @@ from ..utilities.view import refresh from ..solver import solve_system +from ..model.distance import SlvsDistance +from ..model.types import SlvsPoint2D +from ..model.types import SlvsLine2D +from ..model.types import SlvsPoint3D +from ..model.types import SlvsLine3D + logger = logging.getLogger(__name__) @@ -124,3 +130,35 @@ def draw(self, context: Context): for key in self.property_keys: layout.prop(self, key) + + + def exists(self, context, constraint_type=None) -> bool: + if hasattr(self, "entity2"): + new_dependencies = [i for i in [self.entity1, self.entity2, self.sketch] if i is not None] + + if ((SlvsPoint3D == type(self.entity1) or SlvsPoint3D == type(self.entity2)) or + (SlvsLine3D == type(self.entity1) or SlvsLine3D == type(self.entity2))): + max_distance_dof = 3 + elif ((type(self.entity1) == SlvsLine2D and self.entity2 == None) or + type(self.entity1) == SlvsPoint2D and type(self.entity2) == SlvsPoint2D): + max_distance_dof = 2 + else: + max_distance_dof = 1 + else: + new_dependencies = [i for i in [self.entity1, self.sketch] if i is not None] + max_distance_dof = 1 + + + distance_dof = 0 + for c in context.scene.sketcher.constraints.all: + if type(c) == constraint_type: + if set(c.dependencies()) == set(new_dependencies): + if constraint_type == SlvsDistance: + distance_dof +=1 + else: + return True + + if distance_dof < max_distance_dof: + return False + else: + return True \ No newline at end of file From 743946c85a3bac98412e95b02ae7cbcc240420ac Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 18 Apr 2024 03:21:16 +0200 Subject: [PATCH 2/4] Flake8 cleanups --- operators/add_distance.py | 14 ++++++++------ operators/base_constraint.py | 20 +++++++++----------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/operators/add_distance.py b/operators/add_distance.py index 74394c5c..55a851f8 100644 --- a/operators/add_distance.py +++ b/operators/add_distance.py @@ -36,16 +36,18 @@ class VIEW3D_OT_slvs_add_distance(Operator, GenericConstraintOp): property_keys = ("value", "align", "flip") def main(self, context): - if type(self.entity1) == SlvsLine2D and self.entity2 == None: + if isinstance(self.entity1, SlvsLine2D) and self.entity2 is None: dependencies = self.entity1.dependencies() - if type(dependencies[0]) == SlvsPoint2D and type(dependencies[1]) == SlvsPoint2D: - # for loop changes the values of self.entity1 and self.entity2 from a line to its endpoints - for i in range(0,2): - self.state_index = i # set index to access i state_data + if (isinstance(dependencies[0], SlvsPoint2D) and + isinstance(dependencies[1], SlvsPoint2D)): + # for loop changes the values of self.entity1 and self.entity2 + # from a line entity to its endpoints + for i in range(0, 2): + self.state_index = i # set index to access i state_data self.state_data["hovered"] = -1 self.state_data["type"] = type(dependencies[i]) self.state_data["is_existing_entity"] = True - self.state_data["entity_index"] = dependencies[i].slvs_index + self.state_data["entity_index"] = dependencies[i].slvs_index if not self.exists(context, SlvsDistance): self.target = context.scene.sketcher.constraints.add_distance( diff --git a/operators/base_constraint.py b/operators/base_constraint.py index df9ba29d..b93b1cb3 100644 --- a/operators/base_constraint.py +++ b/operators/base_constraint.py @@ -131,16 +131,15 @@ def draw(self, context: Context): for key in self.property_keys: layout.prop(self, key) - def exists(self, context, constraint_type=None) -> bool: if hasattr(self, "entity2"): new_dependencies = [i for i in [self.entity1, self.entity2, self.sketch] if i is not None] - if ((SlvsPoint3D == type(self.entity1) or SlvsPoint3D == type(self.entity2)) or - (SlvsLine3D == type(self.entity1) or SlvsLine3D == type(self.entity2))): - max_distance_dof = 3 - elif ((type(self.entity1) == SlvsLine2D and self.entity2 == None) or - type(self.entity1) == SlvsPoint2D and type(self.entity2) == SlvsPoint2D): + if (isinstance(self.entity1, (SlvsPoint3D, SlvsLine3D)) or + isinstance(self.entity2, (SlvsPoint3D, SlvsLine3D))): + max_distance_dof = 3 + elif ((isinstance(self.entity1, SlvsLine2D) and self.entity2 is None) or + isinstance(self.entity1, SlvsPoint2D) and isinstance(self.entity2, SlvsPoint2D)): max_distance_dof = 2 else: max_distance_dof = 1 @@ -148,17 +147,16 @@ def exists(self, context, constraint_type=None) -> bool: new_dependencies = [i for i in [self.entity1, self.sketch] if i is not None] max_distance_dof = 1 - distance_dof = 0 for c in context.scene.sketcher.constraints.all: - if type(c) == constraint_type: + if isinstance(c, constraint_type): if set(c.dependencies()) == set(new_dependencies): if constraint_type == SlvsDistance: - distance_dof +=1 + distance_dof += 1 else: return True - + if distance_dof < max_distance_dof: return False else: - return True \ No newline at end of file + return True From b1ed6f8d7f10a493154eaa33b4fc1cef1485e67d Mon Sep 17 00:00:00 2001 From: Tim Date: Mon, 22 Apr 2024 19:34:20 +0200 Subject: [PATCH 3/4] Simplify exists method and version changes Simplified the exists method in base_constraint not to do specific checks and moved this logic into the add_distance operator. Added version check for older drawings. This updates all distance constraints on a line to the endpoints of that line. Changed addon version from 0.27.3 to 0.27.4 --- __init__.py | 2 +- blender_manifest.toml | 2 +- operators/add_distance.py | 28 ++++++++++++++++++++-------- operators/base_constraint.py | 33 +++++---------------------------- versioning.py | 17 +++++++++++++++++ 5 files changed, 44 insertions(+), 38 deletions(-) diff --git a/__init__.py b/__init__.py index 0d1f6d53..7a3d8693 100644 --- a/__init__.py +++ b/__init__.py @@ -7,7 +7,7 @@ bl_info = { "name": "CAD Sketcher", "author": "hlorus", - "version": (0, 27, 3), + "version": (0, 27, 4), "blender": (3, 3, 0), "location": "View3D > Toolbar", "description": "Parametric, constraint-based geometry sketcher", diff --git a/blender_manifest.toml b/blender_manifest.toml index 97b8e9f4..0f62ef68 100644 --- a/blender_manifest.toml +++ b/blender_manifest.toml @@ -1,7 +1,7 @@ schema_version = "1.0.0" id = "CAD_Sketcher" -version = "0.27.3" +version = "0.27.4" name = "CAD Sketcher" tagline = "Parametric, constraint-based geometry sketcher" maintainer = "hlorus " diff --git a/operators/add_distance.py b/operators/add_distance.py index 55a851f8..b426dd65 100644 --- a/operators/add_distance.py +++ b/operators/add_distance.py @@ -11,6 +11,8 @@ from ..model.distance import SlvsDistance from ..model.line_2d import SlvsLine2D from ..model.point_2d import SlvsPoint2D +from ..model.types import SlvsPoint3D +from ..model.types import SlvsLine3D logger = logging.getLogger(__name__) @@ -38,18 +40,28 @@ class VIEW3D_OT_slvs_add_distance(Operator, GenericConstraintOp): def main(self, context): if isinstance(self.entity1, SlvsLine2D) and self.entity2 is None: dependencies = self.entity1.dependencies() - if (isinstance(dependencies[0], SlvsPoint2D) and + if (isinstance(dependencies[0], SlvsPoint2D) and isinstance(dependencies[1], SlvsPoint2D)): # for loop changes the values of self.entity1 and self.entity2 # from a line entity to its endpoints for i in range(0, 2): - self.state_index = i # set index to access i state_data - self.state_data["hovered"] = -1 - self.state_data["type"] = type(dependencies[i]) - self.state_data["is_existing_entity"] = True - self.state_data["entity_index"] = dependencies[i].slvs_index - - if not self.exists(context, SlvsDistance): + state_data = self.get_state_data(i) + state_data["hovered"] = -1 + state_data["type"] = type(dependencies[i]) + state_data["is_existing_entity"] = True + state_data["entity_index"] = dependencies[i].slvs_index + self.next_state(context) # end user selection, no need for second entity + + if (isinstance(self.entity1, (SlvsPoint3D, SlvsLine3D)) or + isinstance(self.entity2, (SlvsPoint3D, SlvsLine3D))): + max_constraints = 3 + elif ((isinstance(self.entity1, SlvsLine2D) and self.entity2 is None) or + isinstance(self.entity1, SlvsPoint2D) and isinstance(self.entity2, SlvsPoint2D)): + max_constraints = 2 + else: + max_constraints = 1 + + if not self.exists(context, SlvsDistance, max_constraints): self.target = context.scene.sketcher.constraints.add_distance( self.entity1, self.entity2, diff --git a/operators/base_constraint.py b/operators/base_constraint.py index b93b1cb3..05044257 100644 --- a/operators/base_constraint.py +++ b/operators/base_constraint.py @@ -10,15 +10,6 @@ from ..utilities.select import deselect_all from ..utilities.view import refresh from .base_2d import Operator2d -from ..utilities.select import deselect_all -from ..utilities.view import refresh -from ..solver import solve_system - -from ..model.distance import SlvsDistance -from ..model.types import SlvsPoint2D -from ..model.types import SlvsLine2D -from ..model.types import SlvsPoint3D -from ..model.types import SlvsLine3D logger = logging.getLogger(__name__) @@ -131,32 +122,18 @@ def draw(self, context: Context): for key in self.property_keys: layout.prop(self, key) - def exists(self, context, constraint_type=None) -> bool: + def exists(self, context, constraint_type=None, max_constraints=1) -> bool: if hasattr(self, "entity2"): new_dependencies = [i for i in [self.entity1, self.entity2, self.sketch] if i is not None] - - if (isinstance(self.entity1, (SlvsPoint3D, SlvsLine3D)) or - isinstance(self.entity2, (SlvsPoint3D, SlvsLine3D))): - max_distance_dof = 3 - elif ((isinstance(self.entity1, SlvsLine2D) and self.entity2 is None) or - isinstance(self.entity1, SlvsPoint2D) and isinstance(self.entity2, SlvsPoint2D)): - max_distance_dof = 2 - else: - max_distance_dof = 1 else: new_dependencies = [i for i in [self.entity1, self.sketch] if i is not None] - max_distance_dof = 1 - distance_dof = 0 + constraint_counter = 0 for c in context.scene.sketcher.constraints.all: if isinstance(c, constraint_type): if set(c.dependencies()) == set(new_dependencies): - if constraint_type == SlvsDistance: - distance_dof += 1 - else: + constraint_counter += 1 + if constraint_counter >= max_constraints: return True - if distance_dof < max_distance_dof: - return False - else: - return True + return False diff --git a/versioning.py b/versioning.py index f3e3a2af..756e8658 100644 --- a/versioning.py +++ b/versioning.py @@ -100,5 +100,22 @@ def do_versioning(self): ) ) c.is_reference = True + if version < (0, 27, 4): + # update distance constraints on only a line + # to distance constraints on the endpoints of that line. + from .model.line_2d import SlvsLine2D + from .model.distance import SlvsDistance + from .model.point_2d import SlvsPoint2D + from .model.sketch import SlvsSketch + for c in context.scene.sketcher.constraints.dimensional: + if len(c.dependencies()) == 2: + if (isinstance(c, SlvsDistance) and + isinstance(c.dependencies()[0], SlvsLine2D) and + isinstance(c.dependencies()[1], SlvsSketch)): + line_dependencies = c.dependencies()[0].dependencies() + if (isinstance(line_dependencies[0], SlvsPoint2D) and + isinstance(line_dependencies[1], SlvsPoint2D)): + setattr(c, "entity1", line_dependencies[0]) + setattr(c, "entity2", line_dependencies[1]) logger.debug(msg) From 7d9d534b9349a06b71aa80a917445648b2001af2 Mon Sep 17 00:00:00 2001 From: Tim Date: Tue, 23 Apr 2024 16:27:18 +0200 Subject: [PATCH 4/4] Removed nested if statements Added extra check on length of line_entities to avoid errors if it would somehow be out of bounds --- versioning.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/versioning.py b/versioning.py index 756e8658..8b857362 100644 --- a/versioning.py +++ b/versioning.py @@ -100,6 +100,7 @@ def do_versioning(self): ) ) c.is_reference = True + if version < (0, 27, 4): # update distance constraints on only a line # to distance constraints on the endpoints of that line. @@ -107,15 +108,21 @@ def do_versioning(self): from .model.distance import SlvsDistance from .model.point_2d import SlvsPoint2D from .model.sketch import SlvsSketch + for c in context.scene.sketcher.constraints.dimensional: - if len(c.dependencies()) == 2: - if (isinstance(c, SlvsDistance) and + if len(c.dependencies()) != 2: + continue + if not (isinstance(c, SlvsDistance) and isinstance(c.dependencies()[0], SlvsLine2D) and - isinstance(c.dependencies()[1], SlvsSketch)): - line_dependencies = c.dependencies()[0].dependencies() - if (isinstance(line_dependencies[0], SlvsPoint2D) and - isinstance(line_dependencies[1], SlvsPoint2D)): - setattr(c, "entity1", line_dependencies[0]) - setattr(c, "entity2", line_dependencies[1]) + isinstance(c.dependencies()[1], SlvsSketch)): + continue + + line_dependencies = c.dependencies()[0].dependencies() + if len(line_dependencies) != 3: + continue + if (isinstance(line_dependencies[0], SlvsPoint2D) and + isinstance(line_dependencies[1], SlvsPoint2D)): + setattr(c, "entity1", line_dependencies[0]) + setattr(c, "entity2", line_dependencies[1]) logger.debug(msg)