sevgi-geometry 0.73.2 → 0.94.0

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.
@@ -2,59 +2,134 @@
2
2
 
3
3
  module Sevgi
4
4
  module Geometry
5
+ # Immutable polar segment in SVG/screen coordinates.
6
+ #
7
+ # `length` is a distance and `angle` is a clockwise angle in degrees.
8
+ # Use `Segment[length, angle]` to create a segment from polar components.
5
9
  Segment = Data.define(:length, :angle) do
6
10
  include Comparable
7
11
 
12
+ # @!attribute [r] length
13
+ # @return [Float] segment length
14
+ # @!attribute [r] angle
15
+ # @return [Float] clockwise angle in degrees
16
+
17
+ # Creates a segment from start and end points.
18
+ # @param starting [Sevgi::Geometry::Point, Array<Numeric>] start point
19
+ # @param ending [Sevgi::Geometry::Point, Array<Numeric>] end point
20
+ # @return [Sevgi::Geometry::Segment]
21
+ # @raise [Sevgi::Geometry::Error] when either point cannot be coerced
8
22
  def self.call(starting, ending)
9
23
  starting, ending = Tuples[Point, starting, ending]
10
24
  self[Point.length(starting, ending), Point.angle(starting, ending)]
11
25
  end
12
26
 
27
+ # Compares two segments with optional numeric precision.
28
+ # @param this [Sevgi::Geometry::Segment, Array<Numeric>] first segment
29
+ # @param that [Sevgi::Geometry::Segment, Array<Numeric>] second segment
30
+ # @param precision [Integer, nil] decimal precision, or nil for the current function default
31
+ # @return [Boolean]
32
+ # @raise [Sevgi::Geometry::Error] when either segment cannot be coerced
13
33
  def self.eq?(this, that, precision: nil)
14
34
  this, that = Tuples[self, this, that]
15
35
  F.eq?(this.length, that.length, precision:) && F.eq?(this.angle, that.angle, precision:)
16
36
  end
17
37
 
18
- def self.horizontal!(length) = self[length, 180.0]
19
- def self.horizontal(length) = self[length, 0.0]
20
- def self.vertical!(length) = self[length, -90.0]
21
- def self.vertical(length) = self[length, 90.0]
22
-
23
- def initialize(length:, angle:) = super(length: length.to_f, angle: angle.to_f)
38
+ # Returns a downward segment.
39
+ # @param length [Numeric] segment length
40
+ # @return [Sevgi::Geometry::Segment]
41
+ def self.downward(length) = self[length, 90.0]
42
+
43
+ # Returns a leftward segment.
44
+ # @param length [Numeric] segment length
45
+ # @return [Sevgi::Geometry::Segment]
46
+ def self.leftward(length) = self[length, 180.0]
47
+
48
+ # Returns a rightward segment.
49
+ # @param length [Numeric] segment length
50
+ # @return [Sevgi::Geometry::Segment]
51
+ def self.rightward(length) = self[length, 0.0]
52
+
53
+ # Returns an upward segment.
54
+ # @param length [Numeric] segment length
55
+ # @return [Sevgi::Geometry::Segment]
56
+ def self.upward(length) = self[length, -90.0]
57
+
58
+ class << self
59
+ # @return [Sevgi::Geometry::Segment]
60
+ alias_method :horizontal, :rightward
61
+ # @return [Sevgi::Geometry::Segment]
62
+ alias_method :vertical, :downward
63
+ end
24
64
 
65
+ # Creates a segment.
66
+ # @param length [Numeric] segment length
67
+ # @param angle [Numeric] clockwise angle in degrees
68
+ # @return [void]
69
+ # @raise [Sevgi::Geometry::Error] when a component is not a finite Numeric
70
+ def initialize(length:, angle:) = super(length: Real[:length, length], angle: Real[:angle, angle])
71
+
72
+ # Compares segments by length.
73
+ # @param other [Sevgi::Geometry::Segment, Array<Numeric>] segment to compare
74
+ # @return [Integer, nil]
75
+ # @raise [Sevgi::Geometry::Error] when other cannot be coerced
25
76
  def <=>(other) = (other = Tuple[Segment, other]).nan? || nan? ? nil : length <=> other.length
