class HexaPDF::Content::GraphicObject::Arc
This class describes an elliptical in center parameterization arc that is approximated using Bezier curves. It can be used to draw circles, circular arcs, ellipses and elliptical arcs, all either in clockwise or counterclockwise direction and optionally inclined in respect to the x-axis.
See: ELL - spaceroots.org/documents/ellipse/elliptical-arc.pdf
Attributes
Length of semi-major axis
Length of semi-minor axis
Direction of arc - if true
in clockwise direction, else in counterclockwise direction
x-coordinate of center point
y-coordinate of center point
End angle in degrees
Inclination in degrees of semi-major axis in respect to x-axis
The maximal number of curves used for approximating a complete ellipse.
The higher the value the better the approximation will be but it will also take longer to compute. The value should not be lower than 4. Default value is 6 which already provides a good approximation.
Start angle in degrees
Public Class Methods
Creates and configures a new elliptical arc object.
See configure
for the allowed keyword arguments.
# File lib/hexapdf/content/graphic_object/arc.rb, line 55 def self.configure(**kwargs) new.configure(**kwargs) end
Creates an elliptical arc with default values (a counterclockwise unit circle at the origin).
# File lib/hexapdf/content/graphic_object/arc.rb, line 92 def initialize @max_curves = 6 @cx = @cy = 0 @a = @b = 1 @start_angle = 0 @end_angle = 360 @inclination = 0 @clockwise = false calculate_cached_values end
Public Instance Methods
Configures the arc with
-
center point (
cx
,cy
), -
semi-major axis
a
, -
semi-minor axis
b
, -
start angle of
start_angle
degrees, -
end angle of
end_angle
degrees and -
an inclination in respect to the x-axis of
inclination
degrees.
The clockwise
argument 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/arc.rb, line 119 def configure(cx: nil, cy: nil, a: nil, b: nil, start_angle: nil, end_angle: nil, inclination: nil, clockwise: nil) @cx = cx if cx @cy = cy if cy @a = a.abs if a @b = b.abs if b if @a == 0 || @b == 0 raise HexaPDF::Error, "Semi-major and semi-minor axes must be greater than zero" end @start_angle = start_angle if start_angle @end_angle = end_angle if end_angle @inclination = inclination if inclination @clockwise = clockwise unless clockwise.nil? calculate_cached_values self end
Returns an array of arrays that contain the points for the Bezier curves which are used for approximating the elliptical arc between start_point
and end_point
.
One subarray consists of
[end_point_x, end_point_y, p1: control_point_1, p2: control_point_2]
The first start point is the one returned by start_point
, the other start points are the end points of the curve before.
The format of the subarray is chosen so that it can be fed to the Canvas#curve_to
method by using array splatting.
See: ELL s3.4.1 (especially the last box on page 18)
# File lib/hexapdf/content/graphic_object/arc.rb, line 181 def curves result = [] # Number of curves to use, maximal segment angle is 2*PI/max_curves n = [@max_curves, ((@end_eta - @start_eta).abs / (2 * Math::PI / @max_curves)).ceil].min d_eta = (@end_eta - @start_eta) / n alpha = Math.sin(d_eta) * (Math.sqrt(4 + 3 * Math.tan(d_eta / 2)**2) - 1) / 3 eta2 = @start_eta p2x, p2y = evaluate(eta2) p2x_prime, p2y_prime = derivative_evaluate(eta2) 1.upto(n) do p1x = p2x p1y = p2y p1x_prime = p2x_prime p1y_prime = p2y_prime eta2 += d_eta p2x, p2y = evaluate(eta2) p2x_prime, p2y_prime = derivative_evaluate(eta2) result << [p2x, p2y, {p1: [p1x + alpha * p1x_prime, p1y + alpha * p1y_prime], p2: [p2x - alpha * p2x_prime, p2y - alpha * p2y_prime]}] end result end
Draws the arc on the given Canvas
.
If the argument move_to_start
is true
, a Canvas#move_to
operation is executed to move the current point to the start point of the arc. Otherwise it is assumed that the current point already coincides with the start point
The max_curves
value is set to the value of the configuration option 'graphic_object.arc.max_curves' before drawing.
# File lib/hexapdf/content/graphic_object/arc.rb, line 161 def draw(canvas, move_to_start: true) @max_curves = canvas.context.document.config['graphic_object.arc.max_curves'] canvas.move_to(*start_point) if move_to_start curves.each {|x, y, hash| canvas.curve_to(x, y, **hash) } end
Returns the end point of the elliptical arc.
# File lib/hexapdf/content/graphic_object/arc.rb, line 142 def end_point evaluate(@end_eta) end
Returns the point at angle
degrees on the ellipse.
Note that the point may not lie on the arc itself!
# File lib/hexapdf/content/graphic_object/arc.rb, line 149 def point_at(angle) evaluate(angle_to_param(angle)) end
Returns the start point of the elliptical arc.
# File lib/hexapdf/content/graphic_object/arc.rb, line 137 def start_point evaluate(@start_eta) end
Private Instance Methods
Converts the angle
in degrees to the parameter used for the parametric function defining the ellipse.
The return value is between 0 and 2*PI.
# File lib/hexapdf/content/graphic_object/arc.rb, line 236 def angle_to_param(angle) angle = deg_to_rad(angle % 360) eta = Math.atan2(Math.sin(angle) / @b, Math.cos(angle) / @a) eta += 2 * Math::PI if eta < 0 eta end
Calculates the values that are derived from the input values and needed for the calculations
# File lib/hexapdf/content/graphic_object/arc.rb, line 215 def calculate_cached_values theta = deg_to_rad(@inclination) @cos_theta = Math.cos(theta) @sin_theta = Math.sin(theta) # (see ELL s2.2.1) Calculating start_eta and end_eta so that # start_eta < end_eta <= start_eta + 2*PI if counterclockwise # end_eta < start_eta <= end_eta + 2*PI if clockwise @start_eta = angle_to_param(@start_angle) @end_eta = angle_to_param(@end_angle) if !@clockwise && @end_eta <= @start_eta @end_eta += 2 * Math::PI elsif @clockwise && @end_eta >= @start_eta @start_eta += 2 * Math::PI end end
Returns an array containing the derivative of the parametric function defining the ellipse evaluated at eta
.
See: ELL s2.2.1 (4)
# File lib/hexapdf/content/graphic_object/arc.rb, line 258 def derivative_evaluate(eta) a_sin_eta = @a * Math.sin(eta) b_cos_eta = @b * Math.cos(eta) [- a_sin_eta * @cos_theta - b_cos_eta * @sin_theta, - a_sin_eta * @sin_theta + b_cos_eta * @cos_theta] end
Returns an array containing the x and y coordinates of the point on the elliptical arc specified by the parameter eta
.
See: ELL s2.2.1 (3)
# File lib/hexapdf/content/graphic_object/arc.rb, line 247 def evaluate(eta) a_cos_eta = @a * Math.cos(eta) b_sin_eta = @b * Math.sin(eta) [@cx + a_cos_eta * @cos_theta - b_sin_eta * @sin_theta, @cy + a_cos_eta * @sin_theta + b_sin_eta * @cos_theta] end