Class: Sevgi::Geometry::Line

Inherits:
LineBase
  • Object
show all
Defined in:
lib/sevgi/geometry/elements/line.rb,
lib/sevgi/geometry/equation.rb

Overview

Finite, directed line between two endpoints.

Direction affects #left?, #right?, and the sign of #shift. Use #equation when the corresponding infinite line is required; #over? deliberately tests only the finite extent between the endpoints.

Examples:

Query sides of a directed line in screen coordinates

line = Sevgi::Geometry::Line.([0, 0], [10, 0])
line.left?([5, -2])  # => true
line.right?([5, 2])  # => true
line.shift(2).starting.deconstruct # => [0.0, -2.0]

Distinguish the finite segment from its infinite equation

line = Sevgi::Geometry::Line.([0, 0], [10, 0])
line.over?([5, 0])  # => true
line.over?([15, 0]) # => false

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#ASevgi::Geometry::Point (readonly)

Returns starting point.

Returns:



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/sevgi/geometry/elements/line.rb', line 36

class Line < LineBase
  # @overload [](length, angle, position: Origin)
  #   Builds a line from length and angle.
  #   @param length [Numeric] line length
  #   @param angle [Numeric] clockwise angle in degrees
  #   @param position [Sevgi::Geometry::Point, Array<Numeric>] starting point
  #   @return [Sevgi::Geometry::Line]
  #   @raise [Sevgi::Geometry::Error] when length, angle, or position cannot be coerced to finite geometry values
  # @example Mathematical notation and English convenience are equivalent
  #   Sevgi::Geometry::Line[5, 30] == Sevgi::Geometry::Line.from_length_angle(5, 30)
  def self.[](length, angle, position: Origin) = new_by_segments(Segment[length, angle], position:)

  # Builds a line from length and angle.
  # @param length [Numeric] line length
  # @param angle [Numeric] clockwise angle in degrees
  # @param position [Sevgi::Geometry::Point, Array<Numeric>] starting point
  # @return [Sevgi::Geometry::Line]
  # @raise [Sevgi::Geometry::Error] when length, angle, or position cannot be coerced to finite geometry values
  def self.from_length_angle(length, angle, position: Origin) = self[length, angle, position:]

  # @overload from_points(starting, ending)
  #   Builds a line from two endpoints.
  #   @param starting [Sevgi::Geometry::Point, Array<Numeric>] starting point
  #   @param ending [Sevgi::Geometry::Point, Array<Numeric>] ending point
  #   @return [Sevgi::Geometry::Line]
  #   @raise [Sevgi::Geometry::Error] when either point cannot be coerced
  # @example Mathematical notation and English convenience are equivalent
  #   Sevgi::Geometry::Line.([0, 0], [3, 4]) == Sevgi::Geometry::Line.from_points([0, 0], [3, 4])
  def self.from_points(...) = call(...)

  private_class_method :from_segments

  # Returns the clockwise line angle in degrees.
  # @return [Float]
  def angle = head.angle

  # Returns the ending point.
  # @return [Sevgi::Geometry::Point]
  def ending = points.last

  # Reports whether a point is left of the directed line from {#starting} to {#ending} in screen coordinates.
  # Points on the infinite line are on neither side. A zero-length line has no direction and returns false.
  # @param point [Sevgi::Geometry::Point, Array<Numeric>] point to test
  # @return [Boolean]
  # @raise [Sevgi::Geometry::Error] when point cannot be coerced
  def left?(point) = F.lt?(side(point), 0.0)

  # Reports whether a point is right of the directed line from {#starting} to {#ending} in screen coordinates.
  # Points on the infinite line are on neither side. A zero-length line has no direction and returns false.
  # @param point [Sevgi::Geometry::Point, Array<Numeric>] point to test
  # @return [Boolean]
  # @raise [Sevgi::Geometry::Error] when point cannot be coerced
  def right?(point) = F.gt?(side(point), 0.0)

  # Returns the starting point.
  # @return [Sevgi::Geometry::Point]
  def starting = points.first

  # Draws the line into a graphics node.
  # @param node [Object] graphics node receiving the drawing command
  # @return [Object] graphics node command result
  def draw!(node, **) = node.LineTo(x1: position.x, y1: position.y, x2: ending.x, y2: ending.y, **)

  private :draw!

  # Reports whether a point lies on the finite line segment.
  # @param point [Sevgi::Geometry::Point, Array<Numeric>] point to test
  # @return [Boolean]
  # @raise [Sevgi::Geometry::Error] when point cannot be coerced
  def over?(point)
    point = Tuple[Point, point]

    within_range?(point) && equation.on?(point)
  end

  # Returns a parallel line shifted by a signed perpendicular offset.
  # Positive distance moves to the directed line's left in screen coordinates; reversing the endpoints reverses the
  # shift direction.
  # @param distance [Numeric] signed perpendicular offset
  # @return [Sevgi::Geometry::Line]
  # @raise [Sevgi::Geometry::Error] when distance is not a finite real number
  def shift(distance)
    distance = Real[:distance, distance]
    translate(distance * F.sin(angle), -distance * F.cos(angle))
  end

  private

  def cross(ax, ay, bx, by) = (ax * by) - (ay * bx)
  def delta(from, to) = [to.x - from.x, to.y - from.y]

  def side(point)
    point = Tuple[Point, point]
    cross(*delta(starting, ending), *delta(starting, point))
  end

  def within_range?(point)
    point = point.approx
    points = [starting.approx, ending.approx]

    point.between?(points.min, points.max)
  end
