symcad.core.Coordinate

  1#!/usr/bin/env python3
  2# Copyright (C) 2022, Will Hedgecock
  3#
  4# This program is free software: you can redistribute it and/or modify
  5# it under the terms of the GNU General Public License as published by
  6# the Free Software Foundation, either version 3 of the License, or
  7# (at your option) any later version.
  8#
  9# This program is distributed in the hope that it will be useful,
 10# but WITHOUT ANY WARRANTY; without even the implied warranty of
 11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 12# GNU General Public License for more details.
 13#
 14# You should have received a copy of the GNU General Public License
 15# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 16
 17from __future__ import annotations
 18from typing import List, Optional, Tuple, Union
 19from sympy import Expr, Symbol
 20from copy import deepcopy
 21
 22class Coordinate(object):
 23   """Represents a set of XYZ Cartesian coordinates."""
 24
 25   # Public attributes ----------------------------------------------------------------------------
 26
 27   name: str
 28   """Unique, identifying name of the `Coordinate` instance."""
 29
 30   x: Union[float, Expr]
 31   """X-axis coordinate in meters."""
 32
 33   y: Union[float, Expr]
 34   """Y-axis coordinate in meters."""
 35
 36   z: Union[float, Expr]
 37   """Z-axis coordinate in meters."""
 38
 39
 40   # Constructor ----------------------------------------------------------------------------------
 41
 42   def __init__(self, identifier: str, **kwargs) -> None:
 43      """Initializes a `Coordinate` instance, where `identifier` uniquely identifies the instance,
 44      and `**kwargs` may contain the keywords `x`, `y`, or `z` to specify a concrete value
 45      for each axis of the coordinate. If any of these keywords are missing, the corresponding
 46      coordinate will be treated as a symbol.
 47      """
 48      super().__init__()
 49      self.name = identifier
 50      self.x = kwargs.get('x', Symbol(identifier + '_x'))
 51      self.y = kwargs.get('y', Symbol(identifier + '_y'))
 52      self.z = kwargs.get('z', Symbol(identifier + '_z'))
 53
 54
 55   # Built-in method implementations --------------------------------------------------------------
 56
 57   def __repr__(self) -> str:
 58      return 'x = {} m, y = {} m, z = {} m'.format(self.x, self.y, self.z)
 59
 60   def __eq__(self, other: Coordinate) -> bool:
 61      return (self.x == other.x) and (self.y == other.y) and (self.z == other.z)
 62
 63   def __copy__(self):
 64      copy = self.__class__.__new__(self.__class__)
 65      copy.__dict__.update(self.__dict__)
 66      return copy
 67
 68   def __deepcopy__(self, memo):
 69      copy = self.__class__.__new__(self.__class__)
 70      memo[id(self)] = copy
 71      for key, val in self.__dict__.items():
 72         setattr(copy, key, deepcopy(val, memo))
 73      return copy
 74
 75
 76   # Public methods -------------------------------------------------------------------------------
 77
 78   def clone(self, concrete_values: Optional[List[Tuple[str, float]]] = None) -> Coordinate:
 79      """Returns an exact clone of this `Coordinate` instance, optionally replacing any symbolic
 80      parameters with their corresponding values as specified in the `concrete_values` list."""
 81      copy = deepcopy(self)
 82      if concrete_values is not None:
 83         if isinstance(copy.x, Expr):
 84            copy.x = copy.x.subs(concrete_values)
 85         if isinstance(copy.y, Expr):
 86            copy.y = copy.y.subs(concrete_values)
 87         if isinstance(copy.z, Expr):
 88            copy.z = copy.z.subs(concrete_values)
 89      return copy
 90
 91
 92   def copy_from(self, other: Coordinate) -> Coordinate:
 93      """Copies the coordinates from another `Coordinate` instance into this one.
 94
 95      The name of this instance will not be changed or overwritten.
 96
 97      Parameters
 98      ----------
 99      other : `Coordinate`
100         A Coordinate object whose contents should be copied into this instance.
101
102      Returns
103      -------
104      self : `Coordinate`
105         The Coordinate instance being manipulated.
106      """
107      self.x = other.x
108      self.y = other.y
109      self.z = other.z
110      return self
111
112
113   def set(self, *, x: Union[float, Expr, None],
114                    y: Union[float, Expr, None],
115                    z: Union[float, Expr, None]) -> Coordinate:
116      """Sets the Cartesian coordinates to the specified values.
117
118      Parameters
119      ----------
120      x : `Union[float, sympy.Expr, None]`
121         Desired x-axis coordinate in meters. If `None` is specified, the coordinate
122         will be treated as a symbol.
123      y : `Union[float, sympy.Expr, None]`
124         Desired y-axis coordinate in meters. If `None` is specified, the coordinate
125         will be treated as a symbol.
126      z : `Union[float, sympy.Expr, None]`
127         Desired z-axis coordinate in meters. If `None` is specified, the coordinate
128         will be treated as a symbol.
129
130      Returns
131      -------
132      self : `Coordinate`
133         The Coordinate instance being manipulated.
134      """
135      self.x = x if x is not None else Symbol(self.name + '_x')
136      self.y = y if y is not None else Symbol(self.name + '_y')
137      self.z = z if z is not None else Symbol(self.name + '_z')
138      return self
139
140
141   def clear(self) -> Coordinate:
142      """Clears all coordinates to `<0, 0, 0>`.
143
144      Returns
145      -------
146      self : `Coordinate`
147         The Coordinate instance being manipulated.
148      """
149      self.x = self.y = self.z = 0.0
150      return self
151
152
153   def as_tuple(self) -> Tuple[float, float, float]:
154      """Returns the current XYZ coordinates as a `tuple`."""
155      return self.x, self.y, self.z
class Coordinate:
 23class Coordinate(object):
 24   """Represents a set of XYZ Cartesian coordinates."""
 25
 26   # Public attributes ----------------------------------------------------------------------------
 27
 28   name: str
 29   """Unique, identifying name of the `Coordinate` instance."""
 30
 31   x: Union[float, Expr]
 32   """X-axis coordinate in meters."""
 33
 34   y: Union[float, Expr]
 35   """Y-axis coordinate in meters."""
 36
 37   z: Union[float, Expr]
 38   """Z-axis coordinate in meters."""
 39
 40
 41   # Constructor ----------------------------------------------------------------------------------
 42
 43   def __init__(self, identifier: str, **kwargs) -> None:
 44      """Initializes a `Coordinate` instance, where `identifier` uniquely identifies the instance,
 45      and `**kwargs` may contain the keywords `x`, `y`, or `z` to specify a concrete value
 46      for each axis of the coordinate. If any of these keywords are missing, the corresponding
 47      coordinate will be treated as a symbol.
 48      """
 49      super().__init__()
 50      self.name = identifier
 51      self.x = kwargs.get('x', Symbol(identifier + '_x'))
 52      self.y = kwargs.get('y', Symbol(identifier + '_y'))
 53      self.z = kwargs.get('z', Symbol(identifier + '_z'))
 54
 55
 56   # Built-in method implementations --------------------------------------------------------------
 57
 58   def __repr__(self) -> str:
 59      return 'x = {} m, y = {} m, z = {} m'.format(self.x, self.y, self.z)
 60
 61   def __eq__(self, other: Coordinate) -> bool:
 62      return (self.x == other.x) and (self.y == other.y) and (self.z == other.z)
 63
 64   def __copy__(self):
 65      copy = self.__class__.__new__(self.__class__)
 66      copy.__dict__.update(self.__dict__)
 67      return copy
 68
 69   def __deepcopy__(self, memo):
 70      copy = self.__class__.__new__(self.__class__)
 71      memo[id(self)] = copy
 72      for key, val in self.__dict__.items():
 73         setattr(copy, key, deepcopy(val, memo))
 74      return copy
 75
 76
 77   # Public methods -------------------------------------------------------------------------------
 78
 79   def clone(self, concrete_values: Optional[List[Tuple[str, float]]] = None) -> Coordinate:
 80      """Returns an exact clone of this `Coordinate` instance, optionally replacing any symbolic
 81      parameters with their corresponding values as specified in the `concrete_values` list."""
 82      copy = deepcopy(self)
 83      if concrete_values is not None:
 84         if isinstance(copy.x, Expr):
 85            copy.x = copy.x.subs(concrete_values)
 86         if isinstance(copy.y, Expr):
 87            copy.y = copy.y.subs(concrete_values)
 88         if isinstance(copy.z, Expr):
 89            copy.z = copy.z.subs(concrete_values)
 90      return copy
 91
 92
 93   def copy_from(self, other: Coordinate) -> Coordinate:
 94      """Copies the coordinates from another `Coordinate` instance into this one.
 95
 96      The name of this instance will not be changed or overwritten.
 97
 98      Parameters
 99      ----------
100      other : `Coordinate`
101         A Coordinate object whose contents should be copied into this instance.
102
103      Returns
104      -------
105      self : `Coordinate`
106         The Coordinate instance being manipulated.
107      """
108      self.x = other.x
109      self.y = other.y
110      self.z = other.z
111      return self
112
113
114   def set(self, *, x: Union[float, Expr, None],
115                    y: Union[float, Expr, None],
116                    z: Union[float, Expr, None]) -> Coordinate:
117      """Sets the Cartesian coordinates to the specified values.
118
119      Parameters
120      ----------
121      x : `Union[float, sympy.Expr, None]`
122         Desired x-axis coordinate in meters. If `None` is specified, the coordinate
123         will be treated as a symbol.
124      y : `Union[float, sympy.Expr, None]`
125         Desired y-axis coordinate in meters. If `None` is specified, the coordinate
126         will be treated as a symbol.
127      z : `Union[float, sympy.Expr, None]`
128         Desired z-axis coordinate in meters. If `None` is specified, the coordinate
129         will be treated as a symbol.
130
131      Returns
132      -------
133      self : `Coordinate`
134         The Coordinate instance being manipulated.
135      """
136      self.x = x if x is not None else Symbol(self.name + '_x')
137      self.y = y if y is not None else Symbol(self.name + '_y')
138      self.z = z if z is not None else Symbol(self.name + '_z')
139      return self
140
141
142   def clear(self) -> Coordinate:
143      """Clears all coordinates to `<0, 0, 0>`.
144
145      Returns
146      -------
147      self : `Coordinate`
148         The Coordinate instance being manipulated.
149      """
150      self.x = self.y = self.z = 0.0
151      return self
152
153
154   def as_tuple(self) -> Tuple[float, float, float]:
155      """Returns the current XYZ coordinates as a `tuple`."""
156      return self.x, self.y, self.z