26
77
 
78
+ # Returns a segment rounded to precision.
79
+ # @param precision [Integer, nil] decimal precision, or nil for the current function default
80
+ # @return [Sevgi::Geometry::Segment]
27
81
  def approx(precision = nil) = with(length: F.approx(length, precision), angle: F.approx(angle, precision))
28
82
 
29
- def com = angle - 90.0
30
-
83
+ # Returns the endpoint reached from a starting point.
84
+ # @param starting [Sevgi::Geometry::Point, Array<Numeric>] start point
85
+ # @return [Sevgi::Geometry::Point]
86
+ # @raise [Sevgi::Geometry::Error] when starting cannot be coerced
31
87
  def ending(starting) = Tuple[Point, starting].translate(x, y)
32
88
 
89
+ # Compares this segment with optional numeric precision.
90
+ # @param other [Sevgi::Geometry::Segment, Array<Numeric>] segment to compare
91
+ # @param precision [Integer, nil] decimal precision, or nil for the current function default
92
+ # @return [Boolean]
93
+ # @raise [Sevgi::Geometry::Error] when other cannot be coerced
33
94
  def eq?(other, precision: nil) = self.class.eq?(self, other, precision:)
34
95
 
96
+ # Reports strict segment equality.
97
+ # @param other [Object] object to compare
98
+ # @return [Boolean]
35
99
  def eql?(other) = self.class == other.class && deconstruct == other.deconstruct
36
100
 
101
+ # Returns a hash compatible with strict equality.
102
+ # @return [Integer]
37
103
  def hash = [self.class, *deconstruct].hash
38
104
 
105
+ # Reports whether any component is infinite.
106
+ # @return [Boolean]
39
107
  def infinite? = deconstruct.any?(&:infinite?)
40
108
 
109
+ # Converts the segment into a line at a point.
110
+ # @param point [Sevgi::Geometry::Point, Array<Numeric>] line start point
111
+ # @return [Sevgi::Geometry::Line]
112
+ # @raise [Sevgi::Geometry::Error] when point cannot be coerced
41
113
  def line(point = Origin) = Line[length, angle, position: Tuple[Point, point]]
42
114
 
43
- def lx = x.abs
44
-
45
- def ly = y.abs
46
-
115
+ # Reports whether any component is NaN.
116
+ # @return [Boolean]
47
117
  def nan? = deconstruct.any?(&:nan?)
48
118
 
119
+ # Returns the opposite segment.
120
+ # @return [Sevgi::Geometry::Segment]
49
121
  def reverse = with(angle: angle + 180.0)
50
122
 
51
- def sup = angle - 180.0
52
-
123
+ # Returns the x component of the segment.
124
+ # @return [Float]
53
125
  def x = length * F.cos(angle)
54
126
 
127
+ # Returns the y component of the segment.
128
+ # @return [Float]
55
129
  def y = length * F.sin(angle)
56
130
  end
57
131
 
58
- LengthAngle = Data.define(:length, :angle)
132
+ # Lightweight polar value used where a plain length/angle tuple is enough.
133
+ Polar = Data.define(:length, :angle)
59
134
  end
60
135
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  module Sevgi
4
4
  module Geometry
5
- VERSION = "0.73.2"
5
+ # Component version.
6
+ VERSION = "0.94.0"
6
7
  end
7
8
  end
@@ -12,3 +12,12 @@ require_relative "geometry/equation"
12
12
  require_relative "geometry/operation"
13
13
 
14
14
  require_relative "geometry/version"
15
+
16
+ module Sevgi
17
+ # Screen-space geometry primitives used by Sevgi layout and drawing helpers.
18
+ #
19
+ # Coordinates follow SVG screen conventions: +x points right, +y points down,
20
+ # and positive angles turn clockwise.
21
+ module Geometry
22
+ end
23
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sevgi-geometry
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.73.2
4
+ version: 0.94.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Recai Oktaş
@@ -15,32 +15,31 @@ dependencies:
15
15
  requirements:
16
16
  - - '='
17
17
  - !ruby/object:Gem::Version
18
- version: 0.73.2
18
+ version: 0.94.0
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - '='
24
24
  - !ruby/object:Gem::Version
25
- version: 0.73.2
25
+ version: 0.94.0
26
26
  description: Enhances the Sevgi toolkit with geometry objects and methods.
27
27
  email: roktas@gmail.com
28
28
  executables: []
29
29
  extensions: []
30
30
  extra_rdoc_files: []
31
31
  files:
32
+ - CHANGELOG.md
33
+ - LICENSE
32
34
  - README.md
33
35
  - lib/sevgi/geometry.rb
34
36
  - lib/sevgi/geometry/element.rb
35
- - lib/sevgi/geometry/elements/circle.rb
36
- - lib/sevgi/geometry/elements/curve.rb
37
- - lib/sevgi/geometry/elements/ellipse.rb
38
37
  - lib/sevgi/geometry/elements/line.rb
39
- - lib/sevgi/geometry/elements/parm.rb
38
+ - lib/sevgi/geometry/elements/parallelogram.rb
40
39
  - lib/sevgi/geometry/elements/polygon.rb
41
40
  - lib/sevgi/geometry/elements/polyline.rb
42
41
  - lib/sevgi/geometry/elements/rect.rb
43
- - lib/sevgi/geometry/elements/tri.rb
42
+ - lib/sevgi/geometry/elements/triangle.rb
44
43
  - lib/sevgi/geometry/equation.rb
45
44
  - lib/sevgi/geometry/equation/linear.rb
46
45
  - lib/sevgi/geometry/equation/quadratic.rb
@@ -67,7 +66,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
67
66
  requirements:
68
67
  - - ">="
69
68
  - !ruby/object:Gem::Version
70
- version: 3.4.0.pre.preview1
69
+ version: 3.4.0
71
70
  required_rubygems_version: !ruby/object:Gem::Requirement
72
71
  requirements:
73
72
  - - ">="
@@ -1 +0,0 @@
1
-
@@ -1 +0,0 @@
1
-
@@ -1 +0,0 @@
1
-
@@ -1,37 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Parallelogram
4
-
5
- module Sevgi
6
- module Geometry
7
- class Parm < Element.lined(4)
8
- def self.[](horizontal, vertical, position: Origin)
9
- horizontal, vertical = Tuples[Segment, horizontal, vertical]
10
-
11
- new_by_segments(horizontal, vertical.reverse, horizontal.reverse, vertical, position:)
12
- end
13
-
14
- def self.new_by_height(horizontal:, tallness:, position: Origin)
15
- horizontal = Tuple[Segment, horizontal]
16
- tallness = Tuple[LengthAngle, tallness]
17
-
18
- height = tallness.length - horizontal.ly
19
- angle = tallness.angle
20
- length = height / F.sin(angle)
21
-
22
- self[horizontal, Segment[length, angle], position:]
23
- end
24
-
25
- def self.new_by_width(vertical:, wideness:, position: Origin)
26
- vertical = Tuple[Segment, vertical]
27
- wideness = Tuple[LengthAngle, wideness]
28
-
29
- width = wideness.length - vertical.lx
30
- angle = wideness.angle
31
- length = width / F.cos(angle)
32
-
33
- self[Segment[length, angle], vertical, position:]
34
- end
35
- end
36
- end
37
- end
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Sevgi
4
- module Geometry
5
- class Tri < Element.lined(3)
6
- def self.[](segment_a, segment_b, position: Origin)
7
- a, b = Tuples[Segment, segment_a, segment_b]
8
-
9
- length = ::Math.sqrt((a.length ** 2) + (b.length ** 2) - (2 * a.length * b.length * F.cos(b.sup - a.angle)))
10
- angle = b.sup + F.asin(a.length * F.sin(b.sup - a.angle) / length)
11
-
12
- c = Segment[length, angle]
13
-
14
- new_by_segments(a, b, c, position:)
15
- end
16
- end
17
- end
18
- end