pyaltiumlib 0.2__tar.gz → 0.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. {pyaltiumlib-0.2/src/pyaltiumlib.egg-info → pyaltiumlib-0.4}/PKG-INFO +1 -1
  2. {pyaltiumlib-0.2 → pyaltiumlib-0.4}/src/pyaltiumlib/__init__.py +1 -6
  3. {pyaltiumlib-0.2 → pyaltiumlib-0.4}/src/pyaltiumlib/base.py +42 -44
  4. {pyaltiumlib-0.2 → pyaltiumlib-0.4}/src/pyaltiumlib/datatypes/__init__.py +3 -2
  5. pyaltiumlib-0.4/src/pyaltiumlib/datatypes/binaryreader.py +193 -0
  6. {pyaltiumlib-0.2 → pyaltiumlib-0.4}/src/pyaltiumlib/datatypes/coordinate.py +130 -50
  7. pyaltiumlib-0.4/src/pyaltiumlib/datatypes/mapping.py +39 -0
  8. pyaltiumlib-0.4/src/pyaltiumlib/datatypes/parametercollection.py +270 -0
  9. {pyaltiumlib-0.2 → pyaltiumlib-0.4}/src/pyaltiumlib/datatypes/pcbmapping.py +7 -7
  10. {pyaltiumlib-0.2 → pyaltiumlib-0.4}/src/pyaltiumlib/datatypes/schematicmapping.py +8 -8
  11. {pyaltiumlib-0.2 → pyaltiumlib-0.4}/src/pyaltiumlib/libcomponent.py +41 -43
  12. pyaltiumlib-0.4/src/pyaltiumlib/pcblib/footprint.py +103 -0
  13. {pyaltiumlib-0.2 → pyaltiumlib-0.4}/src/pyaltiumlib/pcblib/lib.py +36 -19
  14. {pyaltiumlib-0.2 → pyaltiumlib-0.4}/src/pyaltiumlib/pcblib/records/PCBArc.py +25 -33
  15. pyaltiumlib-0.4/src/pyaltiumlib/pcblib/records/PCBComponentBody.py +24 -0
  16. pyaltiumlib-0.4/src/pyaltiumlib/pcblib/records/PCBFill.py +97 -0
  17. pyaltiumlib-0.4/src/pyaltiumlib/pcblib/records/PCBPad.py +313 -0
  18. pyaltiumlib-0.4/src/pyaltiumlib/pcblib/records/PCBRegion.py +118 -0
  19. {pyaltiumlib-0.2 → pyaltiumlib-0.4}/src/pyaltiumlib/pcblib/records/PCBString.py +23 -70
  20. {pyaltiumlib-0.2 → pyaltiumlib-0.4}/src/pyaltiumlib/pcblib/records/PCBTrack.py +24 -53
  21. pyaltiumlib-0.4/src/pyaltiumlib/pcblib/records/PCBVia.py +143 -0
  22. {pyaltiumlib-0.2 → pyaltiumlib-0.4}/src/pyaltiumlib/pcblib/records/__init__.py +6 -2
  23. pyaltiumlib-0.4/src/pyaltiumlib/pcblib/records/base.py +191 -0
  24. {pyaltiumlib-0.2 → pyaltiumlib-0.4}/src/pyaltiumlib/schlib/lib.py +16 -7
  25. {pyaltiumlib-0.2 → pyaltiumlib-0.4}/src/pyaltiumlib/schlib/records/SchArc.py +26 -30
  26. {pyaltiumlib-0.2 → pyaltiumlib-0.4}/src/pyaltiumlib/schlib/records/SchBezier.py +34 -40
  27. pyaltiumlib-0.4/src/pyaltiumlib/schlib/records/SchComponent.py +45 -0
  28. pyaltiumlib-0.4/src/pyaltiumlib/schlib/records/SchDesignator.py +78 -0
  29. {pyaltiumlib-0.2 → pyaltiumlib-0.4}/src/pyaltiumlib/schlib/records/SchEllipse.py +25 -28
  30. {pyaltiumlib-0.2 → pyaltiumlib-0.4}/src/pyaltiumlib/schlib/records/SchEllipticalArc.py +28 -32
  31. pyaltiumlib-0.4/src/pyaltiumlib/schlib/records/SchImplementationList.py +20 -0
  32. {pyaltiumlib-0.2 → pyaltiumlib-0.4}/src/pyaltiumlib/schlib/records/SchLabel.py +26 -21
  33. {pyaltiumlib-0.2 → pyaltiumlib-0.4}/src/pyaltiumlib/schlib/records/SchLine.py +27 -19
  34. pyaltiumlib-0.4/src/pyaltiumlib/schlib/records/SchParameter.py +27 -0
  35. {pyaltiumlib-0.2 → pyaltiumlib-0.4}/src/pyaltiumlib/schlib/records/SchPin.py +31 -25
  36. {pyaltiumlib-0.2 → pyaltiumlib-0.4}/src/pyaltiumlib/schlib/records/SchPolygon.py +19 -29
  37. {pyaltiumlib-0.2 → pyaltiumlib-0.4}/src/pyaltiumlib/schlib/records/SchPolyline.py +25 -34
  38. {pyaltiumlib-0.2 → pyaltiumlib-0.4}/src/pyaltiumlib/schlib/records/SchRectangle.py +26 -31
  39. {pyaltiumlib-0.2 → pyaltiumlib-0.4}/src/pyaltiumlib/schlib/records/SchRoundRectangle.py +26 -34
  40. {pyaltiumlib-0.2 → pyaltiumlib-0.4}/src/pyaltiumlib/schlib/records/__init__.py +1 -1
  41. pyaltiumlib-0.4/src/pyaltiumlib/schlib/records/base.py +160 -0
  42. {pyaltiumlib-0.2 → pyaltiumlib-0.4}/src/pyaltiumlib/schlib/symbol.py +48 -21
  43. {pyaltiumlib-0.2 → pyaltiumlib-0.4/src/pyaltiumlib.egg-info}/PKG-INFO +1 -1
  44. {pyaltiumlib-0.2 → pyaltiumlib-0.4}/src/pyaltiumlib.egg-info/SOURCES.txt +3 -2
  45. pyaltiumlib-0.2/src/pyaltiumlib/datatypes/binaryreader.py +0 -129
  46. pyaltiumlib-0.2/src/pyaltiumlib/datatypes/mapping.py +0 -13
  47. pyaltiumlib-0.2/src/pyaltiumlib/datatypes/parametercollection.py +0 -157
  48. pyaltiumlib-0.2/src/pyaltiumlib/datatypes/schematicpin.py +0 -55
  49. pyaltiumlib-0.2/src/pyaltiumlib/datatypes/svg_utils.py +0 -39
  50. pyaltiumlib-0.2/src/pyaltiumlib/pcblib/footprint.py +0 -93
  51. pyaltiumlib-0.2/src/pyaltiumlib/pcblib/records/PCBComponentBody.py +0 -25
  52. pyaltiumlib-0.2/src/pyaltiumlib/pcblib/records/PCBPad.py +0 -322
  53. pyaltiumlib-0.2/src/pyaltiumlib/pcblib/records/base.py +0 -62
  54. pyaltiumlib-0.2/src/pyaltiumlib/schlib/records/SchComponent.py +0 -83
  55. pyaltiumlib-0.2/src/pyaltiumlib/schlib/records/SchDesignator.py +0 -42
  56. pyaltiumlib-0.2/src/pyaltiumlib/schlib/records/SchImplementationList.py +0 -42
  57. pyaltiumlib-0.2/src/pyaltiumlib/schlib/records/SchParameter.py +0 -32
  58. pyaltiumlib-0.2/src/pyaltiumlib/schlib/records/base.py +0 -137
  59. {pyaltiumlib-0.2 → pyaltiumlib-0.4}/LICENSE.txt +0 -0
  60. {pyaltiumlib-0.2 → pyaltiumlib-0.4}/README.md +0 -0
  61. {pyaltiumlib-0.2 → pyaltiumlib-0.4}/setup.cfg +0 -0
  62. {pyaltiumlib-0.2 → pyaltiumlib-0.4}/setup.py +0 -0
  63. {pyaltiumlib-0.2 → pyaltiumlib-0.4}/src/pyaltiumlib/datatypes/parametercolor.py +0 -0
  64. {pyaltiumlib-0.2 → pyaltiumlib-0.4}/src/pyaltiumlib/datatypes/parameterfont.py +0 -0
  65. {pyaltiumlib-0.2 → pyaltiumlib-0.4}/src/pyaltiumlib/datatypes/pcblayerdefinition.py +0 -0
  66. {pyaltiumlib-0.2 → pyaltiumlib-0.4}/src/pyaltiumlib/pcblib/__init__.py +0 -0
  67. {pyaltiumlib-0.2 → pyaltiumlib-0.4}/src/pyaltiumlib/schlib/__init__.py +0 -0
  68. {pyaltiumlib-0.2 → pyaltiumlib-0.4}/src/pyaltiumlib.egg-info/dependency_links.txt +0 -0
  69. {pyaltiumlib-0.2 → pyaltiumlib-0.4}/src/pyaltiumlib.egg-info/requires.txt +0 -0
  70. {pyaltiumlib-0.2 → pyaltiumlib-0.4}/src/pyaltiumlib.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: pyaltiumlib
