sevgi-geometry 0.93.1 → 0.95.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.
@@ -10,8 +10,11 @@ module Sevgi
10
10
  # Default maximum number of sweep iterations.
11
11
  LIMIT = 1_000
12
12
 
13
- # Sweeps parallel lines across an element in both directions.
14
- # @param element [Sevgi::Geometry::Element] element to intersect
13
+ # Sweeps parallel lines across a lined element in both directions.
14
+ #
15
+ # Generated lines are boundary-to-boundary interior spans. A single
16
+ # sweep position can produce multiple lines for closed concave elements; open paths produce no interior lines.
17
+ # @param element [Sevgi::Geometry::Element::Lined] element to intersect
15
18
  # @param initial [Sevgi::Geometry::Point, Array<Numeric>] point on the initial sweep line
16
19
  # @param angle [Numeric] clockwise sweep line angle in degrees
17
20
  # @param step [Numeric] signed distance between sweep lines
@@ -33,8 +36,11 @@ module Sevgi
33
36
  end
34
37
  end
35
38
 
36
- # Sweeps parallel lines and requires at least one result.
37
- # @param element [Sevgi::Geometry::Element] element to intersect
39
+ # Sweeps parallel lines across a lined element and requires at least one result.
40
+ #
41
+ # Generated lines are boundary-to-boundary interior spans. A single
42
+ # sweep position can produce multiple lines for closed concave elements; open paths produce no interior lines.
43
+ # @param element [Sevgi::Geometry::Element::Lined] element to intersect
38
44
  # @param initial [Sevgi::Geometry::Point, Array<Numeric>] point on the initial sweep line
39
45
  # @param angle [Numeric] clockwise sweep line angle in degrees
40
46
  # @param step [Numeric] signed distance between sweep lines
@@ -56,7 +62,10 @@ module Sevgi
56
62
  end
57
63
 
58
64
  # Sweeps parallel lines in one signed direction from an equation.
59
- # @param element [Sevgi::Geometry::Element] element to intersect
65
+ #
66
+ # Generated lines are boundary-to-boundary interior spans. A single
67
+ # sweep position can produce multiple lines for closed concave elements; open paths produce no interior lines.
68
+ # @param element [Sevgi::Geometry::Element::Lined] element to intersect
60
69
  # @param equation [Sevgi::Geometry::Equation] initial sweep equation
61
70
  # @param step [Numeric] signed distance between sweep lines
62
71
  # @param limit [Integer] maximum iterations
@@ -69,9 +78,7 @@ module Sevgi
69
78
  points = element.intersection(equation)
70
79
  return lines if points.empty?
71
80
 
72
- if points.size == 2 && !(line = Line.(*points)).ignorable?
73
- lines << line
74
- end
81
+ lines.concat(interior_lines(element, equation, points))
75
82
 
76
83
  equation = equation.shift(step)
77
84
  end
@@ -86,9 +93,45 @@ module Sevgi
86
93
  def applicable?(element)
87
94
  element.respond_to?(:intersection)
88
95
  end
96
+
97
+ private
98
+
99
+ def interior_lines(element, equation, points)
100
+ return [] if element.class.respond_to?(:open?) && element.class.open?
101
+
102
+ if points.size == 2
103
+ line = simple_line(points)
104
+
105
+ return line ? [line] : []
106
+ end
107
+
108
+ sorted_points(equation, points).each_cons(2).filter_map do |starting, ending|
109
+ next unless element.inside?(midpoint(starting, ending))
110
+
111
+ simple_line([starting, ending])
112
+ end
113
+ end
114
+
115
+ def midpoint(starting, ending)
116
+ Point[(starting.x + ending.x) / 2.0, (starting.y + ending.y) / 2.0]
117
+ end
118
+
119
+ def simple_line(points)
120
+ line = Line.(*points)
121
+
122
+ line unless line.ignorable?
123
+ end
124
+
125
+ def sorted_points(equation, points)
126
+ points.sort_by do |point|
127
+ equation.is_a?(Equation::Linear::Vertical) ? [point.y, point.x] : [point.x, point.y]
128
+ end
129
+ end
89
130
  end
90
131
 
91
132
  register(Sweep, :sweep, :sweep!, :unisweep)
133
+
134
+ private_constant :Sweep
92
135
  end
93
136
  end
94
137
  end
@@ -12,6 +12,66 @@ module Sevgi
12
12
  # Raised when an operation does not apply to the target element.
13
13
  OperationInapplicableError = Class.new(Error)
14
14
 