end

#ABSevgi::Geometry::Line (readonly)

Returns line from A to B.

Returns:



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/sevgi/geometry/elements/line.rb', line 36

class Line < LineBase
  # @overload [](length, angle, position: Origin)
  #   Builds a line from length and angle.
  #   @param length [Numeric] line length
  #   @param angle [Numeric] clockwise angle in degrees
  #   @param position [Sevgi::Geometry::Point, Array<Numeric>] starting point
  #   @return [Sevgi::Geometry::Line]
  #   @raise [Sevgi::Geometry::Error] when length, angle, or position cannot be coerced to finite geometry values
  # @example Mathematical notation and English convenience are equivalent
  #   Sevgi::Geometry::Line[5, 30] == Sevgi::Geometry::Line.from_length_angle(5, 30)
  def self.[](length, angle, position: Origin) = new_by_segments(Segment[length, angle], position:)

  # Builds a line from length and angle.
  # @param length [Numeric] line length
  # @param angle [Numeric] clockwise angle in degrees
  # @param position [Sevgi::Geometry::Point, Array<Numeric>] starting point
  # @return [Sevgi::Geometry::Line]
  # @raise [Sevgi::Geometry::Error] when length, angle, or position cannot be coerced to finite geometry values
  def self.from_length_angle(length, angle, position: Origin) = self[length, angle, position:]

  # @overload from_points(starting, ending)
  #   Builds a line from two endpoints.
  #   @param starting [Sevgi::Geometry::Point, Array<Numeric>] starting point
  #   @param ending [Sevgi::Geometry::Point, Array<Numeric>] ending point
  #   @return [Sevgi::Geometry::Line]
  #   @raise [Sevgi::Geometry::Error] when either point cannot be coerced
  # @example Mathematical notation and English convenience are equivalent
  #   Sevgi::Geometry::Line.([0, 0], [3, 4]) == Sevgi::Geometry::Line.from_points([0, 0], [3, 4])
  def self.from_points(...) = call(...)

  private_class_method :from_segments

  # Returns the clockwise line angle in degrees.
  # @return [Float]
  def angle = head.angle

  # Returns the ending point.
  # @return [Sevgi::Geometry::Point]
  def ending = points.last

  # Reports whether a point is left of the directed line from {#starting} to {#ending} in screen coordinates.
  # Points on the infinite line are on neither side. A zero-length line has no direction and returns false.
  # @param point [Sevgi::Geometry::Point, Array<Numeric>] point to test
  # @return [Boolean]
  # @raise [Sevgi::Geometry::Error] when point cannot be coerced
  def left?(point) = F.lt?(side(point), 0.0)

  # Reports whether a point is right of the directed line from {#starting} to {#ending} in screen coordinates.
  # Points on the infinite line are on neither side. A zero-length line has no direction and returns false.
  # @param point [Sevgi::Geometry::Point, Array<Numeric>] point to test
  # @return [Boolean]
  # @raise [Sevgi::Geometry::Error] when point cannot be coerced
  def right?(point) = F.gt?(side(point), 0.0)

  # Returns the starting point.
  # @return [Sevgi::Geometry::Point]
  def starting = points.first

  # Draws the line into a graphics node.
  # @param node [Object] graphics node receiving the drawing command
  # @return [Object] graphics node command result
  def draw!(node, **) = node.LineTo(x1: position.x, y1: position.y, x2: ending.x, y2: ending.y, **)

  private :draw!

  # Reports whether a point lies on the finite line segment.
  # @param point [Sevgi::Geometry::Point, Array<Numeric>] point to test
  # @return [Boolean]
  # @raise [Sevgi::Geometry::Error] when point cannot be coerced
  def over?(point)
    point = Tuple[Point, point]

    within_range?(point) && equation.on?(point)
  end

  # Returns a parallel line shifted by a signed perpendicular offset.
  # Positive distance moves to the directed line's left in screen coordinates; reversing the endpoints reverses the
  # shift direction.
  # @param distance [Numeric] signed perpendicular offset
  # @return [Sevgi::Geometry::Line]
  # @raise [Sevgi::Geometry::Error] when distance is not a finite real number
  def shift(distance)
    distance = Real[:distance, distance]
    translate(distance * F.sin(angle), -distance * F.cos(angle))
  end

  private

  def cross(ax, ay, bx, by) = (ax * by) - (ay * bx)
  def delta(from, to) = [to.x - from.x, to.y - from.y]

  def side(point)
    point = Tuple[Point, point]
    cross(*delta(starting, ending), *delta(starting, point))
  end

  def within_range?(point)
    point = point.approx
    points = [starting.approx, ending.approx]

    point.between?(points.min, points.max)
  end
