lib/mindee/geometry/utils.rb
# frozen_string_literal: true module Mindee # Various helper functions for geometry. module Geometry # Transform a prediction into a Quadrilateral. # @param prediction [Hash] # @return [Mindee::Geometry::Quadrilateral] def self.quadrilateral_from_prediction(prediction) throw "Prediction must have exactly 4 points, found #{prediction.size}" if prediction.size != 4 Quadrilateral.new( Point.new(prediction[0][0], prediction[0][1]), Point.new(prediction[1][0], prediction[1][1]), Point.new(prediction[2][0], prediction[2][1]), Point.new(prediction[3][0], prediction[3][1]) ) end # Transform a prediction into a Polygon. # @param prediction [Hash] # @return [Mindee::Geometry::Polygon] def self.polygon_from_prediction(prediction) polygon = Polygon.new return polygon if prediction.nil? prediction.each do |point| polygon << Point.new(point[0], point[1]) end polygon end # Gets the points of a bounding box for a given set of points # @param vertices [Array<Mindee::Geometry::Point>] # @return [Array<Float>] def self.get_bbox(vertices) x_coords = vertices.map(&:x) y_coords = vertices.map(&:y) [x_coords.min, y_coords.min, x_coords.max, y_coords.max] end # Creates the bounding bounding box for a given set of points # @param vertices [Array<Mindee::Geometry::Point>] # @return [Mindee::Geometry::Quadrilateral] def self.get_bounding_box(vertices) x_min, y_min, x_max, y_max = get_bbox(vertices) Quadrilateral.new( Point.new(x_min, y_min), Point.new(x_max, y_min), Point.new(x_max, y_max), Point.new(x_min, y_max) ) end # Get the central point (centroid) given a sequence of points. # @param points [Array<Mindee::Geometry::Point>] # @return [Mindee::Geometry::Point] def self.get_centroid(points) vertices_count = points.size x_sum = points.map(&:x).sum y_sum = points.map(&:y).sum Point.new(x_sum / vertices_count, y_sum / vertices_count) end # Get the maximum and minimum Y value given a sequence of points. # @param points [Array<Mindee::Geometry::Point>] # @return [Mindee::Geometry::MinMax] def self.get_min_max_y(points) coords = points.map(&:y) MinMax.new(coords.min, coords.max) end # Get the maximum and minimum X value given a sequence of points. # @param points [Array<Mindee::Geometry::Point>] # @return [Mindee::Geometry::MinMax] def self.get_min_max_x(points) coords = points.map(&:x) MinMax.new(coords.min, coords.max) end # Checks whether a set of coordinates is below another on the page, with a slight margin for the lateral value. # @param candidate [Array<Mindee::Geometry::Point] Polygon to check # @param anchor [Array<Mindee::Geometry::Point] Reference polygon # @param margin_left [Float] Margin tolerance on the left of the anchor # @param margin_right [Float] Margin tolerance on the right of the anchor def self.below?(candidate, anchor, margin_left, margin_right) return false if Geometry.get_min_max_y(candidate).min < Geometry.get_min_max_y(anchor).min if Geometry.get_min_max_x(candidate).min < Geometry.get_min_max_x(anchor).min - (Geometry.get_min_max_x(anchor).min * margin_left) return false end if Geometry.get_min_max_x(candidate).max > Geometry.get_min_max_x(anchor).max + (Geometry.get_min_max_x(anchor).max * margin_right) return false end true end end end