3
- Version: 0.2
3
+ Version: 0.4
4
4
  Summary: PyAltiumLib is a tool to read Altium Designer library files.
5
5
  Home-page: https://github.com/ChrisHoyer/pyAltiumLib.git
6
6
  Author: Chris Hoyer
@@ -7,7 +7,7 @@ AUTHOR_NAME = 'Chris Hoyer'
7
7
  AUTHOR_EMAIL = 'info@chrishoyer.de'
8
8
  CYEAR = '2024-2025'
9
9
 
10
- __version__ = "0.2"
10
+ __version__ = "0.4"
11
11
  __author__ = "Chris Hoyer <info@chrishoyer.de>"
12
12
 
13
13
  import os
@@ -51,8 +51,3 @@ def read(filepath: str) -> Union[SchLib, PcbLib]:
51
51
  else:
52
52
  logger.error(f"Invalid file type: {filepath}.")
53
53
  raise
54
-
55
-
56
-
57
-
58
-
@@ -1,5 +1,6 @@
1
1
  from pyaltiumlib.datatypes import ParameterColor
2
2
 
3
+ import os
3
4
  import olefile
4
5
  from typing import List, Optional, Dict, Any
5
6
 
@@ -17,45 +18,44 @@ class GenericLibFile:
17
18
  :param string filepath: The path to the library file