end

#BSevgi::Geometry::Point (readonly)

Returns ending point.

Returns:



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/sevgi/geometry/elements/line.rb', line 36

class Line < LineBase
  # @overload [](length, angle, position: Origin)
  #   Builds a line from length and angle.
  #   @param length [Numeric] line length
  #   @param angle [Numeric] clockwise angle in degrees
  #   @param position [Sevgi::Geometry::Point, Array<Numeric>] starting point
  #   @return [Sevgi::Geometry::Line]
  #   @raise [Sevgi::Geometry::Error] when length, angle, or position cannot be coerced to finite geometry values
  # @example Mathematical notation and English convenience are equivalent
  #   Sevgi::Geometry::Line[5, 30] == Sevgi::Geometry::Line.from_length_angle(5, 30)
  def self.[](length, angle, position: Origin) = new_by_segments(Segment[length, angle], position:)

  # Builds a line from length and angle.
  # @param length [Numeric] line length
  # @param angle [Numeric] clockwise angle in degrees
  # @param position [Sevgi::Geometry::Point, Array<Numeric>] starting point
  # @return [Sevgi::Geometry::Line]
  # @raise [Sevgi::Geometry::Error] when length, angle, or position cannot be coerced to finite geometry values
  def self.from_length_angle(length, angle, position: Origin) = self[length, angle, position:]

  # @overload from_points(starting, ending)
  #   Builds a line from two endpoints.
  #   @param starting [Sevgi::Geometry::Point, Array<Numeric>] starting point
  #   @param ending [Sevgi::Geometry::Point, Array<Numeric>] ending point
  #   @return [Sevgi::Geometry::Line]
  #   @raise [Sevgi::Geometry::Error] when either point cannot be coerced
  # @example Mathematical notation and English convenience are equivalent
  #   Sevgi::Geometry::Line.([0, 0], [3, 4]) == Sevgi::Geometry::Line.from_points([0, 0], [3, 4])
  def self.from_points(...) = call(...)

  private_class_method :from_segments

  # Returns the clockwise line angle in degrees.
  # @return [Float]
  def angle = head.angle

  # Returns the ending point.
  # @return [Sevgi::Geometry::Point]
  def ending = points.last

  # Reports whether a point is left of the directed line from {#starting} to {#ending} in screen coordinates.
  # Points on the infinite line are on neither side. A zero-length line has no direction and returns false.
  # @param point [Sevgi::Geometry::Point, Array<Numeric>] point to test
  # @return [Boolean]
  # @raise [Sevgi::Geometry::Error] when point cannot be coerced
  def left?(point) = F.lt?(side(point), 0.0)

  # Reports whether a point is right of the directed line from {#starting} to {#ending} in screen coordinates.
  # Points on the infinite line are on neither side. A zero-length line has no direction and returns false.
  # @param point [Sevgi::Geometry::Point, Array<Numeric>] point to test
  # @return [Boolean]
  # @raise [Sevgi::Geometry::Error] when point cannot be coerced
  def right?(point) = F.gt?(side(point), 0.0)

  # Returns the starting point.
  # @return [Sevgi::Geometry::Point]
  def starting = points.first

  # Draws the line into a graphics node.
  # @param node [Object] graphics node receiving the drawing command
  # @return [Object] graphics node command result
  def draw!(node, **) = node.LineTo(x1: position.x, y1: position.y, x2: ending.x, y2: ending.y, **)

  private :draw!

  # Reports whether a point lies on the finite line segment.
  # @param point [Sevgi::Geometry::Point, Array<Numeric>] point to test
  # @return [Boolean]
  # @raise [Sevgi::Geometry::Error] when point cannot be coerced
  def over?(point)
    point = Tuple[Point, point]

    within_range?(point) && equation.on?(point)
  end

  # Returns a parallel line shifted by a signed perpendicular offset.
  # Positive distance moves to the directed line's left in screen coordinates; reversing the endpoints reverses the
  # shift direction.
  # @param distance [Numeric] signed perpendicular offset
  # @return [Sevgi::Geometry::Line]
  # @raise [Sevgi::Geometry::Error] when distance is not a finite real number
  def shift(distance)
    distance = Real[:distance, distance]
    translate(distance * F.sin(angle), -distance * F.cos(angle))
  end

  private

  def cross(ax, ay, bx, by) = (ax * by) - (ay * bx)
  def delta(from, to) = [to.x - from.x, to.y - from.y]

  def side(point)
    point = Tuple[Point, point]
    cross(*delta(starting, ending), *delta(starting, point))
  end

  def within_range?(point)
    point = point.approx
    points = [starting.approx, ending.approx]

    point.between?(points.min, points.max)
  end