Represents a set of XYZ Cartesian coordinates.

Coordinate(identifier: str, **kwargs)
43   def __init__(self, identifier: str, **kwargs) -> None:
44      """Initializes a `Coordinate` instance, where `identifier` uniquely identifies the instance,
45      and `**kwargs` may contain the keywords `x`, `y`, or `z` to specify a concrete value
46      for each axis of the coordinate. If any of these keywords are missing, the corresponding
47      coordinate will be treated as a symbol.
48      """
49      super().__init__()
50      self.name = identifier
51      self.x = kwargs.get('x', Symbol(identifier + '_x'))
52      self.y = kwargs.get('y', Symbol(identifier + '_y'))
53      self.z = kwargs.get('z', Symbol(identifier + '_z'))

Initializes a Coordinate instance, where identifier uniquely identifies the instance, and **kwargs may contain the keywords x, y, or z to specify a concrete value for each axis of the coordinate. If any of these keywords are missing, the corresponding coordinate will be treated as a symbol.

name: str

Unique, identifying name of the Coordinate instance.

x: Union[float, sympy.core.expr.Expr]

X-axis coordinate in meters.

y: Union[float, sympy.core.expr.Expr]

Y-axis coordinate in meters.

z: Union[float, sympy.core.expr.Expr]

Z-axis coordinate in meters.