15
+ # @!parse
16
+ # class << self
17
+ # # Returns an element translated to align with another element.
18
+ # # @param element [Sevgi::Geometry::Element] element to move
19
+ # # @param other [Sevgi::Geometry::Element] reference element
20
+ # # @param alignment [Symbol] one of :center, :left, :right, :top, or :bottom
21
+ # # @return [Sevgi::Geometry::Element] translated element
22
+ # # @raise [Sevgi::Geometry::Operation::OperationInapplicableError] when an argument is not a geometry element
23
+ # # @raise [Sevgi::ArgumentError] when alignment is unknown
24
+ # def align(element, other, alignment = :center); end
25
+ #
26
+ # # Returns the offset needed to align one element with another.
27
+ # # @param element [Sevgi::Geometry::Element] element to move
28
+ # # @param other [Sevgi::Geometry::Element] reference element
29
+ # # @param alignment [Symbol] one of :center, :left, :right, :top, or :bottom
30
+ # # @return [Sevgi::Geometry::Point] translation offset
31
+ # # @raise [Sevgi::Geometry::Operation::OperationInapplicableError] when an argument is not a geometry element
32
+ # # @raise [Sevgi::ArgumentError] when alignment is unknown
33
+ # def alignment(element, other, alignment = :center); end
34
+ #
35
+ # # Sweeps parallel lines across a lined element in both directions.
36
+ # # @param element [Sevgi::Geometry::Element::Lined] element to intersect
37
+ # # @param initial [Sevgi::Geometry::Point, Array<Numeric>] point on the initial sweep line
38
+ # # @param angle [Numeric] clockwise sweep line angle in degrees
39
+ # # @param step [Numeric] signed distance between sweep lines
40
+ # # @param limit [Integer] maximum iterations per direction
41
+ # # @yield [lines] optional hook receiving the generated lines
42
+ # # @yieldparam lines [Array<Sevgi::Geometry::Line>] generated sweep lines
43
+ # # @yieldreturn [void]
44
+ # # @return [Array<Sevgi::Geometry::Line>] generated sweep lines
45
+ # # @raise [Sevgi::Geometry::Operation::OperationInapplicableError] when element is not sweepable
46
+ # # @raise [Sevgi::Geometry::Error] when initial cannot be coerced
47
+ # # @raise [Sevgi::Geometry::Operation::OperationError] when iteration reaches the limit
48
+ # def sweep(element, initial:, angle:, step:, limit: Sweep::LIMIT); end
49
+ #
50
+ # # Sweeps parallel lines across a lined element and requires at least one result.
51
+ # # @param element [Sevgi::Geometry::Element::Lined] element to intersect
52
+ # # @param initial [Sevgi::Geometry::Point, Array<Numeric>] point on the initial sweep line
53
+ # # @param angle [Numeric] clockwise sweep line angle in degrees
54
+ # # @param step [Numeric] signed distance between sweep lines
55
+ # # @param limit [Integer] maximum iterations per direction
56
+ # # @yield [lines] optional hook receiving the generated lines
57
+ # # @yieldparam lines [Array<Sevgi::Geometry::Line>] generated sweep lines
58
+ # # @yieldreturn [void]
59
+ # # @return [Array<Sevgi::Geometry::Line>] generated sweep lines
60
+ # # @raise [Sevgi::Geometry::Operation::OperationInapplicableError] when element is not sweepable
61
+ # # @raise [Sevgi::Geometry::Error] when initial cannot be coerced
62
+ # # @raise [Sevgi::Geometry::Operation::OperationError] when no lines are found or iteration reaches the limit
63
+ # def sweep!(element, initial:, angle:, step:, limit: Sweep::LIMIT); end
64
+ #
65
+ # # Sweeps parallel lines in one signed direction from an equation.
66
+ # # @param element [Sevgi::Geometry::Element::Lined] element to intersect
67
+ # # @param equation [Sevgi::Geometry::Equation] initial sweep equation
68
+ # # @param step [Numeric] signed distance between sweep lines
69
+ # # @param limit [Integer] maximum iterations
70
+ # # @return [Array<Sevgi::Geometry::Line>] generated sweep lines
71
+ # # @raise [Sevgi::Geometry::Operation::OperationInapplicableError] when element is not sweepable
72
+ # # @raise [Sevgi::Geometry::Operation::OperationError] when iteration reaches the limit
73
+ # def unisweep(element, equation, step, limit: Sweep::LIMIT); end
74
+ # end
15
75
  # Registers one or more public operation methods.
16
76
  # @api private
17
77
  # @param handler [Module] operation handler module
@@ -19,6 +79,8 @@ module Sevgi
19
79
  # @return [Array<Symbol>] registered operation names
20
80
  def register(handler, *operations) = operations.each { |operation| def_operation(operation, handler) }
21
81
 
82
+ private :register
83
+
22
84
  private
23
85
 
24
86
  def def_operation(operation, handler)
@@ -100,13 +100,14 @@ module Sevgi
100
100
  # @param x [Numeric] x coordinate
101
101
  # @param y [Numeric] y coordinate
102
102
  # @return [void]
103
- def initialize(x:, y:) = super(x: x.to_f, y: y.to_f)
103
+ # @raise [Sevgi::Geometry::Error] when a coordinate is not a finite Numeric
104
+ def initialize(x:, y:) = super(x: Real[:x, x], y: Real[:y, y])
104
105
 