end

Class Method Details

.[](length, angle, position: Origin) ⇒ Sevgi::Geometry::Line

Examples:

Mathematical notation and English convenience are equivalent

Sevgi::Geometry::Line[5, 30] == Sevgi::Geometry::Line.from_length_angle(5, 30)

Builds a line from length and angle.

Parameters:

  • length (Numeric)

    line length

  • angle (Numeric)

    clockwise angle in degrees

  • position (Sevgi::Geometry::Point, Array<Numeric>) (defaults to: Origin)

    starting point

Returns:

Raises:



46
# File 'lib/sevgi/geometry/elements/line.rb', line 46

def self.[](length, angle, position: Origin) = new_by_segments(Segment[length, angle], position:)

.call(starting, ending) ⇒ Sevgi::Geometry::Line

Builds a line from two endpoints.

Parameters:

Returns:

Raises:



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/sevgi/geometry/elements/line.rb', line 36

class Line < LineBase
  # @overload [](length, angle, position: Origin)
  #   Builds a line from length and angle.
  #   @param length [Numeric] line length
  #   @param angle [Numeric] clockwise angle in degrees
  #   @param position [Sevgi::Geometry::Point, Array<Numeric>] starting point
  #   @return [Sevgi::Geometry::Line]
  #   @raise [Sevgi::Geometry::Error] when length, angle, or position cannot be coerced to finite geometry values
  # @example Mathematical notation and English convenience are equivalent
  #   Sevgi::Geometry::Line[5, 30] == Sevgi::Geometry::Line.from_length_angle(5, 30)
  def self.[](length, angle, position: Origin) = new_by_segments(Segment[length, angle], position:)

  # Builds a line from length and angle.
  # @param length [Numeric] line length
  # @param angle [Numeric] clockwise angle in degrees
  # @param position [Sevgi::Geometry::Point, Array<Numeric>] starting point
  # @return [Sevgi::Geometry::Line]
  # @raise [Sevgi::Geometry::Error] when length, angle, or position cannot be coerced to finite geometry values
  def self.from_length_angle(length, angle, position: Origin) = self[length, angle, position:]

  # @overload from_points(starting, ending)
  #   Builds a line from two endpoints.
  #   @param starting [Sevgi::Geometry::Point, Array<Numeric>] starting point
  #   @param ending [Sevgi::Geometry::Point, Array<Numeric>] ending point
  #   @return [Sevgi::Geometry::Line]
  #   @raise [Sevgi::Geometry::Error] when either point cannot be coerced
  # @example Mathematical notation and English convenience are equivalent
  #   Sevgi::Geometry::Line.([0, 0], [3, 4]) == Sevgi::Geometry::Line.from_points([0, 0], [3, 4])
  def self.from_points(...) = call(...)

  private_class_method :from_segments

  # Returns the clockwise line angle in degrees.
  # @return [Float]
  def angle = head.angle

  # Returns the ending point.
  # @return [Sevgi::Geometry::Point]
  def ending = points.last

  # Reports whether a point is left of the directed line from {#starting} to {#ending} in screen coordinates.
  # Points on the infinite line are on neither side. A zero-length line has no direction and returns false.
  # @param point [Sevgi::Geometry::Point, Array<Numeric>] point to test
  # @return [Boolean]
  # @raise [Sevgi::Geometry::Error] when point cannot be coerced
  def left?(point) = F.lt?(side(point), 0.0)

  # Reports whether a point is right of the directed line from {#starting} to {#ending} in screen coordinates.
  # Points on the infinite line are on neither side. A zero-length line has no direction and returns false.
  # @param point [Sevgi::Geometry::Point, Array<Numeric>] point to test
  # @return [Boolean]
  # @raise [Sevgi::Geometry::Error] when point cannot be coerced
  def right?(point) = F.gt?(side(point), 0.0)

  # Returns the starting point.
  # @return [Sevgi::Geometry::Point]
  def starting = points.first

  # Draws the line into a graphics node.
  # @param node [Object] graphics node receiving the drawing command
  # @return [Object] graphics node command result
  def draw!(node, **) = node.LineTo(x1: position.x, y1: position.y, x2: ending.x, y2: ending.y, **)

  private :draw!

  # Reports whether a point lies on the finite line segment.
  # @param point [Sevgi::Geometry::Point, Array<Numeric>] point to test
  # @return [Boolean]
  # @raise [Sevgi::Geometry::Error] when point cannot be coerced
  def over?(point)
    point = Tuple[Point, point]

    within_range?(point) && equation.on?(point)
  end

  # Returns a parallel line shifted by a signed perpendicular offset.
  # Positive distance moves to the directed line's left in screen coordinates; reversing the endpoints reverses the
  # shift direction.
  # @param distance [Numeric] signed perpendicular offset
  # @return [Sevgi::Geometry::Line]
  # @raise [Sevgi::Geometry::Error] when distance is not a finite real number
  def shift(distance)
    distance = Real[:distance, distance]
    translate(distance * F.sin(angle), -distance * F.cos(angle))
  end

  private

  def cross(ax, ay, bx, by) = (ax * by) - (ay * bx)
  def delta(from, to) = [to.x - from.x, to.y - from.y]

  def side(point)
    point = Tuple[Point, point]
    cross(*delta(starting, ending), *delta(starting, point))
  end

  def within_range?(point)
    point = point.approx
    points = [starting.approx, ending.approx]

    point.between?(points.min, points.max)
  end