18
19
 
19
20
  :raises FileNotFoundError: If file is not a supported file.
20
- """
21
-
22
- LibType = None
23
- """
24
- `string` that specifies the type of the library.
25
- """
26
-
27
- LibHeader = ''
28
- """`string` that contains the file path to the library.
29
- """
30
-
31
- FilePath = ''
32
- """
33
- `string` that stores the header information of the library.
34
- """
35
-
36
- ComponentCount = 0
37
- """
38
- `int` with total number of components in the library.
39
- """
40
-
41
- Parts = []
42
- """
43
- `List[any]` is a collection of components derived from :class:`pyaltiumlib.libcomponent.LibComponent` in their specific class
44
- contained in the library.
45
- """
21
+ """
46
22
 
47
23
  def __init__(self, filepath: str):
48
- """
49
- Initialize a GenericLibFile object.
50
- """
24
+
51
25
  if not olefile.isOleFile( filepath ):
52
- logger.error(f"{filepath} is not a supported file.")
26
+ logger.error(f"'{filepath}' is not a supported file.")
53
27
  raise
54
28
 
55
- self.LibType = type(self)
29
+ self.LibType = type(self)
30
+ """
31
+ `string` that specifies the type of the library.
32
+ """
33
+
34
+ self.LibHeader = ""
35
+ """
36
+ `string` that stores the header information of the library.
37
+ """
38
+
56
39
  self.FilePath = filepath
40
+ """
41
+ `string` that contains the file path to the library.
42
+ """
43
+
44
+ self.FileName = os.path.basename(filepath)
45
+ """
46
+ `string` that contains the name of the library.
47
+ """
48
+
57
49
  self.ComponentCount = 0
