Class: Fontisan::Variation::RegionMatcher
- Inherits:
-
Object
- Object
- Fontisan::Variation::RegionMatcher
- Defined in:
- lib/fontisan/variation/region_matcher.rb
Overview
Region matcher for variable fonts
This class matches design space coordinates to variation regions/tuples, determining which regions contribute to the final interpolated value and calculating their contribution scalars.
A variation region defines a sub-space within the design space where a particular set of deltas applies. Regions are defined by start, peak, and end coordinates on each axis.
Matching Process:
- For each region, check if current coordinates fall within the region
- Calculate the scalar (contribution factor) for each matching region
- Return only non-zero contributions
Reference: OpenType Font Variations specification, gvar table
Instance Attribute Summary collapse
-
#axes ⇒ Array<VariationAxisRecord>
readonly
Variation axes.
-
#interpolator ⇒ Interpolator
readonly
Coordinate interpolator.
Instance Method Summary collapse
-
#active_region_indices(coordinates, regions) ⇒ Array<Integer>
Get active regions at coordinates.
-
#build_region_from_arrays(start_arr, peak_arr, end_arr) ⇒ Hash<String, Hash>
Build region from start, peak, end arrays.
-
#build_region_from_peaks(peaks) ⇒ Hash<String, Hash>
Build region from peak coordinates.
-
#contribution_percentages(coordinates, regions) ⇒ Array<Float>
Calculate contribution percentages for all regions.
-
#dominant_region(coordinates, regions) ⇒ Hash?
Find the dominant region at coordinates.
-
#initialize(axes) ⇒ RegionMatcher
constructor
Initialize region matcher.
-
#match_regions(coordinates:, regions:) ⇒ Array<Hash>
Match coordinates to variation regions.
-
#match_tuples(coordinates:, tuples:) ⇒ Array<Hash>
Match coordinates to gvar tuple variations.
-
#valid_region?(region) ⇒ Boolean
Validate region definition.
-
#within_region?(coordinates, region) ⇒ Boolean
Check if coordinates are within a region.
Constructor Details
#initialize(axes) ⇒ RegionMatcher
Initialize region matcher
39 40 41 42 |
# File 'lib/fontisan/variation/region_matcher.rb', line 39 def initialize(axes) @axes = axes || [] @interpolator = Interpolator.new(@axes) end |
Instance Attribute Details
#axes ⇒ Array<VariationAxisRecord> (readonly)
Returns Variation axes.
31 32 33 |
# File 'lib/fontisan/variation/region_matcher.rb', line 31 def axes @axes end |
#interpolator ⇒ Interpolator (readonly)
Returns Coordinate interpolator.
34 35 36 |
# File 'lib/fontisan/variation/region_matcher.rb', line 34 def interpolator @interpolator end |
Instance Method Details
#active_region_indices(coordinates, regions) ⇒ Array<Integer>
Get active regions at coordinates
Returns the subset of regions that are active (non-zero contribution) at the given coordinates.
108 109 110 111 |
# File 'lib/fontisan/variation/region_matcher.rb', line 108 def active_region_indices(coordinates, regions) matches = match_regions(coordinates: coordinates, regions: regions) matches.map { |m| m[:region_index] } end |
#build_region_from_arrays(start_arr, peak_arr, end_arr) ⇒ Hash<String, Hash>
Build region from start, peak, end arrays
Converts array-based region data (as in gvar) to hash-based format.
184 185 186 187 188 189 190 191 192 193 194 195 196 |
# File 'lib/fontisan/variation/region_matcher.rb', line 184 def build_region_from_arrays(start_arr, peak_arr, end_arr) region = {} @axes.each_with_index do |axis, index| region[axis.axis_tag] = { start: start_arr[index] || -1.0, peak: peak_arr[index] || 0.0, end: end_arr[index] || 1.0, } end region end |
#build_region_from_peaks(peaks) ⇒ Hash<String, Hash>
Build region from peak coordinates
Creates a simple region definition from peak coordinates only, using ±1.0 for start/end on each axis.
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/fontisan/variation/region_matcher.rb', line 159 def build_region_from_peaks(peaks) region = {} @axes.each do |axis| tag = axis.axis_tag peak = peaks[tag] || 0.0 region[tag] = { start: peak.negative? ? -1.0 : 0.0, peak: peak, end: peak.positive? ? 1.0 : 0.0, } end region end |
#contribution_percentages(coordinates, regions) ⇒ Array<Float>
Calculate contribution percentages for all regions
Returns the percentage contribution of each region at the given coordinates. All percentages sum to 100% (or less if some regions are inactive).
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'lib/fontisan/variation/region_matcher.rb', line 122 def contribution_percentages(coordinates, regions) matches = match_regions(coordinates: coordinates, regions: regions) # Calculate total scalar total_scalar = matches.sum { |m| m[:scalar] } return Array.new(regions.size, 0.0) if total_scalar.zero? # Build percentage array percentages = Array.new(regions.size, 0.0) matches.each do |match| percentages[match[:region_index]] = match[:scalar] / total_scalar end percentages end |
#dominant_region(coordinates, regions) ⇒ Hash?
Find the dominant region at coordinates
Returns the region with the highest contribution scalar.
145 146 147 148 149 150 |
# File 'lib/fontisan/variation/region_matcher.rb', line 145 def dominant_region(coordinates, regions) matches = match_regions(coordinates: coordinates, regions: regions) return nil if matches.empty? matches.max_by { |m| m[:scalar] } end |
#match_regions(coordinates:, regions:) ⇒ Array<Hash>
Match coordinates to variation regions
Returns all regions that contribute (have non-zero scalar) at the given coordinates, along with their contribution scalars.
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/fontisan/variation/region_matcher.rb', line 52 def match_regions(coordinates:, regions:) # Normalize coordinates normalized = @interpolator.normalize_coordinates(coordinates) # Find matching regions matches = [] regions.each_with_index do |region, index| scalar = @interpolator.calculate_region_scalar(normalized, region) # Only include non-zero contributions matches << { region_index: index, scalar: scalar } if scalar > 0.0 end matches end |
#match_tuples(coordinates:, tuples:) ⇒ Array<Hash>
Match coordinates to gvar tuple variations
Converts gvar tuple data to regions and matches them.
75 76 77 78 79 80 81 82 83 |
# File 'lib/fontisan/variation/region_matcher.rb', line 75 def match_tuples(coordinates:, tuples:) # Convert tuples to regions regions = tuples.map do |tuple| @interpolator.build_region_from_tuple(tuple) end # Match regions match_regions(coordinates: coordinates, regions: regions) end |
#valid_region?(region) ⇒ Boolean
Validate region definition
Checks if a region is well-formed.
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
# File 'lib/fontisan/variation/region_matcher.rb', line 204 def valid_region?(region) return false unless region.is_a?(Hash) region.all? do |_axis_tag, axis_region| next false unless axis_region.is_a?(Hash) next false unless axis_region.key?(:peak) start_val = axis_region[:start] || -1.0 peak = axis_region[:peak] end_val = axis_region[:end] || 1.0 # Validate ordering: start <= peak <= end start_val <= peak && peak <= end_val end end |
#within_region?(coordinates, region) ⇒ Boolean
Check if coordinates are within a region
90 91 92 93 94 95 96 97 98 |
# File 'lib/fontisan/variation/region_matcher.rb', line 90 def within_region?(coordinates, region) region.all? do |axis_tag, axis_region| coord = coordinates[axis_tag] || 0.0 start_val = axis_region[:start] || -1.0 end_val = axis_region[:end] || 1.0 coord >= start_val && coord <= end_val end end |