class HexaPDF::Content::GraphicObject::EndpointArc

This class describes an elliptical arc in endpoint parameterization. It allows one to generate an arc from the current point to a given point, similar to Content::Canvas#line_to.

See: GraphicObject::Arc, ARC - www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes

Constants

EPSILON

Attributes

a[R]

Length of semi-major axis

b[R]

Length of semi-minor axis

clockwise[R]

Direction of arc - if true in clockwise direction, else in counterclockwise direction

inclination[R]

Inclination in degrees of semi-major axis in respect to x-axis

large_arc[R]

Large arc choice - if true use the large arc (i.e. the one spanning more than 180 degrees), else the small arc

x[R]

x-coordinate of endpoint

y[R]

y-coordinate of endpoint

Public Class Methods

configure(**kwargs) click to toggle source

Creates and configures a new endpoint arc object.

See configure for the allowed keyword arguments.

# File lib/hexapdf/content/graphic_object/endpoint_arc.rb, line 55
def self.configure(**kwargs)
  new.configure(**kwargs)
end
new() click to toggle source

Creates an endpoint arc with default values x=0, y=0, a=0, b=0, inclination=0, large_arc=true, clockwise=false (a line to the origin).

# File lib/hexapdf/content/graphic_object/endpoint_arc.rb, line 83
def initialize
  @x = @y = 0
  @a = @b = 0
  @inclination = 0
  @large_arc = true
  @clockwise = false
end

Public Instance Methods

configure(x: nil, y: nil, a: nil, b: nil, inclination: nil, large_arc: nil, clockwise: nil) click to toggle source

Configures the endpoint arc with

  • endpoint (x, y),

  • semi-major axis a,

  • semi-minor axis b,

  • an inclination in respect to the x-axis of inclination degrees,

  • the given large_arc flag and

  • the given clockwise flag.

The large_arc option determines whether the large arc, i.e. the one spanning more than 180 degrees, is used (true) or the small arc (false).

The clockwise option determines if the arc is drawn in the counterclockwise direction (false) or in the clockwise direction (true).

Any arguments not specified are not modified and retain their old value, see initialize for the inital values.

Returns self.

# File lib/hexapdf/content/graphic_object/endpoint_arc.rb, line 110
def configure(x: nil, y: nil, a: nil, b: nil, inclination: nil, large_arc: nil,
              clockwise: nil)
  @x = x if x
  @y = y if y
  @a = a.abs if a
  @b = b.abs if b
  @inclination = inclination % 360 if inclination
  @large_arc = large_arc unless large_arc.nil?
  @clockwise = clockwise unless clockwise.nil?

  self
end
draw(canvas) click to toggle source

Draws the arc on the given Canvas.

# File lib/hexapdf/content/graphic_object/endpoint_arc.rb, line 124
def draw(canvas)
  x1, y1 = *canvas.current_point

  # ARC F.6.2 - nothing to do if endpoint is equal to current point
  return if float_equal(x1, @x) && float_equal(y1, @y)

  if @a == 0 || @b == 0
    # ARC F.6.2, F.6.6 - just use a line if it is not really an arc
    canvas.line_to(@x, @y)
  else
    values = compute_arc_values(x1, y1)
    arc = canvas.graphic_object(:arc, **values)
    arc.draw(canvas, move_to_start: false)
  end
end

Private Instance Methods

compute_angle_to_x_axis(vx, vy) click to toggle source

Computes the angle in degrees between the x-axis and the vector.

# File lib/hexapdf/content/graphic_object/endpoint_arc.rb, line 202
def compute_angle_to_x_axis(vx, vy)
  (vy < 0 ? -1 : 1) * rad_to_deg(Math.acos(vx / Math.sqrt(vx**2 + vy**2)))
end
compute_arc_values(x1, y1) click to toggle source

Compute the center parameterization from the endpoint parameterization.

The argument (x1, y1) is the starting point.

See: ARC F.6.5, F.6.6

# File lib/hexapdf/content/graphic_object/endpoint_arc.rb, line 147
def compute_arc_values(x1, y1)
  x2 = @x
  y2 = @y
  rx = @a
  ry = @b
  theta = deg_to_rad(@inclination)
  cos_theta = Math.cos(theta)
  sin_theta = Math.sin(theta)

  # F.6.5.1
  x1p = (x1 - x2) / 2.0 * cos_theta + (y1 - y2) / 2.0 * sin_theta
  y1p = (x1 - x2) / 2.0 * -sin_theta + (y1 - y2) / 2.0 * cos_theta

  x1ps = x1p**2
  y1ps = y1p**2
  rxs = rx**2
  rys = ry**2

  # F.6.6.2
  l = x1ps / rxs + y1ps / rys
  if l > 1
    rx *= Math.sqrt(l)
    ry *= Math.sqrt(l)
    rxs = rx**2
    rys = ry**2
  end

  # F.6.5.2
  sqrt = (rxs * rys - rxs * y1ps - rys * x1ps) / (rxs * y1ps + rys * x1ps)
  sqrt = 0 if sqrt.abs < EPSILON
  sqrt = Math.sqrt(sqrt)
  sqrt *= -1 unless @large_arc == @clockwise
  cxp = sqrt * rx * y1p / ry
  cyp = - sqrt * ry * x1p / rx

  # F.6.5.3
  cx = cos_theta * cxp - sin_theta * cyp + (x1 + x2) / 2.0
  cy = sin_theta * cxp + cos_theta * cyp + (y1 + y2) / 2.0

  # F.6.5.5
  start_angle = compute_angle_to_x_axis((x1p - cxp) / rx, (y1p - cyp) / ry)

  # F.6.5.6 (modified bc we just need the end angle)
  end_angle = compute_angle_to_x_axis((-x1p - cxp) / rx, (-y1p - cyp) / ry)

  {cx: cx, cy: cy, a: rx, b: ry, start_angle: start_angle, end_angle: end_angle,
   inclination: @inclination, clockwise: @clockwise}
end
float_equal(a, b) click to toggle source

Compares two float numbers if they are within a certain delta.

# File lib/hexapdf/content/graphic_object/endpoint_arc.rb, line 197
def float_equal(a, b)
  (a - b).abs < EPSILON
end