end

.from_length_angle(length, angle, position: Origin) ⇒ Sevgi::Geometry::Line

Builds a line from length and angle.

Parameters:

  • length (Numeric)

    line length

  • angle (Numeric)

    clockwise angle in degrees

  • position (Sevgi::Geometry::Point, Array<Numeric>) (defaults to: Origin)

    starting point

Returns:

Raises:



54
# File 'lib/sevgi/geometry/elements/line.rb', line 54

def self.from_length_angle(length, angle, position: Origin) = self[length, angle, position:]

.from_points(starting, ending) ⇒ Sevgi::Geometry::Line

Examples:

Mathematical notation and English convenience are equivalent

Sevgi::Geometry::Line.([0, 0], [3, 4]) == Sevgi::Geometry::Line.from_points([0, 0], [3, 4])

Builds a line from two endpoints.

Parameters:

Returns:

Raises:



64
# File 'lib/sevgi/geometry/elements/line.rb', line 64

def self.from_points(...) = call(...)

Instance Method Details

#angleFloat

Returns the clockwise line angle in degrees.

Returns:

  • (Float)


70
# File 'lib/sevgi/geometry/elements/line.rb', line 70

def angle = head.angle

#endingSevgi::Geometry::Point

Returns the ending point.



74
# File 'lib/sevgi/geometry/elements/line.rb', line 74