def clone( self, concrete_values: Optional[List[Tuple[str, float]]] = None) -> Coordinate:
79   def clone(self, concrete_values: Optional[List[Tuple[str, float]]] = None) -> Coordinate:
80      """Returns an exact clone of this `Coordinate` instance, optionally replacing any symbolic
81      parameters with their corresponding values as specified in the `concrete_values` list."""
82      copy = deepcopy(self)
83      if concrete_values is not None:
84         if isinstance(copy.x, Expr):
85            copy.x = copy.x.subs(concrete_values)
86         if isinstance(copy.y, Expr):
87            copy.y = copy.y.subs(concrete_values)
88         if isinstance(copy.z, Expr):
89            copy.z = copy.z.subs(concrete_values)
90      return copy

Returns an exact clone of this Coordinate instance, optionally replacing any symbolic parameters with their corresponding values as specified in the concrete_values list.

def copy_from( self, other: Coordinate) -> Coordinate:
 93   def copy_from(self, other: Coordinate) -> Coordinate:
 94      """Copies the coordinates from another `Coordinate` instance into this one.
 95
 96      The name of this instance will not be changed or overwritten.
 97
 98      Parameters
 99      ----------
100      other : `Coordinate`
101         A Coordinate object whose contents should be copied into this instance.
102
103      Returns
104      -------
105      self : `Coordinate`
106         The Coordinate instance being manipulated.
107      """
108      self.x = other.x
109      self.y = other.y
110      self.z = other.z
111      return self

