Coverage for pyTooling / Cartesian2D / Shapes.py: 75%

43 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-02-20 22:29 +0000

1# ==================================================================================================================== # 

2# _____ _ _ ____ _ _ ____ ____ # 

3# _ __ _ |_ _|__ ___ | (_)_ __ __ _ / ___|__ _ _ __| |_ ___ ___(_) __ _ _ __ |___ \| _ \ # 

4# | '_ \| | | || |/ _ \ / _ \| | | '_ \ / _` || | / _` | '__| __/ _ \/ __| |/ _` | '_ \ __) | | | | # 

5# | |_) | |_| || | (_) | (_) | | | | | | (_| || |__| (_| | | | || __/\__ \ | (_| | | | |/ __/| |_| | # 

6# | .__/ \__, ||_|\___/ \___/|_|_|_| |_|\__, (_)____\__,_|_| \__\___||___/_|\__,_|_| |_|_____|____/ # 

7# |_| |___/ |___/ # 

8# ==================================================================================================================== # 

9# Authors: # 

10# Patrick Lehmann # 

11# # 

12# License: # 

13# ==================================================================================================================== # 

14# Copyright 2025-2026 Patrick Lehmann - Bötzingen, Germany # 

15# # 

16# Licensed under the Apache License, Version 2.0 (the "License"); # 

17# you may not use this file except in compliance with the License. # 

18# You may obtain a copy of the License at # 

19# # 

20# http://www.apache.org/licenses/LICENSE-2.0 # 

21# # 

22# Unless required by applicable law or agreed to in writing, software # 

23# distributed under the License is distributed on an "AS IS" BASIS, # 

24# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # 

25# See the License for the specific language governing permissions and # 

26# limitations under the License. # 

27# # 

28# SPDX-License-Identifier: Apache-2.0 # 

29# ==================================================================================================================== # 

30# 

31"""An implementation of 2D cartesian shapes for Python.""" 

32 

33from typing import Generic, Tuple 

34 

35from pyTooling.Decorators import export 

36from pyTooling.Common import getFullyQualifiedName 

37from pyTooling.Cartesian2D import Coordinate, Point2D, LineSegment2D 

38 

39 

40@export 

41class Shape(Generic[Coordinate]): 

42 """Base-class for all 2D cartesian shapes.""" 

43 

44 

45@export 

46class Trapezium(Shape[Coordinate], Generic[Coordinate]): 

47 """ 

48 A Trapezium is a four-sided polygon, having four edges (sides) and four corners (vertices). 

49 """ 

50 points: Tuple[Point2D[Coordinate], ...] #: A tuple of 2D-points describing the trapezium. 

51 segments: Tuple[LineSegment2D[Coordinate], ...] #: A tuple of 2D line segments describing the trapezium. 

52 

53 def __init__(self, p00: Point2D[Coordinate], p01: Point2D[Coordinate], p11: Point2D[Coordinate], p10: Point2D[Coordinate]) -> None: 

54 """ 

55 Initializes a trapezium with 4 corners. 

56 

57 :param p00: First corner. 

58 :param p01: Second corner. 

59 :param p11: Third corner. 

60 :param p10: Forth corner 

61 """ 

62 if not isinstance(p00, Point2D): 

63 ex = TypeError(f"Parameter 'p00' is not of type Point2D.") 

64 ex.add_note(f"Got type '{getFullyQualifiedName(p00)}'.") 

65 raise ex 

66 if not isinstance(p01, Point2D): 

67 ex = TypeError(f"Parameter 'p01' is not of type Point2D.") 

68 ex.add_note(f"Got type '{getFullyQualifiedName(p01)}'.") 

69 raise ex 

70 if not isinstance(p11, Point2D): 

71 ex = TypeError(f"Parameter 'p11' is not of type Point2D.") 

72 ex.add_note(f"Got type '{getFullyQualifiedName(p11)}'.") 

73 raise ex 

74 if not isinstance(p10, Point2D): 

75 ex = TypeError(f"Parameter 'p10' is not of type Point2D.") 

76 ex.add_note(f"Got type '{getFullyQualifiedName(p10)}'.") 

77 raise ex 

78 

79 self.points = ( 

80 _p00 := p00.Copy(), 

81 _p01 := p01.Copy(), 

82 _p11 := p11.Copy(), 

83 _p10 := p10.Copy(), 

84 ) 

85 

86 self.segments = ( 

87 LineSegment2D(_p00, _p01, copyPoints=False), 

88 LineSegment2D(_p01, _p11, copyPoints=False), 

89 LineSegment2D(_p11, _p10, copyPoints=False), 

90 LineSegment2D(_p10, _p00, copyPoints=False) 

91 ) 

92 

93 

94@export 

95class Rectangle(Trapezium[Coordinate]): 

96 """ 

97 A rectangle is a trapezium, where opposite edges a parallel to each other and all inner angels are 90 |degree| . 

98 """ 

99 

100 def __init__(self, p00: Point2D[Coordinate], p01: Point2D[Coordinate], p11: Point2D[Coordinate], p10: Point2D[Coordinate]) -> None: 

101 """ 

102 Initializes a rectangle with 4 corners. 

103 

104 :param p00: First corner. 

105 :param p01: Second corner. 

106 :param p11: Third corner. 

107 :param p10: Forth corner 

108 """ 

109 super().__init__(p00, p01, p11, p10) 

110 

111 if self.segments[0].Length != self.segments[2].Length or self.segments[1].Length != self.segments[3].Length: 

112 raise ValueError(f"Line segments (edges) of opposite edges different lengths.") 

113 

114 if (self.segments[0].AngleTo(self.segments[1]) == 0.0 and self.segments[1].AngleTo(self.segments[2]) == 0.0 

115 and self.segments[2].AngleTo(self.segments[3]) == 0.0 and self.segments[3].AngleTo(self.segments[0]) == 0.0): 

116 raise ValueError(f"Line segments (edges) have no 90° angles.") 

117 

118 

119@export 

120class Square(Rectangle[Coordinate]): 

121 """ 

122 A square is a rectangle, where all edges have the same length and all inner angels are 90 |degree| . 

123 """ 

124 

125 def __init__(self, p00: Point2D[Coordinate], p01: Point2D[Coordinate], p11: Point2D[Coordinate], p10: Point2D[Coordinate]) -> None: 

126 """ 

127 Initializes a square with 4 corners. 

128 

129 :param p00: First corner. 

130 :param p01: Second corner. 

131 :param p11: Third corner. 

132 :param p10: Forth corner 

133 """ 

134 super().__init__(p00, p01, p11, p10) 

135 

136 if self.segments[0].Length != self.segments[1].Length: 

137 raise ValueError(f"Line segments (edges) between corners have different lengths.")