Class: Qrio::FinderPatternSlice

Inherits:
Region
  • Object
show all
Defined in:
lib/qrio/finder_pattern_slice.rb

Overview

QR codes have a finder pattern in three corners. any horizontal or vertical slice through the center square will be a band of: black, white, black, white, black, with widths matching ratio: 1, 1, 3, 1, 1. According to spec, the tolerance should be +/- 0.5.

Direct Known Subclasses

HorizontalMatch, VerticalMatch

Constant Summary collapse

ONE =
0.5..1.5
THREE =

not to spec, but required for some “in-the-wild” QR

2.1..3.9
ENDPOINT_TOLERANCE =

origin/terminus delta between adjacent slices

0.05
OFFSET_TOLERANCE =

how many non-matching slices can be skipped?

0.25
LENGTH_TOLERANCE =

allowed length delta bewteen 2 intersecting slices

0.35
WIDTH_TOLERANCE =

allowed width delta bewteen 2 intersecting slices

0.15

Instance Attribute Summary collapse

Attributes inherited from Region

#orientation, #x1, #x2, #y1, #y2

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Region

#==, #bottom, #bottom_right, #center, #eql?, #hash, #height, #horizontal?, #left, #orientation_matches?, #right, #rotate, #to_coordinates, #to_point_size, #top, #top_left, #translate, #union, #vertical?, #width

Constructor Details

#initialize(*args) ⇒ FinderPatternSlice

Returns a new instance of FinderPatternSlice.



19
20
21
22
# File 'lib/qrio/finder_pattern_slice.rb', line 19

def initialize(*args)
  @neighbors = []
  super
end

Instance Attribute Details

#neighborsObject

Returns the value of attribute neighbors.



16
17
18
# File 'lib/qrio/finder_pattern_slice.rb', line 16

def neighbors
  @neighbors
end

#offsetObject (readonly)

Returns the value of attribute offset.



17
18
19
# File 'lib/qrio/finder_pattern_slice.rb', line 17

def offset
  @offset
end

#originObject (readonly)

Returns the value of attribute origin.



17
18
19
# File 'lib/qrio/finder_pattern_slice.rb', line 17

def origin
  @origin
end

#terminusObject (readonly)

Returns the value of attribute terminus.



17
18
19
# File 'lib/qrio/finder_pattern_slice.rb', line 17

def terminus
  @terminus
end

Class Method Details

.build_matching(offset, origin, widths, direction) ⇒ Object

given a width buffer extracted from a given coordinate, test for ratio matching. if it matches, return a match object of the appropriate orientation



49
50
51
52
53
54
55
56
# File 'lib/qrio/finder_pattern_slice.rb', line 49

def build_matching(offset, origin, widths, direction)
  return nil unless matches_ratio?(widths)

  match_class = direction == :horizontal ? HorizontalMatch : VerticalMatch
  terminus = origin + widths.inject(0){|s,w| s + w } - 1

  match_class.build(offset, origin, terminus)
end

.matches_ratio?(widths) ⇒ Boolean

Returns:

  • (Boolean)


58
59
60
61
62
63
64
65
66
# File 'lib/qrio/finder_pattern_slice.rb', line 58

def matches_ratio?(widths)
  norm = normalized_ratio(widths)

  ONE.include?(norm[0]) &&
  ONE.include?(norm[1]) &&
  THREE.include?(norm[2]) &&
  ONE.include?(norm[3]) &&
  ONE.include?(norm[4])
end

.normalized_ratio(widths) ⇒ Object



68
69
70
71
# File 'lib/qrio/finder_pattern_slice.rb', line 68

def normalized_ratio(widths)
  scale = (widths[0] + widths[1] + widths[3] + widths[4]) / 4.0
  widths.map{|w| w / scale }
end

Instance Method Details

#<=>(other) ⇒ Object



74
75
76
77
78
# File 'lib/qrio/finder_pattern_slice.rb', line 74

def <=>(other)
  return -1 if offset < other.offset
  return  1 if offset > other.offset
  origin <=> other.origin
end

#adjacent?(other) ⇒ Boolean

Returns:

  • (Boolean)


88
89
90
# File 'lib/qrio/finder_pattern_slice.rb', line 88