105
106
  # Compares points by x, then y.
106
107
  # @param other [Sevgi::Geometry::Point, Array<Numeric>] point to compare
107
108
  # @return [Integer, nil]
108
109
  # @raise [Sevgi::Geometry::Error] when other cannot be coerced
109
- def <=>(other) = (other = Tuple[Point, other]).nan? || nan? ? nil : deconstruct <=> other.deconstruct
110
+ def <=>(other) = deconstruct <=> Tuple[Point, other].deconstruct
110
111
 
111
112
  # Reports whether this point is at or above another point in screen coordinates.
112
113
  # @param other [Sevgi::Geometry::Point, Array<Numeric>] point to compare
@@ -141,20 +142,12 @@ module Sevgi
141
142
  # @return [Integer]
142
143
  def hash = [self.class, *deconstruct].hash
143
144
 
144
- # Reports whether any coordinate is infinite.
145
- # @return [Boolean]
146
- def infinite? = deconstruct.any?(&:infinite?)
147
-
148
145
  # Reports whether this point is at or left of another point.
149
146
  # @param other [Sevgi::Geometry::Point, Array<Numeric>] point to compare
150
147
  # @return [Boolean]
151
148
  # @raise [Sevgi::Geometry::Error] when other cannot be coerced
152
149
  def left?(other) = x <= Tuple[Point, other].x
153
150
 
154
- # Reports whether any coordinate is NaN.
155
- # @return [Boolean]
156
- def nan? = deconstruct.any?(&:nan?)
157
-
158
151
  # Reports whether this point is at or right of another point.
159
152
  # @param other [Sevgi::Geometry::Point, Array<Numeric>] point to compare
160
153
  # @return [Boolean]
@@ -66,13 +66,14 @@ module Sevgi
66
66
  # @param length [Numeric] segment length
67
67
  # @param angle [Numeric] clockwise angle in degrees
68
68
  # @return [void]
69
- def initialize(length:, angle:) = super(length: length.to_f, angle: angle.to_f)
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])
70
71
 
71
72
  # Compares segments by length.
72
73
  # @param other [Sevgi::Geometry::Segment, Array<Numeric>] segment to compare
73
74
  # @return [Integer, nil]
74
75
  # @raise [Sevgi::Geometry::Error] when other cannot be coerced
75
- def <=>(other) = (other = Tuple[Segment, other]).nan? || nan? ? nil : length <=> other.length
76
+ def <=>(other) = length <=> Tuple[Segment, other].length
76
77
 
77
78
  # Returns a segment rounded to precision.
78
79
  # @param precision [Integer, nil] decimal precision, or nil for the current function default
@@ -101,20 +102,12 @@ module Sevgi
101
102
  # @return [Integer]
102
103
  def hash = [self.class, *deconstruct].hash
103
104
 
104
- # Reports whether any component is infinite.
105
- # @return [Boolean]
106
- def infinite? = deconstruct.any?(&:infinite?)
107
-
108
105
  # Converts the segment into a line at a point.
109
106
  # @param point [Sevgi::Geometry::Point, Array<Numeric>] line start point
110
107
  # @return [Sevgi::Geometry::Line]
111
108
  # @raise [Sevgi::Geometry::Error] when point cannot be coerced
112
109
  def line(point = Origin) = Line[length, angle, position: Tuple[Point, point]]
113
110
 
114
- # Reports whether any component is NaN.
115
- # @return [Boolean]
116
- def nan? = deconstruct.any?(&:nan?)
117
-
118
111
  # Returns the opposite segment.
119
112
  # @return [Sevgi::Geometry::Segment]
120
113
  def reverse = with(angle: angle + 180.0)
@@ -3,6 +3,6 @@
3
3
  module Sevgi
4
4
  module Geometry
5
5
  # Component version.
6
- VERSION = "0.93.1"
6
+ VERSION = "0.95.0"
7
7
  end
8
8
  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.93.1
4
+ version: 0.95.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Recai Oktaş
@@ -15,26 +15,25 @@ dependencies:
15
15
  requirements:
16
16
  - - '='
17
17
  - !ruby/object:Gem::Version
18
- version: 0.93.1
18
+ version: 0.95.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.93.1
25
+ version: 0.95.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
38
  - lib/sevgi/geometry/elements/parallelogram.rb
40
39
  - lib/sevgi/geometry/elements/polygon.rb
@@ -67,14 +66,14 @@ 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
  - - ">="
74
73
  - !ruby/object:Gem::Version
75
74
  version: '0'
76
75
  requirements: []
77
- rubygems_version: 4.0.10
76
+ rubygems_version: 4.0.11
78
77
  specification_version: 4
79
78
  summary: Tiny library for geometric computations.
80
79
  test_files: []
@@ -1 +0,0 @@
1
-
@@ -1 +0,0 @@
1
-
@@ -1 +0,0 @@
1
-