50
+ """
51
+ `int` with total number of components in the library.
52
+ """
53
+
58
54
  self.Parts = []
55
+ """
56
+ `List[any]` is a collection of components derived from :class:`pyaltiumlib.libcomponent.LibComponent` in their specific class
57
+ contained in the library.
58
+ """
59
59
 
60
60
  self._olefile = None
61
61
  self._olefile_open = False
@@ -66,19 +66,19 @@ class GenericLibFile:
66
66
  self._BackgroundColor = ParameterColor.from_hex("#6D6A69")
67
67
 
68
68
 
69
- def __repr__(self) -> Dict:
69
+ def __repr__(self) -> str:
70
70
  """
71
- Converts public attributes of the high level file to a dictionary.
71
+ Converts public attributes of the high level file to a string.
72
72
 
73
- :return: A dict representation of the content of the object
74
- :rtype: Dict
73
+ :return: A string representation of the content of the object
74
+ :rtype: str
75
75
  """
76
- return self.read_meta()
76
+ return str( self.read_meta() )
77
77
 
78
78
  # =============================================================================
79
79
  # External access
80
80
  # =============================================================================
81
-
81
+
82
82
  def read_meta(self) -> Dict:
83
83
  """
84
84
  Converts public attributes of the high level file to a dictionary.
@@ -92,8 +92,7 @@ class GenericLibFile:
92
92
  if not key.startswith("_")
93
93
  }
94
94
  return public_attributes
95
-
96
-
95
+
97
96
  def list_parts(self) -> List[str]:
98
97
  """
99
98
  List the names of all parts in the library.
@@ -102,8 +101,7 @@ class GenericLibFile:
102
101
  :rtype: List[str]
103
102
  """
104
103
  return [x.Name for x in self.Parts]
105
-
106
-
104
+
107
105
  def get_part(self, name: str) -> Optional[Any]:
108
106
  """
109
107
  Get a part of the library by its name.
@@ -128,13 +126,13 @@ class GenericLibFile:
128
126
  Open the library file for reading.
129
127
  """
130
128
  if self._olefile_open:
131
- raise ValueError(f"file: { self.FilePath }. Already open!")
129
+ raise ValueError(f"file: '{self.FilePath}'. Already open!")
132
130
 
133
131
  try:
134
132
  self._olefile = olefile.OleFileIO( self.FilePath )
135
133
  self._olefile_open = True
136
134
  except Exception as e:
137
- logger.error(f"Failed to open file: {self.FilePath}. Error: {e}")
135
+ logger.error(f"Failed to open file: '{self.FilePath}'. Error: {e}")
138
136
  raise
139
137
 
140
138
  def _OpenStream(self, container: str, stream: str) -> Any:
@@ -149,7 +147,7 @@ class GenericLibFile:
149
147
  Any: The opened stream.
150
148
  """
151
149
  if not self._olefile_open:
152
- logger.error(f"file: { self.FilePath }. File not open!")
150
+ logger.error(f"file: '{self.FilePath}'. File not opened!")
153
151
  raise
154
152
 
155
153
  if not container == "":
@@ -2,14 +2,15 @@
2
2
 