def adjacent?(other)
  endpoints_match?(other) && offset_matches?(other)
end

#aspect_ratioObject



32
33
34
# File 'lib/qrio/finder_pattern_slice.rb', line 32

def aspect_ratio
  breadth / length.to_f
end

#breadth_diff(other) ⇒ Object



128
129
130
# File 'lib/qrio/finder_pattern_slice.rb', line 128

def breadth_diff(other)
  (breadth - other.breadth).abs / breadth.to_f
end

#breadth_matches?(other) ⇒ Boolean

Returns:

  • (Boolean)


132
133
134
# File 'lib/qrio/finder_pattern_slice.rb', line 132

def breadth_matches?(other)
  breadth_diff(other) <= WIDTH_TOLERANCE
end

#class_name_prefixObject



28
29
30
# File 'lib/qrio/finder_pattern_slice.rb', line 28

def class_name_prefix
  self.class.to_s.gsub(/^.*::/, '')[0,1]
end

#endpoints_match?(other) ⇒ Boolean

Returns:

  • (Boolean)


92
93
94
# File 'lib/qrio/finder_pattern_slice.rb', line 92

def endpoints_match?(other)
  origin_matches?(other) && terminus_matches?(other)
end

#intersects?(other) ⇒ Boolean

Returns:

  • (Boolean)


80
81
82
83
84
85
86
# File 'lib/qrio/finder_pattern_slice.rb', line 80

def intersects?(other)
  ! orientation_matches?(other) &&
  (other.origin..other.terminus).include?(offset) &&
  (origin..terminus).include?(other.offset) &&
  length_matches?(other) &&
  breadth_matches?(other)
end

#length_diff(other) ⇒ Object



120
121
122
# File 'lib/qrio/finder_pattern_slice.rb', line 120

def length_diff(other)
  (length - other.length).abs / length.to_f
end

#length_matches?(other) ⇒ Boolean

Returns:

  • (Boolean)


124
125
126
# File 'lib/qrio/finder_pattern_slice.rb', line 124

def length_matches?(other)
  length_diff(other) <= LENGTH_TOLERANCE
end

#matches_aspect_ratio?Boolean

based on the 1, 1, 3, 1, 1 width ratio, a finder pattern has total width of 7. an ideal grouped slice would then have aspect ratio of 3/7, since slice breadth would be 3 (just the center square) and length would be 7 (entire slice)

Returns:

  • (Boolean)


40
41
42
# File 'lib/qrio/finder_pattern_slice.rb', line 40

def matches_aspect_ratio?
  (0.25..0.59).include? aspect_ratio
end

#normalized_offset_diff(other) ⇒ Object



112
113
114
# File 'lib/qrio/finder_pattern_slice.rb', line 112

def normalized_offset_diff(other)
  offset_diff(other) / length.to_f
end

#offset_matches?(other) ⇒ Boolean

Returns:

  • (Boolean)


116
117
118
# File 'lib/qrio/finder_pattern_slice.rb', line 116

def offset_matches?(other)
  normalized_offset_diff(other) <= OFFSET_TOLERANCE
end

#origin_diff(other) ⇒ Object



96
97
98
# File 'lib/qrio/finder_pattern_slice.rb', line 96

def origin_diff(other)
  (origin - other.origin).abs / length.to_f
end

#origin_matches?(other) ⇒ Boolean

Returns:

  • (Boolean)


100
101
102
# File 'lib/qrio/finder_pattern_slice.rb', line 100

def origin_matches?(other)
  origin_diff(other) <= ENDPOINT_TOLERANCE
end

#terminus_diff(other) ⇒ Object



104
105
106
# File 'lib/qrio/finder_pattern_slice.rb', line 104

def terminus_diff(other)
  (terminus - other.terminus).abs / length.to_f
end

#terminus_matches?(other) ⇒ Boolean

Returns:

  • (Boolean)


108
109
110
# File 'lib/qrio/finder_pattern_slice.rb', line 108

def terminus_matches?(other)
  terminus_diff(other) <= ENDPOINT_TOLERANCE
end

#to_sObject



24
25
26
# File 'lib/qrio/finder_pattern_slice.rb', line 24

def to_s
  "#{ class_name_prefix }[#{ to_coordinates.join(',') }]"
end