Copies the coordinates from another Coordinate instance into this one.

The name of this instance will not be changed or overwritten.

Parameters
  • other (Coordinate): A Coordinate object whose contents should be copied into this instance.
Returns
  • self (Coordinate): The Coordinate instance being manipulated.
def set( self, *, x: Union[float, sympy.core.expr.Expr, NoneType], y: Union[float, sympy.core.expr.Expr, NoneType], z: Union[float, sympy.core.expr.Expr, NoneType]) -> Coordinate:
114   def set(self, *, x: Union[float, Expr, None],
115                    y: Union[float, Expr, None],
116                    z: Union[float, Expr, None]) -> Coordinate:
117      """Sets the Cartesian coordinates to the specified values.
118
119      Parameters
120      ----------
121      x : `Union[float, sympy.Expr, None]`
122         Desired x-axis coordinate in meters. If `None` is specified, the coordinate
123         will be treated as a symbol.
124      y : `Union[float, sympy.Expr, None]`
125         Desired y-axis coordinate in meters. If `None` is specified, the coordinate
126         will be treated as a symbol.
127      z : `Union[float, sympy.Expr, None]`
128         Desired z-axis coordinate in meters. If `None` is specified, the coordinate
129         will be treated as a symbol.
130
131      Returns
132      -------
133      self : `Coordinate`
134         The Coordinate instance being manipulated.
135      """
136      self.x = x if x is not None else Symbol(self.name + '_x')
137      self.y = y if y is not None else Symbol(self.name + '_y')
138      self.z = z if z is not None else Symbol(self.name + '_z')
139      return self

Sets the Cartesian coordinates to the specified values.

Parameters
  • x (Union[float, sympy.Expr, None]): Desired x-axis coordinate in meters. If None is specified, the coordinate will be treated as a symbol.
  • y (Union[float, sympy.Expr, None]): Desired y-axis coordinate in meters. If None is specified, the coordinate will be treated as a symbol.
  • z (Union[float, sympy.Expr, None]): Desired z-axis coordinate in meters. If None is specified, the coordinate will be treated as a symbol.
Returns
  • self (Coordinate): The Coordinate instance being manipulated.
def clear(self) -> Coordinate:
142   def clear(self) -> Coordinate:
143      """Clears all coordinates to `<0, 0, 0>`.
144
145      Returns
146      -------
147      self : `Coordinate`
148         The Coordinate instance being manipulated.
149      """
150      self.x = self.y = self.z = 0.0
151      return self

Clears all coordinates to <0, 0, 0>.

Returns
  • self (Coordinate): The Coordinate instance being manipulated.
def as_tuple(self) -> Tuple[float, float, float]:
154   def as_tuple(self) -> Tuple[float, float, float]:
155      """Returns the current XYZ coordinates as a `tuple`."""
156      return self.x, self.y, self.z

Returns the current XYZ coordinates as a tuple.