Module: Overlap

Defined in:
lib/nswtopo/geometry/overlap.rb

Instance Method Summary collapse

Instance Method Details

#overlap?(buffer = 0) ⇒ Boolean

Returns:

  • (Boolean)


42
43
44
# File 'lib/nswtopo/geometry/overlap.rb', line 42

def overlap?(buffer = 0)
  !separated_by?(buffer)
end

#overlaps(buffer = 0) ⇒ Object



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
# File 'lib/nswtopo/geometry/overlap.rb', line 46

def overlaps(buffer = 0)
  return [] if empty?
  axis = flatten(1).transpose.map { |values| values.max - values.min }.map.with_index.max.last
  events, tops, bots, results = AVLTree.new, [], [], []
  margin = [buffer, 0]
  each.with_index do |hull, index|
    min, max = hull.map { |point| point.rotate axis }.minmax
    events << [min.minus(margin), index, :start]
    events << [max.plus( margin), index, :stop ]
  end
  events.each do |point, index, event|
    top, bot = at(index).transpose[1-axis].minmax
    case event
    when :start
      not_above = bots.select { |bot, other| bot >= top - buffer }.map(&:last)
      not_below = tops.select { |top, other| top <= bot + buffer }.map(&:last)
      (not_below & not_above).reject do |other|
        values_at(index, other).separated_by? buffer
      end.each do |other|
        results << [index, other]
      end
      tops << [top, index]
      bots << [bot, index]
    when :stop
      tops.delete [top, index]
      bots.delete [bot, index]
    end
  end
  results
end

#separated_by?(buffer) ⇒ Boolean

Returns:

  • (Boolean)


2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/nswtopo/geometry/overlap.rb', line 2

def separated_by?(buffer)
  simplex = [map(&:first).inject(&:minus)]
  perp = simplex[0].perp
  loop do
    return false unless case
    when simplex.one? then simplex[0].norm
    when simplex.inject(&:minus).dot(simplex[1]) > 0 then simplex[1].norm
    when simplex.inject(&:minus).dot(simplex[0]) < 0 then simplex[0].norm
    else simplex.inject(&:cross).abs / simplex.inject(&:minus).norm
    end > buffer
    max = self[0].max_by { |point| perp.cross point }
    min = self[1].min_by { |point| perp.cross point }
    support = max.minus min
    return true unless simplex[0].minus(support).cross(perp) > 0
    rays = simplex.map { |point| point.minus support }
    case simplex.length
    when 1
      case
      when rays[0].dot(support) > 0
        simplex, perp = [support], support.perp
      when rays[0].cross(support) < 0
        simplex, perp = [support, *simplex], rays[0]
      else
        simplex, perp = [*simplex, support], rays[0].negate
      end
    when 2
      case
      when rays[0].cross(support) > 0 && rays[0].dot(support) < 0
        simplex, perp = [simplex[0], support], rays[0].negate
      when rays[1].cross(support) < 0 && rays[1].dot(support) < 0
        simplex, perp = [support, simplex[1]], rays[1]
      when rays[0].cross(support) <= 0 && rays[1].cross(support) >= 0
        return false
      else
        simplex, perp = [support], support.perp
      end
    end
  end
end