def ending = points.last

#equationSevgi::Geometry::Equation::Linear

Returns the linear equation containing this line.



131
# File 'lib/sevgi/geometry/equation.rb', line 131

def equation = position.equation(angle)

#left?(point) ⇒ Boolean

Reports whether a point is left of the directed line from #starting to #ending in screen coordinates. Points on the infinite line are on neither side. A zero-length line has no direction and returns false.

Parameters:

Returns:

  • (Boolean)

Raises:



81
# File 'lib/sevgi/geometry/elements/line.rb', line 81

def left?(point) = F.lt?(side(point), 0.0)

#over?(point) ⇒ Boolean

Reports whether a point lies on the finite line segment.

Parameters:

Returns:

  • (Boolean)

Raises:



105
106
107
108
109
# File 'lib/sevgi/geometry/elements/line.rb', line 105

def over?(point)
  point = Tuple[Point, point]

  within_range?(point) && equation.on?(point)
end

#right?(point) ⇒ Boolean

Reports whether a point is right of the directed line from #starting to #ending in screen coordinates. Points on the infinite line are on neither side. A zero-length line has no direction and returns false.

Parameters:

Returns:

  • (Boolean)

Raises:



88
# File 'lib/sevgi/geometry/elements/line.rb', line 88

def right?(point) = F.gt?(side(point), 0.0)

#shift(distance) ⇒ Sevgi::Geometry::Line

Returns a parallel line shifted by a signed perpendicular offset. Positive distance moves to the directed line's left in screen coordinates; reversing the endpoints reverses the shift direction.

Parameters:

  • distance (Numeric)

    signed perpendicular offset

Returns:

Raises:



117
118
119
120
# File 'lib/sevgi/geometry/elements/line.rb', line 117

def shift(distance)
  distance = Real[:distance, distance]
  translate(distance * F.sin(angle), -distance * F.cos(angle))
end

#startingSevgi::Geometry::Point

Returns the starting point.



92
# File 'lib/sevgi/geometry/elements/line.rb', line 92

def starting = points.first