sevgi-geometry 0.95.0 → 1.0.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +221 -2
- data/README.md +33 -8
- data/lib/sevgi/geometry/element.rb +320 -161
- data/lib/sevgi/geometry/elements/arc.rb +256 -0
- data/lib/sevgi/geometry/elements/circle.rb +28 -0
- data/lib/sevgi/geometry/elements/ellipse/affine.rb +72 -0
- data/lib/sevgi/geometry/elements/ellipse/length.rb +80 -0
- data/lib/sevgi/geometry/elements/ellipse.rb +269 -0
- data/lib/sevgi/geometry/elements/line.rb +68 -23
- data/lib/sevgi/geometry/elements/parallelogram.rb +108 -29
- data/lib/sevgi/geometry/elements/polygon.rb +41 -1
- data/lib/sevgi/geometry/elements/polyline.rb +45 -1
- data/lib/sevgi/geometry/elements/rect.rb +163 -29
- data/lib/sevgi/geometry/elements/triangle.rb +57 -11
- data/lib/sevgi/geometry/equation/linear.rb +131 -69
- data/lib/sevgi/geometry/equation/quadratic.rb +98 -16
- data/lib/sevgi/geometry/equation.rb +65 -24
- data/lib/sevgi/geometry/internal.rb +11 -3
- data/lib/sevgi/geometry/operation/align.rb +2 -4
- data/lib/sevgi/geometry/operation/box.rb +50 -0
- data/lib/sevgi/geometry/operation/sweep.rb +32 -18
- data/lib/sevgi/geometry/operation.rb +33 -18
- data/lib/sevgi/geometry/point.rb +126 -14
- data/lib/sevgi/geometry/predicate.rb +176 -0
- data/lib/sevgi/geometry/segment.rb +79 -14
- data/lib/sevgi/geometry/version.rb +1 -1
- data/lib/sevgi/geometry.rb +26 -2
- metadata +13 -6
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Sevgi
|
|
4
|
+
module Geometry
|
|
5
|
+
# Internal helpers shared by public geometry predicates.
|
|
6
|
+
# @api private
|
|
7
|
+
module Predicate
|
|
8
|
+
extend self
|
|
9
|
+
|
|
10
|
+
def adjacent_overlap?(a, b, c, precision: nil)
|
|
11
|
+
orientation(a, b, c, precision:).zero? &&
|
|
12
|
+
(point_on_segment?(c, a, b, precision:) || point_on_segment?(a, b, c, precision:))
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def adjacent_overlap_in?(vertices, precision: nil)
|
|
16
|
+
vertices.each_index.any? do |i|
|
|
17
|
+
adjacent_overlap?(vertices[i - 1], vertices[i], vertices[(i + 1) % vertices.size], precision:)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def between?(value, a, b, precision: nil)
|
|
22
|
+
minimum, maximum = [a, b].minmax
|
|
23
|
+
F.ge?(value, minimum, precision:) && F.le?(value, maximum, precision:)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def collinear?(points, precision: nil)
|
|
27
|
+
# ponytail: cubic worst-case comparisons; optimize the maximum triangle area if large sets need faster queries.
|
|
28
|
+
points.sort_by { [it.x, it.y] }.combination(3).all? do |a, b, c|
|
|
29
|
+
orientation(a, b, c, precision:).zero?
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def convex_turns?(vertices, precision: nil)
|
|
34
|
+
turns = turn_orientations(vertices, precision:)
|
|
35
|
+
!turns.empty? && turns.uniq.one?
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def edges(vertices)
|
|
39
|
+
vertices.each_index.map { |i| [vertices[i], vertices[(i + 1) % vertices.size]] }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def nonadjacent_intersection?(vertices, precision: nil)
|
|
43
|
+
edges = edges(vertices)
|
|
44
|
+
# ponytail: quadratic comparisons; use a sweep-line algorithm if large polygons need faster queries.
|
|
45
|
+
edges.each_index.any? do |i|
|
|
46
|
+
((i + 1)...edges.size).any? do |j|
|
|
47
|
+
!adjacent_indices?(i, j, edges.size) && segments_intersect?(*edges[i], *edges[j], precision:)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def orientation(a, b, c, precision: nil)
|
|
53
|
+
cross = Cross[b.x - a.x, b.y - a.y, c.x - a.x, c.y - a.y]
|
|
54
|
+
return 0 if F.zero?(cross, precision:)
|
|
55
|
+
|
|
56
|
+
F.lt?(cross, 0.0, precision:) ? -1 : 1
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def point_on_segment?(point, a, b, precision: nil)
|
|
60
|
+
orientation(a, b, point, precision:).zero? &&
|
|
61
|
+
between?(point.x, a.x, b.x, precision:) &&
|
|
62
|
+
between?(point.y, a.y, b.y, precision:)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def repeated_vertex?(vertices, precision: nil)
|
|
66
|
+
vertices.each_index.any? do |i|
|
|
67
|
+
((i + 1)...vertices.size).any? { |j| Point.eq?(vertices[i], vertices[j], precision:) }
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def segments_intersect?(a, b, c, d, precision: nil)
|
|
72
|
+
first = [a, b]
|
|
73
|
+
second = [c, d]
|
|
74
|
+
turns = segment_orientations(*first, *second, precision:)
|
|
75
|
+
|
|
76
|
+
proper_intersection?(turns) || boundary_intersection?(first, second, turns, precision:)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def simple?(vertices, precision: nil)
|
|
80
|
+
!repeated_vertex?(vertices, precision:) &&
|
|
81
|
+
!adjacent_overlap_in?(vertices, precision:) &&
|
|
82
|
+
!nonadjacent_intersection?(vertices, precision:)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
private
|
|
86
|
+
|
|
87
|
+
def adjacent_indices?(i, j, size) = j == i + 1 || (i.zero? && j == size - 1)
|
|
88
|
+
|
|
89
|
+
def boundary_intersection?(first, second, turns, precision: nil)
|
|
90
|
+
a, b = first
|
|
91
|
+
c, d = second
|
|
92
|
+
candidates = [[turns[0], c, a, b], [turns[1], d, a, b], [turns[2], a, c, d], [turns[3], b, c, d]]
|
|
93
|
+
|
|
94
|
+
candidates.any? { |turn, point, from, to| turn.zero? && point_on_segment?(point, from, to, precision:) }
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def proper_intersection?(turns)
|
|
98
|
+
turns.none?(&:zero?) && turns[0] != turns[1] && turns[2] != turns[3]
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def segment_orientations(a, b, c, d, precision: nil)
|
|
102
|
+
[
|
|
103
|
+
orientation(a, b, c, precision:),
|
|
104
|
+
orientation(a, b, d, precision:),
|
|
105
|
+
orientation(c, d, a, precision:),
|
|
106
|
+
orientation(c, d, b, precision:)
|
|
107
|
+
]
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def turn_orientations(vertices, precision: nil)
|
|
111
|
+
vertices
|
|
112
|
+
.each_index
|
|
113
|
+
.map { |i|
|
|
114
|
+
orientation(vertices[i], vertices[(i + 1) % vertices.size], vertices[(i + 2) % vertices.size], precision:)
|
|
115
|
+
}
|
|
116
|
+
.reject(&:zero?)
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
private_constant :Predicate
|
|
121
|
+
|
|
122
|
+
class Point
|
|
123
|
+
# Reports whether three or more points lie on one infinite line.
|
|
124
|
+
#
|
|
125
|
+
# Every three-point subset must have a cross product that rounds to zero at the selected decimal precision.
|
|
126
|
+
# The cross-product magnitude is twice the triangle area, not a distance tolerance.
|
|
127
|
+
# Repeated points are allowed. Input order does not affect the result, and accepted sets have accepted subsets.
|
|
128
|
+
# Triangle area is invariant under rigid motion, subject to floating-point error near the rounding threshold.
|
|
129
|
+
# The worst-case number of comparisons is cubic in the point count.
|
|
130
|
+
# @param points [Array<Sevgi::Geometry::Point, Array<Numeric>>] point-like values
|
|
131
|
+
# @param precision [Integer, nil] decimal precision, or nil for the current function default
|
|
132
|
+
# @return [Boolean]
|
|
133
|
+
# @raise [Sevgi::ArgumentError] when fewer than three points are given or precision is not an Integer or nil
|
|
134
|
+
# @raise [Sevgi::Geometry::Error] when a point cannot be coerced
|
|
135
|
+
# @example Test a point set
|
|
136
|
+
# Sevgi::Geometry::Point.collinear?([0, 0], [1, 1], [2, 2]) # => true
|
|
137
|
+
def self.collinear?(*points, precision: nil)
|
|
138
|
+
ArgumentError.("At least three points required") if points.size < 3
|
|
139
|
+
|
|
140
|
+
Predicate.collinear?(Tuples[self, *points], precision:)
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
class Polygon
|
|
145
|
+
# Reports whether the polygon boundary has no self-intersection.
|
|
146
|
+
#
|
|
147
|
+
# Adjacent edges may meet only at their shared endpoint. Non-adjacent
|
|
148
|
+
# touches and overlaps make the polygon non-simple.
|
|
149
|
+
# @param precision [Integer, nil] decimal precision, or nil for the current function default
|
|
150
|
+
# @return [Boolean]
|
|
151
|
+
# @raise [Sevgi::ArgumentError] when precision is not an Integer or nil
|
|
152
|
+
def simple?(precision: nil) = Predicate.simple?(vertices, precision:)
|
|
153
|
+
|
|
154
|
+
# Reports whether this is a simple polygon whose non-collinear turns all have one orientation.
|
|
155
|
+
#
|
|
156
|
+
# Redundant vertices on straight edges are permitted. Self-intersecting
|
|
157
|
+
# and fully degenerate polygons are not convex.
|
|
158
|
+
# @param precision [Integer, nil] decimal precision, or nil for the current function default
|
|
159
|
+
# @return [Boolean]
|
|
160
|
+
# @raise [Sevgi::ArgumentError] when precision is not an Integer or nil
|
|
161
|
+
def convex?(precision: nil)
|
|
162
|
+
simple?(precision:) && Predicate.convex_turns?(vertices, precision:)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Reports whether this is a simple non-convex polygon.
|
|
166
|
+
#
|
|
167
|
+
# Self-intersecting and degenerate polygons are neither convex nor concave.
|
|
168
|
+
# @param precision [Integer, nil] decimal precision, or nil for the current function default
|
|
169
|
+
# @return [Boolean]
|
|
170
|
+
# @raise [Sevgi::ArgumentError] when precision is not an Integer or nil
|
|
171
|
+
def concave?(precision: nil)
|
|
172
|
+
simple?(precision:) && !Predicate.convex_turns?(vertices, precision:)
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
@@ -2,10 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
module Sevgi
|
|
4
4
|
module Geometry
|
|
5
|
-
# Immutable polar
|
|
5
|
+
# Immutable polar displacement in SVG/screen coordinates.
|
|
6
6
|
#
|
|
7
|
-
# `length` is a distance and `angle` is a
|
|
8
|
-
# Use
|
|
7
|
+
# A Segment has no position: `length` is a distance and `angle` is a
|
|
8
|
+
# clockwise direction. Use {#ending} to apply it to a starting point or
|
|
9
|
+
# {#line} when a placed, finite line is required. `Segment[length, angle]`
|
|
10
|
+
# starts from polar components. `Segment.(starting, ending)` derives them
|
|
11
|
+
# from two points.
|
|
12
|
+
# @example Derive polar components from two points
|
|
13
|
+
# segment = Sevgi::Geometry::Segment.([1, 2], [4, 6])
|
|
14
|
+
# segment.length # => 5.0
|
|
15
|
+
# segment.ending([1, 2]).deconstruct # => [4.0, 6.0]
|
|
16
|
+
# @example Use a cardinal direction
|
|
17
|
+
# Sevgi::Geometry::Segment.upward(3).ending([5, 5]).deconstruct # => [5.0, 2.0]
|
|
18
|
+
# @see Sevgi::Geometry::Line
|
|
19
|
+
# @!parse
|
|
20
|
+
# class Segment
|
|
21
|
+
# # Creates a segment from polar components.
|
|
22
|
+
# # @param length [Numeric] non-negative segment length
|
|
23
|
+
# # @param angle [Numeric] clockwise angle in degrees
|
|
24
|
+
# # @return [Sevgi::Geometry::Segment]
|
|
25
|
+
# # @raise [Sevgi::Geometry::Error] when a component is not finite or length is negative
|
|
26
|
+
# # @example Create a segment with mathematical notation
|
|
27
|
+
# # Sevgi::Geometry::Segment[5, 30]
|
|
28
|
+
# def self.[](length, angle); end
|
|
29
|
+
# end
|
|
9
30
|
Segment = Data.define(:length, :angle) do
|
|
10
31
|
include Comparable
|
|
11
32
|
|
|
@@ -56,9 +77,15 @@ module Sevgi
|
|
|
56
77
|
def self.upward(length) = self[length, -90.0]
|
|
57
78
|
|
|
58
79
|
class << self
|
|
59
|
-
# @
|
|
80
|
+
# @overload horizontal(length)
|
|
81
|
+
# Returns a rightward segment.
|
|
82
|
+
# @param length [Numeric] segment length
|
|
83
|
+
# @return [Sevgi::Geometry::Segment]
|
|
60
84
|
alias_method :horizontal, :rightward
|
|
61
|
-
# @
|
|
85
|
+
# @overload vertical(length)
|
|
86
|
+
# Returns a downward segment.
|
|
87
|
+
# @param length [Numeric] segment length
|
|
88
|
+
# @return [Sevgi::Geometry::Segment]
|
|
62
89
|
alias_method :vertical, :downward
|
|
63
90
|
end
|
|
64
91
|
|
|
@@ -66,14 +93,22 @@ module Sevgi
|
|
|
66
93
|
# @param length [Numeric] segment length
|
|
67
94
|
# @param angle [Numeric] clockwise angle in degrees
|
|
68
95
|
# @return [void]
|
|
69
|
-
# @raise [Sevgi::Geometry::Error] when a component is not
|
|
70
|
-
def initialize(length:, angle:)
|
|
96
|
+
# @raise [Sevgi::Geometry::Error] when a component is not finite or length is negative
|
|
97
|
+
def initialize(length:, angle:)
|
|
98
|
+
length = Real[:length, length]
|
|
99
|
+
Error.("Segment length cannot be negative") if length.negative?
|
|
100
|
+
|
|
101
|
+
super(length:, angle: Real[:angle, angle])
|
|
102
|
+
end
|
|
71
103
|
|
|
72
104
|
# Compares segments by length.
|
|
73
|
-
# @param other [
|
|
74
|
-
# @return [Integer, nil]
|
|
75
|
-
|
|
76
|
-
|
|
105
|
+
# @param other [Object] segment or two-item length/angle array to compare
|
|
106
|
+
# @return [Integer, nil] comparison result, or nil when other is not a valid segment value
|
|
107
|
+
def <=>(other)
|
|
108
|
+
length <=> Tuple[Segment, other].length
|
|
109
|
+
rescue Error
|
|
110
|
+
nil
|
|
111
|
+
end
|
|
77
112
|
|
|
78
113
|
# Returns a segment rounded to precision.
|
|
79
114
|
# @param precision [Integer, nil] decimal precision, or nil for the current function default
|
|
@@ -93,11 +128,13 @@ module Sevgi
|
|
|
93
128
|
# @raise [Sevgi::Geometry::Error] when other cannot be coerced
|
|
94
129
|
def eq?(other, precision: nil) = self.class.eq?(self, other, precision:)
|
|
95
130
|
|
|
96
|
-
# Reports
|
|
131
|
+
# Reports exact length-and-direction equality, also used by ==. Ordering through <=> compares length only.
|
|
97
132
|
# @param other [Object] object to compare
|
|
98
133
|
# @return [Boolean]
|
|
99
134
|
def eql?(other) = self.class == other.class && deconstruct == other.deconstruct
|
|
100
135
|
|
|
136
|
+
alias_method :==, :eql?
|
|
137
|
+
|
|
101
138
|
# Returns a hash compatible with strict equality.
|
|
102
139
|
# @return [Integer]
|
|
103
140
|
def hash = [self.class, *deconstruct].hash
|
|
@@ -121,7 +158,35 @@ module Sevgi
|
|
|
121
158
|
def y = length * F.sin(angle)
|
|
122
159
|
end
|
|
123
160
|
|
|
124
|
-
#
|
|
125
|
-
|
|
161
|
+
# Immutable length/angle constraint used when a full segment is not implied.
|
|
162
|
+
#
|
|
163
|
+
# @!attribute [r] length
|
|
164
|
+
# @return [Float] non-negative target measure
|
|
165
|
+
# @!attribute [r] angle
|
|
166
|
+
# @return [Float] direction used to derive a segment
|
|
167
|
+
# @!parse
|
|
168
|
+
# class LengthAngle
|
|
169
|
+
# # Creates a length-and-angle constraint.
|
|
170
|
+
# # @param length [Numeric] finite non-negative target width or height
|
|
171
|
+
# # @param angle [Numeric] finite direction in degrees
|
|
172
|
+
# # @return [Sevgi::Geometry::LengthAngle]
|
|
173
|
+
# # @raise [Sevgi::Geometry::Error] when a component is not finite or length is negative
|
|
174
|
+
# # @example Create a constraint with mathematical notation
|
|
175
|
+
# # Sevgi::Geometry::LengthAngle[3, 90]
|
|
176
|
+
# def self.[](length, angle); end
|
|
177
|
+
# end
|
|
178
|
+
LengthAngle = Data.define(:length, :angle) do
|
|
179
|
+
# Creates a target-measure constraint.
|
|
180
|
+
# @param length [Numeric] finite non-negative target width or height
|
|
181
|
+
# @param angle [Numeric] finite direction in degrees
|
|
182
|
+
# @return [void]
|
|
183
|
+
# @raise [Sevgi::Geometry::Error] when a component is not finite or length is negative
|
|
184
|
+
def initialize(length:, angle:)
|
|
185
|
+
length = Real[:length, length]
|
|
186
|
+
Error.("LengthAngle length cannot be negative") if length.negative?
|
|
187
|
+
|
|
188
|
+
super(length:, angle: Real[:angle, angle])
|
|
189
|
+
end
|
|
190
|
+
end
|
|
126
191
|
end
|
|
127
192
|
end
|
data/lib/sevgi/geometry.rb
CHANGED
|
@@ -8,16 +8,40 @@ require_relative "geometry/errors"
|
|
|
8
8
|
require_relative "geometry/point"
|
|
9
9
|
require_relative "geometry/segment"
|
|
10
10
|
require_relative "geometry/element"
|
|
11
|
+
require_relative "geometry/predicate"
|
|
11
12
|
require_relative "geometry/equation"
|
|
12
13
|
require_relative "geometry/operation"
|
|
13
14
|
|
|
14
15
|
require_relative "geometry/version"
|
|
15
16
|
|
|
16
17
|
module Sevgi
|
|
17
|
-
#
|
|
18
|
+
# Immutable screen-space geometry values used by Sevgi layout and drawing helpers.
|
|
18
19
|
#
|
|
19
20
|
# Coordinates follow SVG screen conventions: +x points right, +y points down,
|
|
20
|
-
# and positive angles turn clockwise.
|
|
21
|
+
# and positive angles turn clockwise. Constructors accept Point and Segment
|
|
22
|
+
# objects or their two-number Array forms. Transformations return new values.
|
|
23
|
+
# They do not mutate their receiver.
|
|
24
|
+
#
|
|
25
|
+
# Shape constructors have two complementary notations: `Shape[...]` accepts
|
|
26
|
+
# dimensions or segments, while `Shape.(...)` accepts points. Named factories
|
|
27
|
+
# such as `Rect.from_size` and `Rect.from_corners` expose the same distinction
|
|
28
|
+
# when a call site benefits from spelling it out.
|
|
29
|
+
#
|
|
30
|
+
# Trigonometric construction can retain ordinary floating-point noise. Use
|
|
31
|
+
# `approx` for presentation values and `eq?` for precision-aware comparison.
|
|
32
|
+
# strict `==` intentionally compares the exact immutable value.
|
|
33
|
+
#
|
|
34
|
+
# @example Measure and move a line
|
|
35
|
+
# line = Sevgi::Geometry::Line.([0, 0], [3, 4])
|
|
36
|
+
# line.length #=> 5.0
|
|
37
|
+
# line.translate(2, 1).starting #=> Point[2.0, 1.0]
|
|
38
|
+
# @example Follow SVG screen directions
|
|
39
|
+
# origin = Sevgi::Geometry::Point.origin
|
|
40
|
+
# Sevgi::Geometry::Point.angle(origin, [0, 10]) #=> 90.0
|
|
41
|
+
# origin.translate(0, 10).below?(origin) #=> true
|
|
42
|
+
# @see Sevgi::Geometry::Element::Lined
|
|
43
|
+
# @see Sevgi::Geometry::Operation
|
|
44
|
+
# @see https://sevgi.roktas.dev/geometry/ Geometry guide
|
|
21
45
|
module Geometry
|
|
22
46
|
end
|
|
23
47
|
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.
|
|
4
|
+
version: 1.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Recai Oktaş
|
|
@@ -15,15 +15,15 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - '='
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: 0.
|
|
18
|
+
version: 1.0.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.
|
|
26
|
-
description:
|
|
25
|
+
version: 1.0.0
|
|
26
|
+
description: Models the points, lines, shapes, and transforms used by the DSL.
|
|
27
27
|
email: roktas@gmail.com
|
|
28
28
|
executables: []
|
|
29
29
|
extensions: []
|
|
@@ -34,6 +34,11 @@ files:
|
|
|
34
34
|
- README.md
|
|
35
35
|
- lib/sevgi/geometry.rb
|
|
36
36
|
- lib/sevgi/geometry/element.rb
|
|
37
|
+
- lib/sevgi/geometry/elements/arc.rb
|
|
38
|
+
- lib/sevgi/geometry/elements/circle.rb
|
|
39
|
+
- lib/sevgi/geometry/elements/ellipse.rb
|
|
40
|
+
- lib/sevgi/geometry/elements/ellipse/affine.rb
|
|
41
|
+
- lib/sevgi/geometry/elements/ellipse/length.rb
|
|
37
42
|
- lib/sevgi/geometry/elements/line.rb
|
|
38
43
|
- lib/sevgi/geometry/elements/parallelogram.rb
|
|
39
44
|
- lib/sevgi/geometry/elements/polygon.rb
|
|
@@ -47,8 +52,10 @@ files:
|
|
|
47
52
|
- lib/sevgi/geometry/internal.rb
|
|
48
53
|
- lib/sevgi/geometry/operation.rb
|
|
49
54
|
- lib/sevgi/geometry/operation/align.rb
|
|
55
|
+
- lib/sevgi/geometry/operation/box.rb
|
|
50
56
|
- lib/sevgi/geometry/operation/sweep.rb
|
|
51
57
|
- lib/sevgi/geometry/point.rb
|
|
58
|
+
- lib/sevgi/geometry/predicate.rb
|
|
52
59
|
- lib/sevgi/geometry/segment.rb
|
|
53
60
|
- lib/sevgi/geometry/version.rb
|
|
54
61
|
homepage: https://sevgi.roktas.dev
|
|
@@ -73,7 +80,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
73
80
|
- !ruby/object:Gem::Version
|
|
74
81
|
version: '0'
|
|
75
82
|
requirements: []
|
|
76
|
-
rubygems_version: 4.0.
|
|
83
|
+
rubygems_version: 4.0.20
|
|
77
84
|
specification_version: 4
|
|
78
|
-
summary:
|
|
85
|
+
summary: Geometry values and operations for Sevgi drawings.
|
|
79
86
|
test_files: []
|