Geometry
Geometry supplies a small set of immutable values for calculations the SVG renderer cannot do for you. It uses SVG screen coordinates: positive x goes right, positive y goes down, and positive angles turn clockwise.
Think of the component as the calculation layer beneath a drawing: build values, transform or intersect them, then pass
the resulting lines or shapes to Draw, Hatch, Align, or your own Ruby code. No geometry constructor adds an SVG
element by itself.
Points and lines
point = Sevgi::Geometry::Point[3, 4]
line = Sevgi::Geometry::Line.([0, 0], point)
line.length # => 5.0
point.translate(2, 1) # => Point[5.0, 5.0]
Points and lined shapes return new values from translate, rotate, scale, skew, and reflect; the original value
does not change. A Segment is an unplaced length and direction; a Line places that segment between finite endpoints:
segment = Sevgi::Geometry::Segment[5, 30]
ending = segment.ending([2, 3])
line = segment.line([2, 3])Shapes
Rect, Triangle, Parallelogram, Polyline, and Polygon provide boxes, points, edges, and affine operations. Pick
the constructor that matches the information you already have. Brackets take lengths and segments; .() takes points:
box = Sevgi::Geometry::Rect[40, 24, position: [6, 8]]
same_box = Sevgi::Geometry::Rect.([6, 8], [46, 32])
polar_line = Sevgi::Geometry::Line[10, 30, position: [2, 3]]
point_line = Sevgi::Geometry::Line.([2, 3], [12, 8])
open_path = Sevgi::Geometry::Polyline.([0, 0], [8, 0], [8, 5])
closed_path = Sevgi::Geometry::Polygon.([0, 0], [8, 0], [8, 5], [0, 5])
triangle = Sevgi::Geometry::Triangle.([0, 20], [10, 0], [20, 20])
The English constructors such as Rect.from_size, Rect.from_corners, Line.from_length_angle, and
Line.from_points are aliases for the same two input families. Use them when the call site benefits from saying which
representation it has.
Parallelogram segment names describe geometric roles, not screen axes. base runs from A to B and side from A to D;
both begin at position. The constrained constructors derive the missing segment while preserving the requested
bounding dimension:
shape = Sevgi::Geometry::Parallelogram.new_by_height(
base: [12, 15],
constraint: [8, 105],
position: [2, 3]
)
shape.AB.angle # => 15.0
shape.DA.angle # => 105.0
shape.box.height # => 8.0
Every lined shape exposes the same path in three forms: points for vertex work, segments for reusable polar
displacements, and lines for finite positioned edges. Closed shapes repeat the first point at the end of points, so
a rectangle has five path points but four segments and four lines.
Closed shapes distinguish interior, boundary, and exterior points. Open paths have no filled interior:
box = Sevgi::Geometry::Rect[40, 24, position: [6, 8]]
open_path = Sevgi::Geometry::Polyline.([0, 0], [8, 0], [8, 5])
box.inside?([20, 20]) # => true
box.on?([6, 20]) # => true
box.outside?([50, 20]) # => true
open_path.inside?([4, 2]) # => falseAlignment
Alignment calculates the translation between an inner and outer box. The DSL Align helper applies that translation
as an SVG transform. Library code can request either the offset or a translated geometry value directly:
inner = Sevgi::Geometry::Rect[8, 4]
outer = Sevgi::Geometry::Rect[40, 20, position: [5, 5]]
offset = Sevgi::Geometry::Operation.alignment(inner, outer, :center)
centered = Sevgi::Geometry::Operation.align(inner, outer, :center)
:center adjusts both coordinates. :left, :right, :top, and :bottom adjust only the named axis; this makes it
possible to align one edge without losing a position already chosen on the other axis.
The drawing equivalent is:
inner = Sevgi::Geometry::Rect[8, 4]
outer = Sevgi::Geometry::Rect[40, 20, position: [5, 5]]
SVG :minimal do
shape = rect width: 8, height: 4
shape.Align :center, inner:, outer:
end.RenderDrawing
In an Inkscape document, Draw converts geometry into suitable SVG elements:
region = Sevgi::Geometry::Rect[48, 18, position: [6, 6]]
SVG :inkscape do
trim = Sevgi::Geometry::Rect[80, 50, position: [5, 5]]
Draw trim.lines, class: %w[guide trim], stroke: "tomato"
end.RenderSweeps and hatching
Sweeps intersect parallel lines with a closed geometry shape. angle is the direction of the returned lines, while
step is the perpendicular spacing between them. sweep may return an empty Array; sweep!, used by Hatch, requires
at least one span. Open paths have no interior and therefore produce no sweep spans:
region = Sevgi::Geometry::Rect[48, 18, position: [6, 6]]
lines = Sevgi::Geometry::Operation.sweep(region, initial: region.position, angle: 30, step: 3)
The same geometry can be sent to a document; Hatch uses region.position as its initial line unless initial: is
given:
region = Sevgi::Geometry::Rect[48, 18, position: [6, 6]]
SVG :inkscape do
Hatch region, angle: 30, step: 3, class: %w[guide no-print], stroke: "black"
end.Render
Hatch materializes each finite segment as a separate SVG path. Use it when those segments must remain editable,
inspectable, or available as explicit geometry. If only the rendered striped fill matters, prefer an SVG
pattern and let the renderer repeat and
clip it.
Arc and curve preparation is still incomplete, so those APIs are not supported yet.