diff --git a/__init__.py b/__init__.py index 0d1f6d5..7a3d869 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 97b8e9f..0f62ef6 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_angle.py b/operators/add_angle.py index 1376018..6740a3b 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 3a0b18d..3393a38 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 6aa7df9..b426dd6 100644 --- a/operators/add_distance.py +++ b/operators/add_distance.py @@ -8,6 +8,11 @@ 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 +from ..model.types import SlvsPoint3D +from ..model.types import SlvsLine3D logger = logging.getLogger(__name__) @@ -33,13 +38,37 @@ 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 isinstance(self.entity1, SlvsLine2D) and self.entity2 is None: + dependencies = self.entity1.dependencies() + 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): + 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, + 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 3306878..d3487e2 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 49ee73b..0504425 100644 --- a/operators/base_constraint.py +++ b/operators/base_constraint.py @@ -10,9 +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 logger = logging.getLogger(__name__) @@ -124,3 +121,19 @@ def draw(self, context: Context): for key in self.property_keys: layout.prop(self, key) + + 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] + else: + new_dependencies = [i for i in [self.entity1, self.sketch] if i is not None] + + constraint_counter = 0 + for c in context.scene.sketcher.constraints.all: + if isinstance(c, constraint_type): + if set(c.dependencies()) == set(new_dependencies): + constraint_counter += 1 + if constraint_counter >= max_constraints: + return True + + return False diff --git a/versioning.py b/versioning.py index f3e3a2a..8b85736 100644 --- a/versioning.py +++ b/versioning.py @@ -101,4 +101,28 @@ 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: + continue + if not (isinstance(c, SlvsDistance) and + isinstance(c.dependencies()[0], SlvsLine2D) and + 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)