Module: Idml::Geometry
- Defined in:
- lib/idml/geometry.rb
Overview
Coordinate-transform helpers for composition operations. The IDML coordinate system has its origin at the bottom-left of the spread, y increasing upward; page item coordinates are spread-relative. When composing two packages, source coordinates must be translated into the destination's coordinate space.
Defined Under Namespace
Class Method Summary collapse
-
.apply_transform(transform, x, y) ⇒ Object
Apply a Transform to a point [x, y], returning [x', y'].
-
.bounds_to_pdf_rect(bounds, page_height) ⇒ Object
Convert IDML bounds [y1, x1, y2, x2] to a PDF rectangle { x:, y:, width:, height: } with Y-axis flipped.
-
.combine_transforms(outer, inner) ⇒ Object
Combine (multiply) two transforms: result = outer ∘ inner.
- .offset(x:, y:) ⇒ Object
-
.parse_transform(str) ⇒ Object
Parse an ItemTransform string "a b c d e f" into a Transform.
-
.placement_rect(bounds, item_transform_str, page_height) ⇒ Object
Convert IDML bounds + ItemTransform string to a PDF rect with Y-axis flip.
-
.rotate(point, angle_degrees:, around: offset(x: 0, y: 0)) ⇒ Object
Rotate
pointcounterclockwise byangle_degreesaroundaround. - .scale(point, by: offset(x: 1, y: 1)) ⇒ Object
-
.transform_bounds(bounds, transform = nil) ⇒ Object
Transform IDML geometric_bounds [y1, x1, y2, x2] by applying an optional ItemTransform.
- .translate(point, by: offset(x: 0, y: 0)) ⇒ Object
Class Method Details
.apply_transform(transform, x, y) ⇒ Object
Apply a Transform to a point [x, y], returning [x', y'].
30 31 32 33 34 35 |
# File 'lib/idml/geometry.rb', line 30 def apply_transform(transform, x, y) return [x, y] unless transform [(transform.a * x) + (transform.c * y) + transform.e, (transform.b * x) + (transform.d * y) + transform.f] end |
.bounds_to_pdf_rect(bounds, page_height) ⇒ Object
Convert IDML bounds [y1, x1, y2, x2] to a PDF rectangle { x:, y:, width:, height: } with Y-axis flipped.
75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/idml/geometry.rb', line 75 def bounds_to_pdf_rect(bounds, page_height) return nil unless bounds && bounds.length == 4 y1, x1, y2, x2 = bounds { x: x1, y: page_height - y2, width: (x2 - x1).abs, height: (y2 - y1).abs, } end |
.combine_transforms(outer, inner) ⇒ Object
Combine (multiply) two transforms: result = outer ∘ inner.
38 39 40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/idml/geometry.rb', line 38 def combine_transforms(outer, inner) return inner unless outer return outer unless inner Transform.new( (outer.a * inner.a) + (outer.c * inner.b), (outer.b * inner.a) + (outer.d * inner.b), (outer.a * inner.c) + (outer.c * inner.d), (outer.b * inner.c) + (outer.d * inner.d), (outer.a * inner.e) + (outer.c * inner.f) + outer.e, (outer.b * inner.e) + (outer.d * inner.f) + outer.f, ) end |
.offset(x:, y:) ⇒ Object
106 107 108 |
# File 'lib/idml/geometry.rb', line 106 def offset(x:, y:) Point.new(x, y) end |
.parse_transform(str) ⇒ Object
Parse an ItemTransform string "a b c d e f" into a Transform.
20 21 22 23 24 25 26 27 |
# File 'lib/idml/geometry.rb', line 20 def parse_transform(str) return nil unless str parts = str.split(/\s+/).map(&:to_f) return nil unless parts.length == 6 Transform.new(*parts) end |
.placement_rect(bounds, item_transform_str, page_height) ⇒ Object
Convert IDML bounds + ItemTransform string to a PDF rect with Y-axis flip. Single entry point for all renderers.
67 68 69 70 71 |
# File 'lib/idml/geometry.rb', line 67 def placement_rect(bounds, item_transform_str, page_height) transform = parse_transform(item_transform_str) transformed = transform_bounds(bounds, transform) bounds_to_pdf_rect(transformed, page_height) end |
.rotate(point, angle_degrees:, around: offset(x: 0, y: 0)) ⇒ Object
Rotate point counterclockwise by angle_degrees around around.
96 97 98 99 100 101 102 103 104 |
# File 'lib/idml/geometry.rb', line 96 def rotate(point, angle_degrees:, around: offset(x: 0, y: 0)) rad = angle_degrees * Math::PI / 180 cos = Math.cos(rad) sin = Math.sin(rad) dx = point.x - around.x dy = point.y - around.y Point.new(around.x + ((dx * cos) - (dy * sin)), around.y + ((dx * sin) + (dy * cos))) end |
.scale(point, by: offset(x: 1, y: 1)) ⇒ Object
91 92 93 |
# File 'lib/idml/geometry.rb', line 91 def scale(point, by: offset(x: 1, y: 1)) Point.new(point.x * by.x, point.y * by.y) end |
.transform_bounds(bounds, transform = nil) ⇒ Object
Transform IDML geometric_bounds [y1, x1, y2, x2] by applying an optional ItemTransform. Returns new [y1, x1, y2, x2].
54 55 56 57 58 59 60 61 62 63 |
# File 'lib/idml/geometry.rb', line 54 def transform_bounds(bounds, transform = nil) return bounds unless transform && bounds y1, x1, y2, x2 = bounds x1p, y1p = apply_transform(transform, x1, y1) x2p, y2p = apply_transform(transform, x2, y2) xs = [x1p, x2p] ys = [y1p, y2p] [ys.min, xs.min, ys.max, xs.max] end |
.translate(point, by: offset(x: 0, y: 0)) ⇒ Object
87 88 89 |
# File 'lib/idml/geometry.rb', line 87 def translate(point, by: offset(x: 0, y: 0)) Point.new(point.x + by.x, point.y + by.y) end |