Class: Sevgi::Geometry::Element::Lined
- Inherits:
-
Sevgi::Geometry::Element
- Object
- Sevgi::Geometry::Element
- Sevgi::Geometry::Element::Lined
- Defined in:
- lib/sevgi/geometry/element.rb
Overview
rubocop:disable Metrics/ClassLength Element whose boundary is represented by straight segments.
Constant Summary collapse
- Open =
Open lined element base class.
Class.new(self) do # Draws the element as an SVG polyline. # @param node [Object] graphics node receiving the drawing command # @return [Object] graphics node command result def draw!(node, **) = node.polyline(points: points.map { it.deconstruct.join(",") }, **) end
- Close =
Closed lined element base class.
Class.new(self) do # Creates a closed element from points, appending the first point. # @param points [Array<Sevgi::Geometry::Point, Array<Numeric>>] boundary points # @return [Sevgi::Geometry::Element::Lined] # @raise [Sevgi::Geometry::Error] when any point cannot be coerced def self.new_by_points(*points) = super(*points, points.first) # Draws the element as an SVG polygon. # @param node [Object] graphics node receiving the drawing command # @return [Object] graphics node command result def draw!(node, **) = node.polygon(points: points.map { it.deconstruct.join(",") }, **) end
- SHORTCUTS =
Point shortcut names generated for fixed-size lined elements.
("A".."Z").to_a.freeze
Class Method Summary collapse
-
.[](*segments, position: Origin) ⇒ Sevgi::Geometry::Element::Lined
Builds an element from segments.
-
.build(size = Undefined, open: false) ⇒ Class
Builds a concrete lined element class.
-
.call(*points) ⇒ Sevgi::Geometry::Element::Lined
Builds an element from points.
-
.from_points(*points) ⇒ Sevgi::Geometry::Element::Lined
Builds an element from points.
-
.from_segments(*segments, position: Origin) ⇒ Sevgi::Geometry::Element::Lined
Builds an element from segments.
-
.new_by_points(*points) ⇒ Sevgi::Geometry::Element::Lined
Builds an element from points, applying closed-path behavior where appropriate.
-
.new_by_points!(*points) ⇒ Sevgi::Geometry::Element::Lined
Builds an element from an exact point path.
-
.new_by_segments(*segments, position: Origin) ⇒ Sevgi::Geometry::Element::Lined
Builds an element from segments and a start position.
Instance Method Summary collapse
-
#[](i) ⇒ Sevgi::Geometry::Line
Returns a line by index.
-
#approx ⇒ Sevgi::Geometry::Element::Lined
Returns an element with approximate points and segments.
-
#box ⇒ Sevgi::Geometry::Rect
Returns the bounding rectangle.
-
#call(i) ⇒ Sevgi::Geometry::Point
Returns a point by index.
-
#draw(node, **attributes) ⇒ Object
Draws an approximate element into a graphics node.
-
#eql?(other) ⇒ Boolean
(also: #==)
Reports strict element equality by class and approximate points.
-
#equations ⇒ Array<Sevgi::Geometry::Equation::Linear>
Returns boundary equations for all lines.
-
#hash ⇒ Integer
Returns a hash compatible with strict equality.
-
#head ⇒ Sevgi::Geometry::Segment
Returns the first segment.
-
#initialize(&block) ⇒ Lined
constructor
A new instance of Lined.
-
#inside?(point) ⇒ Boolean
Reports whether a point is inside or on the boundary.
-
#intersection(equation, precision: nil) ⇒ Array<Sevgi::Geometry::Point>
Intersects the element boundary with an equation.
-
#lines ⇒ Array<Sevgi::Geometry::Line>
Returns boundary lines derived from segments and points.
-
#on?(point) ⇒ Boolean
Reports whether a point is on the boundary.
-
#outside?(point) ⇒ Boolean
Reports whether a point is outside the element boundary.
-
#perimeter ⇒ Float
Returns the sum of segment lengths.
-
#points(approximate = false) ⇒ Array<Sevgi::Geometry::Point>
Returns element points.
-
#position ⇒ Sevgi::Geometry::Point
Returns the first point.
-
#segments(approximate = false) ⇒ Array<Sevgi::Geometry::Segment>
Returns element segments.
-
#tail ⇒ Sevgi::Geometry::Segment
Returns the last segment.
Methods inherited from Sevgi::Geometry::Element
arced, #at, #ignorable?, lined, #translate
Constructor Details
#initialize(&block) ⇒ Lined
Returns a new instance of Lined.
204 205 206 207 208 209 210 211 212 213 214 215 |
# File 'lib/sevgi/geometry/element.rb', line 204 def initialize(&block) super() Error.("Constructor block required") unless block instance_exec(&block) @points ||= calculate_points_from_segments @segments ||= calculate_segments_from_points sanitize end |
Class Method Details
.[](*segments, position: Origin) ⇒ Sevgi::Geometry::Element::Lined
124 |
# File 'lib/sevgi/geometry/element.rb', line 124 def self.[](...) = from_segments(...) |
.build(size = Undefined, open: false) ⇒ Class
Builds a concrete lined element class.
104 105 106 107 108 109 110 111 112 113 114 115 116 |
# File 'lib/sevgi/geometry/element.rb', line 104 def self.build(size = Undefined, open: false) Class.new(open ? Open : Close) do define_singleton_method(:close?) { !open } define_singleton_method(:open?) { open } define_singleton_method(:poly?) { size == Undefined } define_singleton_method(:size) { size } Lined.send(:define_shortcuts, self, size, open:) unless size == Undefined end end |
.call(*points) ⇒ Sevgi::Geometry::Element::Lined
131 |
# File 'lib/sevgi/geometry/element.rb', line 131 def self.call(...) = from_points(...) |
.from_points(*points) ⇒ Sevgi::Geometry::Element::Lined
138 |
# File 'lib/sevgi/geometry/element.rb', line 138 def self.from_points(...) = new_by_points(...) |
.from_segments(*segments, position: Origin) ⇒ Sevgi::Geometry::Element::Lined
146 |
# File 'lib/sevgi/geometry/element.rb', line 146 def self.from_segments(...) = new_by_segments(...) |
.new_by_points(*points) ⇒ Sevgi::Geometry::Element::Lined
153 |
# File 'lib/sevgi/geometry/element.rb', line 153 def self.new_by_points(...) = new_by_points!(...) |
.new_by_points!(*points) ⇒ Sevgi::Geometry::Element::Lined
Builds an element from an exact point path.
Closed classes require the closing point to be supplied by the caller.
161 162 163 164 165 |
# File 'lib/sevgi/geometry/element.rb', line 161 def self.new_by_points!(*points) new do @points = Tuples[Point, *points] end end |
.new_by_segments(*segments, position: Origin) ⇒ Sevgi::Geometry::Element::Lined
Builds an element from segments and a start position.
172 173 174 175 176 177 |
# File 'lib/sevgi/geometry/element.rb', line 172 def self.new_by_segments(*segments, position: Origin) new do @position = Tuple[Point, position] @segments = Tuples[Segment, *segments] end end |
Instance Method Details
#[](i) ⇒ Sevgi::Geometry::Line
Returns a line by index.
305 |
# File 'lib/sevgi/geometry/element.rb', line 305 def [](i) = lines[i].tap { |line| Error.("No line exist for index: #{i}") unless line } |
#approx ⇒ Sevgi::Geometry::Element::Lined
Returns an element with approximate points and segments.
221 222 223 224 225 226 |
# File 'lib/sevgi/geometry/element.rb', line 221 def approx points, segments = points(true), segments(true) self.class.send(:new) do @points, @segments = points, segments end end |
#box ⇒ Sevgi::Geometry::Rect
Returns the bounding rectangle.
309 |
# File 'lib/sevgi/geometry/element.rb', line 309 def box = Rect.from_corners([(xs = points.map(&:x)).min, (ys = points.map(&:y)).min], [xs.max, ys.max]) |
#call(i) ⇒ Sevgi::Geometry::Point
Returns a point by index.
315 |
# File 'lib/sevgi/geometry/element.rb', line 315 def call(i) = points[i].tap { Error.("No point exist for index: #{i}") unless it } |
#draw(node, **attributes) ⇒ Object
233 234 235 |
# File 'lib/sevgi/geometry/element.rb', line 233 def draw(...) approx.draw!(...) end |
#eql?(other) ⇒ Boolean Also known as: ==
Reports strict element equality by class and approximate points.
270 |
# File 'lib/sevgi/geometry/element.rb', line 270 def eql?(other) = self.class == other.class && points(true) == other.points(true) |
#equations ⇒ Array<Sevgi::Geometry::Equation::Linear>
Returns boundary equations for all lines.
282 |
# File 'lib/sevgi/geometry/element.rb', line 282 def equations = @equations ||= lines.map(&:equation) |
#hash ⇒ Integer
Returns a hash compatible with strict equality.
274 |
# File 'lib/sevgi/geometry/element.rb', line 274 def hash = [self.class, *points(true)].hash |
#head ⇒ Sevgi::Geometry::Segment
Returns the first segment.
319 |
# File 'lib/sevgi/geometry/element.rb', line 319 def head = @head ||= segments.first |
#inside?(point) ⇒ Boolean
Reports whether a point is inside or on the boundary.
343 344 345 346 347 |
# File 'lib/sevgi/geometry/element.rb', line 343 def inside?(point) point = Tuple[Point, point] on?(point) || pnpoly(points, point) end |
#intersection(equation, precision: nil) ⇒ Array<Sevgi::Geometry::Point>
Intersects the element boundary with an equation.
290 291 292 293 294 295 296 297 |
# File 'lib/sevgi/geometry/element.rb', line 290 def intersection(equation, precision: nil) equations .map do |candidate| equation.intersect(candidate).map { |point| point.approx(precision) }.select { |point| on?(point) } end .flatten .uniq end |
#lines ⇒ Array<Sevgi::Geometry::Line>
Returns boundary lines derived from segments and points.
323 324 325 326 327 |
# File 'lib/sevgi/geometry/element.rb', line 323 def lines @lines ||= segments.zip(points[...segments.size]).map { |segment, position| segment.line(position) } end |
#on?(point) ⇒ Boolean
Reports whether a point is on the boundary.
353 354 355 356 357 |
# File 'lib/sevgi/geometry/element.rb', line 353 def on?(point) point = Tuple[Point, point] lines.any? { it.over?(point) } end |
#outside?(point) ⇒ Boolean
Reports whether a point is outside the element boundary.
363 |
# File 'lib/sevgi/geometry/element.rb', line 363 def outside?(point) = !inside?(point) |
#perimeter ⇒ Float
Returns the sum of segment lengths.
331 |
# File 'lib/sevgi/geometry/element.rb', line 331 def perimeter = @perimeter ||= segments.sum(&:length) |
#points(approximate = false) ⇒ Array<Sevgi::Geometry::Point>
Returns element points.
240 241 242 |
# File 'lib/sevgi/geometry/element.rb', line 240 def points(approximate = false) approximate ? @points.map(&:approx) : @points end |
#position ⇒ Sevgi::Geometry::Point
Returns the first point.
246 247 248 |
# File 'lib/sevgi/geometry/element.rb', line 246 def position @position ||= points.first end |
#segments(approximate = false) ⇒ Array<Sevgi::Geometry::Segment>
Returns element segments.
253 254 255 |
# File 'lib/sevgi/geometry/element.rb', line 253 def segments(approximate = false) approximate ? @segments.map(&:approx) : @segments end |
#tail ⇒ Sevgi::Geometry::Segment
Returns the last segment.
335 |
# File 'lib/sevgi/geometry/element.rb', line 335 def tail = @tail ||= segments.last |