3
3
  """
4
4
 
5
- from .parametercollection import ParameterCollection
5
+
6
6
  from .parametercolor import ParameterColor
7
7
  from .parameterfont import ParameterFont
8
8
  from .binaryreader import BinaryReader
9
9
  from .coordinate import Coordinate, CoordinatePoint
10
+ from .parametercollection import ParameterCollection
10
11
 
11
12
  # Schematic related
12
- from .schematicpin import SchematicPin
13
+ from .parametercollection import SchematicPin
13
14
  from .schematicmapping import (
14
15
  SchematicLineWidth, SchematicLineStyle, SchematicLineShape,
15
16
  SchematicPinSymbol, SchematicPinElectricalType, SchematicTextOrientation,
@@ -0,0 +1,193 @@
1
+ from pyaltiumlib.datatypes.coordinate import Coordinate, CoordinatePoint
2
+
3
+ # Configure logging
4
+ import logging
5
+ logger = logging.getLogger(__name__)
6
+
7
+ class BinaryReader:
8
+ """
9
+ A utility class for reading binary data with structured methods.
10
+
11
+ :param bytes data: The binary data to be read.
12
+ """
13
+ def __init__(self, data):
14
+ self.data = data
15
+ self.offset = 0
16
+
17
+ @classmethod
18
+ def from_stream(cls, stream, size_length=4):
19
+ """
20
+ Reads a binary block from a stream and initializes a BinaryReader.
21
+
22
+ :param file-like object stream: The binary stream to read from.
23
+ :param int optional size_length: Number of bytes indicating the block size.
24
+ :return: An instance of BinaryReader.
25
+ :rtype: BinaryReader
26
+ """
27
+ length = int.from_bytes( stream.read( size_length ), "little" )
28
+ data = stream.read( length )
29
+
30
+ if len(data) != length:
31
+ logger.warning("Stream does not match the declared block length.")
32
+
33
+ return cls( data )
34
+
35
+ def has_content(self):
36
+ """
37
+ Checks if there is remaining data to read.
38
+
39
+ :return: True if data exists, False otherwise.
40
+ :rtype: bool
41
+ """
42
+ return not len(self.data) == 0
43
+
44
+ def length(self):
45
+ """
46
+ Returns the length of the binary data.
47
+
48
+ :return: The length of the data.
49
+ :rtype: int
50
+ """
51
+ return len(self.data)
52
+
53
+ def read(self, length):
54
+ """
55
+ Reads a specified number of bytes from the binary data.
56
+
57
+ :param int length: The number of bytes to read.
58
+ :return: The read bytes.
59
+ :rtype: bytes
60
+ """
61
+ if self.offset + length > len(self.data):
62
+ logger.warning("Not enough data to read the requested length.")
63
+
64
+ result = self.data[self.offset:self.offset + length]
65
+ self.offset += length
66
+ return result
67
+
68
+ def read_byte(self):
69
+ """
70
+ Reads a single byte from the binary data.
71
+
72
+ :return: The read byte.
73
+ :rtype: bytes
74
+ """
75
+ return self.read(1)
76
+
77
+ def read_int8(self, signed=False):
78
+ """
79
+ Reads an 8-bit integer.
80
+
81
+ :param bool optional signed: Whether to interpret the value as signed.
82
+ :return: The integer value.
83
+ :rtype: int
84
+ """
85
+ return int.from_bytes(self.read_byte(), signed=signed)
86
+
87
+ def read_int16(self, signed=False):
88
+ """
89
+ Reads a 16-bit integer.
90
+
91
+ :param bool optional signed: Whether to interpret the value as signed.
92
+ :return: The integer value.
93
+ :rtype: int
94
+ """
95
+ return int.from_bytes(self.read(2), byteorder="little", signed=signed)
96
+
97
+ def read_int32(self, signed=False):
98
+ """
99
+ Reads a 32-bit integer.
100
+
101
+ :param bool optional signed: Whether to interpret the value as signed.
102
+ :return: The integer value.
103
+ :rtype: int
104
+ """
105
+ return int.from_bytes(self.read(4), byteorder="little", signed=signed)
106
+
107
+ def read_double(self):
108
+ """
109
+ Reads an IEEE 754 double-precision floating point value.
110
+
111
+ :return: The double value.
112
+ :rtype: float
113
+ """
114
+ value = int.from_bytes(self.read(8), byteorder='little', signed=False)
115
+ sign = (value >> 63) & 0x1
116
+ exponent = (value >> 52) & 0x7FF
117
+ mantissa = value & ((1 << 52) - 1)
118
+
119
+ if exponent == 0x7FF:
120
+ return float('inf') if mantissa == 0 else float('nan')
121
+
122
+ if exponent == 0:
123
+ result = (mantissa / (1 << 52)) * (2 ** (-1022))
124
+ else:
125
+ result = (1 + (mantissa / (1 << 52))) * (2 ** (exponent - 1023))
126
+
127
+ return -result if sign == 1 else result
128
+
129
+ def read_string_block(self, size_string=1):
130
+ """
131
+ Reads a length-prefixed string.
132
+
133
+ :param int optional size_string: Number of bytes specifying the string length.
134
+
135
+ :return: The decoded string.
136
+ :rtype: str
137
+ """
138
+ length_string = int.from_bytes(self.read(size_string), "little")
139
+ string_data = self.read( length_string )
140
+
141
+ if len(string_data) != length_string:
142
+ logger.warning("String does not match the declared string length.")
143
+
144
+ return string_data.decode('windows-1252')
145
+
146
+ def read_bin_coord(self, scaley=-1.0, double=False, double_scaling = 1/10000.0):
147
+ """
148
+ Reads a binary coordinate pair and returns a CoordinatePoint.
149
+
150
+ :param float scaley: Scaling factor for the Y coordinate.
151
+ :param bool optional double: Read coordinates as double
152
+ :param float optional float: Scaling of double values
153
+
154
+ :return: A CoordinatePoint object.
155
+ :rtype: CoordinatePoint
156
+ """
157
+ if double:
158
+ x = (self.read_double() * double_scaling)
159
+ y = (self.read_double() * double_scaling) * scaley
160
+ return CoordinatePoint( Coordinate(x), Coordinate(y))
161
+
162
+ else:
163
+ x = self.read(4)
164
+ y = self.read(4)
165
+ return CoordinatePoint( Coordinate.parse_bin(x), Coordinate.parse_bin(y, scale=scaley))
166
+
167
+ def read_unicode_text(self, length=32, encoding='utf-16-le'):
168
+ """
169
+ Reads a Unicode string of fixed length.
170
+
171
+ :param int optional length: Maximum length of the string in bytes.
172
+ :param str optional encoding: The encoding format.
173
+
174
+ :return: The decoded Unicode string.
175
+ :rtype: str
176
+ """
177
+ pos = self.offset
178
+ data = []
179
+ while len(data) < length:
180
+
181
+ if self.offset + 2 > len(self.data):
182
+ logger.warning("Not enough data to read.")
183
+
184
+ unicode_char = self.read(2)
185
+ if unicode_char == b'\x00\x00': # Null terminator
186
+ break
187
+ data.extend(unicode_char)
188
+
189
+ # Ensure we skip the remaining bytes to read exactly `length` bytes
190
+ self.offset = pos + length
191
+
192
+ return bytes(data).decode(encoding)
193
+
@@ -1,30 +1,64 @@
1
1
  import math
2
2
 
3
- # =============================================================================
4
- # Single Coordinate
5
- # =============================================================================
3
+ # Set up logging
4
+ import logging
5
+ logger = logging.getLogger(__name__)
6
6
 
7
7
  class Coordinate:
8
+ """
9
+ Represents a single coordinate value.
10
+
11
+ This class provides parsing functions for extracting coordinates from
12
+ different data formats, as well as mathematical operations.
13
+
14
+ :param value: The numerical value of the coordinate.
15
+ :type value: int or float
16
+
17
+
18
+ **Operations:**
19
+
20
+ Supports addition, subtraction, multiplication, and division with both
21
+ integers, floats, and other `Coordinate` instances. Can be compared
22
+ using `<`, `>`, `<=`, `>=` with both `Coordinate` instances and numerical values.
23
+ """
24
+
8
25
  def __init__(self, value):
9
26
  self.value = value
10
-
27
+
11
28
  @classmethod
12
- def parse_dpx(cls, key, data, scale=1.0):
29
+ def parse_dpx(cls, key, data, scale=1.0):
30
+ """
31
+ Parses a coordinate from a parameter collection data dictionary (schematic file).
32
+
33
+ :param str key: The key corresponding to the coordinate.
34
+ :param dict data: The dictionary containing coordinate data.
35
+ :param float, optional scale: Scaling factor for the parsed coordinate.
36
+
37
+ :return: A Coordinate instance with the parsed value.
38
+ :rtype: Coordinate
39
+ """
13
40
  num = int(data.get(key, 0))
14
41
  frac = int(data.get(key + "_frac", 0))
15
-
16
42
  coord = (num * 10.0 + frac / 10000.0)
17
-
18
- return cls( scale * coord / 10 )
19
-
43
+ return cls(scale * coord / 10)
44
+
20
45
  @classmethod
21
46
  def parse_bin(cls, x_bytes, scale=1.0):
47
+ """
48
+ Parses a coordinate from binary data (pcb file).
49
+
50
+ :param bytes x_bytes: Binary representation of the coordinate.
51
+ :param float, optional scale: Scaling factor for the parsed coordinate.
52
+
53
+ :return: A Coordinate instance with the parsed value.
54
+ :rtype: Coordinate
55
+ """
22
56
  x = int.from_bytes(x_bytes, byteorder="little", signed=True)
23
-
24
- return cls( scale * x / 10000.0 )
25
-
57
+ return cls(scale * x / 10000.0)
58
+
26
59
  def __repr__(self):
27
- return f"{self.value}"
60
+ """Returns a string representation of the coordinate."""
61
+ return f"{self.value}"
28
62
 
29
63
  def __float__(self):
30
64
  return float(self.value)
@@ -33,28 +67,31 @@ class Coordinate:
33
67
  return int(self.value)
34
68
 
35
69
  # ================== Math Functions =========================================
36
-
70
+
37
71
  def __abs__(self):
38
- return abs( int(self.value) )
72
+ return abs(int(self.value))
39
73
 
40
74
  def __truediv__(self, other):
41
75
  if isinstance(other, (int, float)):
42
76
  if other == 0:
43
- raise ZeroDivisionError("Division by zero is not allowed.")
77
+ logger.warning("Division by zero is not allowed.")
78
+ raise
44
79
  return Coordinate(self.value / other)
80
+
45
81
  elif isinstance(other, Coordinate):
46
82
  if other.value == 0:
47
- raise ZeroDivisionError("Division by zero is not allowed.")
83
+ logger.warning("Division by zero is not allowed.")
84
+ raise
48
85
  return Coordinate(self.value / other.value)
49
86
  return NotImplemented
50
-
87
+
51
88
  def __mul__(self, other):
52
89
  if isinstance(other, (int, float)):
53
90
  return Coordinate(self.value * other)
54
91
  elif isinstance(other, Coordinate):
55
92
  return Coordinate(self.value * other.value)
56
93
  return NotImplemented
57
-
94
+
58
95
  def __add__(self, other):
59
96
  if isinstance(other, (int, float)):
60
97
  return Coordinate(self.value + other)
@@ -80,7 +117,7 @@ class Coordinate:
80
117
 
81
118
  def __rsub__(self, other):
82
119
  return self.__sub__(other)
83
-
120
+
84
121
  def __lt__(self, other):
85
122
  if isinstance(other, Coordinate):
86
123
  return self.value < other.value
@@ -100,56 +137,98 @@ class Coordinate:
100
137
 
101
138
  def __ge__(self, other):
102
139
  return self > other or self == other
103
-
140
+
141
+ # =============================================================================
104
142
  # =============================================================================
105
143
  # 2D Coordinate Point
106
144
  # =============================================================================
107
145
 
108
- class CoordinatePoint:
146
+ class CoordinatePoint:
147
+ """
148
+ This class encapsulates two `Coordinate` instances for the X and Y axes.
149
+
150
+ :param int, float, or Coordinate x: The X-coordinate value.
151
+ :param int, float, or Coordinate y: The Y-coordinate value.
152
+
153
+ **Mathematical Operations:**
154
+
155
+ Supports addition, subtraction, multiplication, and division with
156
+ `CoordinatePoint` instances or numerical values.
157
+ """
158
+
109
159
  def __init__(self, x, y):
110
160
  if not isinstance(x, Coordinate):
111
161
  x = Coordinate(x)
112
162
  if not isinstance(y, Coordinate):
113
163
  y = Coordinate(y)
114
-
115
164
  self.x = x
116
165
  self.y = y
117
-
166
+
118
167
  def __repr__(self):
119
- return f"({self.x};{self.y})"
120
-
168
+ """Returns a string representation of the coordinate point."""
169
+ return f"({self.x};{self.y})"
170
+
121
171
  def to_int(self):
122
- return CoordinatePoint( int(self.x), int(self.y))
172
+ """Converts the coordinate point to integer values."""
173
+ return CoordinatePoint(int(self.x), int(self.y))
123
174
 
124
175
  def to_int_tuple(self):
125
- return ( int(self.x), int(self.y))
126
-
176
+ """Returns the coordinate point as a tuple of integers."""
177
+ return int(self.x), int(self.y)
178
+
127
179
  def expand(self, size):
180
+ """
181
+ Expands the coordinate point by a given size.
182
+
183
+ :param size: The amount to expand by.
184
+ :type size: int, float, or Coordinate
185
+ :return: A new expanded CoordinatePoint.
186
+ :rtype: CoordinatePoint
187
+ """
128
188
  if isinstance(size, (int, float)):
129
189
  return CoordinatePoint(self.x + size, self.y + size)
130
190
  if isinstance(size, (int, Coordinate)):
131
191
  return CoordinatePoint(self.x + size.value, self.y - size.value)
132
-
192
+
133
193
  def rotate(self, center, angle):
134
-
135
- theta = math.radians(angle)
136
- x_rel = self.x - center.x
137
- y_rel = self.y - center.y
138
-
139
- x_rot = x_rel * math.cos(theta) - y_rel * math.sin(theta)
140
- y_rot = x_rel * math.sin(theta) + y_rel * math.cos(theta)
141
-
142
- self.x = x_rot + center.x
143
- self.y = y_rot + center.y
144
- return self
194
+ """
195
+ Rotates the coordinate point around a given center by an angle.
196
+
197
+ :param center: The center point for rotation.
198
+ :type center: CoordinatePoint
199
+ :param angle: The rotation angle in degrees.
200
+ :type angle: float
201
+ :return: The rotated CoordinatePoint.
202
+ :rtype: CoordinatePoint
203
+ """
204
+ theta = math.radians(angle)
205
+ x_rel = self.x - center.x
206
+ y_rel = self.y - center.y
207
+
208
+ x_rot = x_rel * math.cos(theta) - y_rel * math.sin(theta)
209
+ y_rot = x_rel * math.sin(theta) + y_rel * math.cos(theta)
210
+
211
+ self.x = x_rot + center.x
212
+ self.y = y_rot + center.y
213
+ return self
145
214
 
146
215
  def offset(self, offset_x, offset_y):
147
-
148
- self.x = self.x + offset_x
149
- self.y = self.y + offset_y
150
- return self
151
-
216
+ """
217
+ Offsets the coordinate point by given values.
218
+
219
+ :param offset_x: Offset for the X coordinate.
220
+ :type offset_x: int or float
221
+ :param offset_y: Offset for the Y coordinate.
222
+ :type offset_y: int or float
223
+ :return: The offset CoordinatePoint.
224
+ :rtype: CoordinatePoint
225
+ """
226
+ self.x = self.x + offset_x
227
+ self.y = self.y + offset_y
228
+ return self
229
+
152
230
  def copy(self):
231
+ """Returns a copy of the CoordinatePoint."""
153
232
  return CoordinatePoint(self.x, self.y)
154
233
 
155
234
  # ================== Math Functions =========================================
@@ -171,11 +250,13 @@ class CoordinatePoint:
171
250
  def __truediv__(self, other):
172
251
  if isinstance(other, (int, float)):
173
252
  if other == 0:
174
- raise ZeroDivisionError("Division by zero is not allowed.")
253
+ logger.warning("Division by zero is not allowed.")
254
+ raise
175
255
  return CoordinatePoint(self.x / other, self.y / other)
176
256
  elif isinstance(other, Coordinate):
177
257
  if other.value == 0:
178
- raise ZeroDivisionError("Division by zero is not allowed.")
258
+ logger.warning("Division by zero is not allowed.")
259
+ raise
179
260
  return CoordinatePoint(self.x / other.x, self.y / other.y)
180
261
  return NotImplemented
181
262
 
@@ -193,5 +274,4 @@ class CoordinatePoint:
193
274
  return self.__add__(other)
194
275
 
195
276
  def __rsub__(self, other):
196
- return self.__sub__(other)
197
-
277
+ return self.__sub__(other)