Class: Sevgi::Geometry::Element::Lined

Inherits:
Sevgi::Geometry::Element show all
Defined in:
lib/sevgi/geometry/element.rb

Overview

rubocop:disable Metrics/ClassLength

Constant Summary collapse

Open =
Class.new(self) do
  def draw!(node, **) = node.polyline(points: points.map { it.deconstruct.join(",") }, **)
end
Close =
Class.new(self) do
  def self.new_by_points(*points) = super(*points, points.first)

  def draw!(node, **) = node.polygon(points: points.map { it.deconstruct.join(",") }, **)
end
SHORTCUTS =

Class methods

("A".."Z").to_a.freeze

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Sevgi::Geometry::Element

arced, #at, #ignorable?, lined, #translate

Constructor Details

#initialize(&block) ⇒ Lined

Returns a new instance of Lined.



92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/sevgi/geometry/element.rb', line 92

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

.[]Object



71
# File 'lib/sevgi/geometry/element.rb', line 71

def self.[](...) = new_by_segments(...)

.build(size = Undefined, open: false) ⇒ Object

rubocop:disable Metrics/MethodLength



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/sevgi/geometry/element.rb', line 46

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 }

    unless size == Undefined
      SHORTCUTS[..size.clamp(..SHORTCUTS.size)].each_with_index do |name, i|
        define_method(name) { points[i] or Error.("No such point: #{name}") }
      end

      methods = SHORTCUTS[..size.clamp(..SHORTCUTS.size)].each_cons(2).with_index.map do |names, i|
        define_method(name = names.join) { lines[i] or Error.("No such line: #{name}") }
      end

      alias_method("#{SHORTCUTS[size - 1]}#{SHORTCUTS.first}", methods.last)
    end
  end
  # rubocop:enable Metrics/MethodLength
end

.callObject



73
# File 'lib/sevgi/geometry/element.rb', line 73

def self.call(...) = new_by_points(...)

.new_by_pointsObject



75
# File 'lib/sevgi/geometry/element.rb', line 75

def self.new_by_points(...) = new_by_points!(...)

.new_by_points!(*points) ⇒ Object



77
78
79
80
81
# File 'lib/sevgi/geometry/element.rb', line 77

def self.new_by_points!(*points)
  new do
    @points = Tuples[Point, *points]
  end
end

.new_by_segments(*segments, position: Origin) ⇒ Object



83
84
85
86
87
88
# File 'lib/sevgi/geometry/element.rb', line 83

def self.new_by_segments(*segments, position: Origin)
  new do
    @position = Tuple[Point, position]
    @segments = Tuples[Segment, *segments]
  end
end

Instance Method Details

#[](i) ⇒ Object

Properties



161
# File 'lib/sevgi/geometry/element.rb', line 161

def [](i) = lines[i].tap { |line| Error.("No line exist for index: #{i}") unless line }

#approxObject

Core methods



107
108
109
110
111
112
# File 'lib/sevgi/geometry/element.rb', line 107

def approx
  points, segments = points(true), segments(true)
  self.class.send(:new) do
    @points, @segments = points, segments
  end
end

#boxObject



163
# File 'lib/sevgi/geometry/element.rb', line 163

def box = Rect.([(xs = points.map(&:x)).min, (ys = points.map(&:y)).min], [xs.max, ys.max])

#call(i) ⇒ Object



165
# File 'lib/sevgi/geometry/element.rb', line 165

def call(i) = points[i].tap { Error.("No point exist for index: #{i}") unless it }

#drawObject



114
115
116
# File 'lib/sevgi/geometry/element.rb', line 114

def draw(...)
  approx.draw!(...)
end

#eql?(other) ⇒ Boolean Also known as: ==

Equality methods

Returns:

  • (Boolean)


140
# File 'lib/sevgi/geometry/element.rb', line 140

def eql?(other) = self.class == other.class && points(true) == other.points(true)

#equationsObject

Interaction methods



148
# File 'lib/sevgi/geometry/element.rb', line 148

def equations = @equations ||= lines.map(&:equation)

#hashObject



142
# File 'lib/sevgi/geometry/element.rb', line 142

def hash = [self.class, *points(true)].hash

#headObject



167
# File 'lib/sevgi/geometry/element.rb', line 167

def head = @head ||= segments.first

#inside?(point) ⇒ Boolean

Relation methods

Returns:

  • (Boolean)


181
# File 'lib/sevgi/geometry/element.rb', line 181

def inside?(point) = on?(point) || pnpoly(points, point)

#intersection(equation, precision: nil) ⇒ Object



150
151
152
153
154
155
156
157
# File 'lib/sevgi/geometry/element.rb', line 150

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

#linesObject



169
170
171
172
173
# File 'lib/sevgi/geometry/element.rb', line 169

def lines
  @lines ||= segments.zip(points[...segments.size]).map { |segment, position|
    segment.line(position)
  }
end

#on?(point) ⇒ Boolean

Returns:

  • (Boolean)


183
# File 'lib/sevgi/geometry/element.rb', line 183

def on?(point) = lines.any? { it.over?(point) }

#outside?(point) ⇒ Boolean

Returns:

  • (Boolean)


185
# File 'lib/sevgi/geometry/element.rb', line 185

def outside?(point) = !inside?(point)

#perimeterObject



175
# File 'lib/sevgi/geometry/element.rb', line 175

def perimeter = @perimeter ||= segments.sum(&:length)

#points(approximate = false) ⇒ Object



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

def points(approximate = false)
  approximate ? (@points_approx ||= @points.map(&:approx)) : @points
end

#positionObject



122
123
124
# File 'lib/sevgi/geometry/element.rb', line 122

def position
  @position ||= points.first
end

#segments(approximate = false) ⇒ Object



126
127
128
# File 'lib/sevgi/geometry/element.rb', line 126

def segments(approximate = false)
  approximate ? (@segments_approx ||= @segments.map(&:approx)) : @segments
end

#tailObject



177
# File 'lib/sevgi/geometry/element.rb', line 177

def tail = @tail ||= segments.last