Class: Pdfrb::Layout::PolygonFrame
- Defined in:
- lib/pdfrb/layout/polygon_frame.rb
Overview
Polygon-aware Frame: extends Frame to compute available area against an arbitrary polygon shape, not just a rectangle. Useful for L-shaped text regions, text-around-image, and irregular page layouts.
The default Frame.find_available_area always returns the top-left rectangle; this subclass finds the largest inscribed rectangle of (width × height) inside the polygon, scanning row-by-row to find the leftmost x where the requested box fits at the requested height.
Constant Summary collapse
- DEFAULT_STEP =
1.0
Instance Attribute Summary collapse
-
#polygon ⇒ Object
readonly
Returns the value of attribute polygon.
Attributes inherited from Frame
#bottom, #height, #left, #shape, #width
Instance Method Summary collapse
- #bounding_box_bottom ⇒ Object
- #bounding_box_left ⇒ Object
- #bounding_box_right ⇒ Object
- #bounding_box_top ⇒ Object
-
#contains_point?(x, y) ⇒ Boolean
Point-in-polygon test (ray casting).
- #contains_rectangle?(x, y, w, h) ⇒ Boolean
-
#find_available_area(width, height) ⇒ Object
Find the next available area inside the polygon that fits (width, height).
-
#initialize(left:, bottom:, width:, height:, polygon: nil, step: DEFAULT_STEP) ⇒ PolygonFrame
constructor
A new instance of PolygonFrame.
Methods inherited from Frame
#empty?, #full?, #remove_area, #right, #top
Constructor Details
#initialize(left:, bottom:, width:, height:, polygon: nil, step: DEFAULT_STEP) ⇒ PolygonFrame
Returns a new instance of PolygonFrame.
24 25 26 27 28 29 30 |
# File 'lib/pdfrb/layout/polygon_frame.rb', line 24 def initialize(left:, bottom:, width:, height:, polygon: nil, step: DEFAULT_STEP) super(left: left, bottom: bottom, width: width, height: height) @polygon = polygon || [[left, bottom], [left + width, bottom], [left + width, bottom + height], [left, bottom + height]] @step = step.to_f end |
Instance Attribute Details
#polygon ⇒ Object (readonly)
Returns the value of attribute polygon.
18 19 20 |
# File 'lib/pdfrb/layout/polygon_frame.rb', line 18 def polygon @polygon end |
Instance Method Details
#bounding_box_bottom ⇒ Object
65 66 67 |
# File 'lib/pdfrb/layout/polygon_frame.rb', line 65 def bounding_box_bottom @polygon.map { |_x, y| y }.min.to_f end |
#bounding_box_left ⇒ Object
69 70 71 |
# File 'lib/pdfrb/layout/polygon_frame.rb', line 69 def bounding_box_left @polygon.map { |x, _y| x }.min.to_f end |
#bounding_box_right ⇒ Object
73 74 75 |
# File 'lib/pdfrb/layout/polygon_frame.rb', line 73 def bounding_box_right @polygon.map { |x, _y| x }.max.to_f end |
#bounding_box_top ⇒ Object
61 62 63 |
# File 'lib/pdfrb/layout/polygon_frame.rb', line 61 def bounding_box_top @polygon.map { |_x, y| y }.max.to_f end |
#contains_point?(x, y) ⇒ Boolean
Point-in-polygon test (ray casting).
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
# File 'lib/pdfrb/layout/polygon_frame.rb', line 78 def contains_point?(x, y) inside = false n = @polygon.length i = 0 j = n - 1 while i < n xi, yi = @polygon[i] xj, yj = @polygon[j] if ((yi > y) != (yj > y)) && (x < ((xj - xi) * (y - yi) / (yj - yi)) + xi) inside = !inside end j = i i += 1 end inside end |
#contains_rectangle?(x, y, w, h) ⇒ Boolean
96 97 98 99 100 101 102 103 |
# File 'lib/pdfrb/layout/polygon_frame.rb', line 96 def contains_rectangle?(x, y, w, h) [ [x, y], [x + w, y], [x, y + h], [x + w, y + h], ].all? { |px, py| contains_point?(px, py) } end |
#find_available_area(width, height) ⇒ Object
Find the next available area inside the polygon that fits (width, height). Walks row by row from the top of the bounding box downward; at each row y, scans x from left toward the right edge, returning the first (x, y, w, h) position where all four corners of (x, y - h, w, h) are inside the polygon.
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/pdfrb/layout/polygon_frame.rb', line 38 def find_available_area(width, height) w = width.to_f h = height.to_f return nil unless w.positive? && h.positive? bbox_top = bounding_box_top bbox_bottom = bounding_box_bottom bbox_left = bounding_box_left bbox_right = bounding_box_right y = bbox_top while y - h >= bbox_bottom x = bbox_left while x + w <= bbox_right return [x, y, w, h] if contains_rectangle?(x, y - h, w, h) x += @step end y -= @step end nil end |