Class: HexaPDF::Content::GraphicObject::Arc

Inherits:
Object
  • Object
show all
Includes:
Utils::MathHelpers
Defined in:
lib/hexapdf/content/graphic_object/arc.rb

Overview

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

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Utils::MathHelpers

deg_to_rad, rad_to_deg

Constructor Details

#initializeArc

Creates an elliptical arc with default values (a counterclockwise unit circle at the origin).



93
94
95
96
97
98
99
100
101
102
# File 'lib/hexapdf/content/graphic_object/arc.rb', line 93

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

Instance Attribute Details

#aObject (readonly)

Length of semi-major axis



74
75
76
# File 'lib/hexapdf/content/graphic_object/arc.rb', line 74

def a
  @a
end

#bObject (readonly)

Length of semi-minor axis



77
78
79
# File 'lib/hexapdf/content/graphic_object/arc.rb', line 77

def b
  @b
end

#clockwiseObject (readonly)

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



89
90
91
# File 'lib/hexapdf/content/graphic_object/arc.rb', line 89

def clockwise
  @clockwise
end

#cxObject (readonly)

x-coordinate of center point



68
69
70
# File 'lib/hexapdf/content/graphic_object/arc.rb', line 68

def cx
  @cx
end

#cyObject (readonly)

y-coordinate of center point



71
72
73
# File 'lib/hexapdf/content/graphic_object/arc.rb', line 71

def cy
  @cy
end

#end_angleObject (readonly)

End angle in degrees



83
84
85
# File 'lib/hexapdf/content/graphic_object/arc.rb', line 83

def end_angle
  @end_angle
end

#inclinationObject (readonly)

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



86
87
88
# File 'lib/hexapdf/content/graphic_object/arc.rb', line 86

def inclination
  @inclination
end

#max_curvesObject

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.



65
66
67
# File 'lib/hexapdf/content/graphic_object/arc.rb', line 65

def max_curves
  @max_curves
end

#start_angleObject (readonly)

Start angle in degrees



80
81
82
# File 'lib/hexapdf/content/graphic_object/arc.rb', line 80

def start_angle
  @start_angle
end

Class Method Details

.configure(**kwargs) ⇒ Object

Creates and configures a new elliptical arc object.

See #configure for the allowed keyword arguments.



56
57
58
# File 'lib/hexapdf/content/graphic_object/arc.rb', line 56

def self.configure(**kwargs)
  new.configure(**kwargs)
end

Instance Method Details

#configure(cx: nil, cy: nil, a: nil, b: nil, start_angle: nil, end_angle: nil, inclination: nil, clockwise: nil) ⇒ Object

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.



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/hexapdf/content/graphic_object/arc.rb', line 120

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

#curvesObject

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)



182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/hexapdf/content/graphic_object/arc.rb', line 182

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

#draw(canvas, move_to_start: true) ⇒ Object

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.



162
163
164
165
166
# File 'lib/hexapdf/content/graphic_object/arc.rb', line 162

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

#end_pointObject

Returns the end point of the elliptical arc.



143
144
145
# File 'lib/hexapdf/content/graphic_object/arc.rb', line 143

def end_point
  evaluate(@end_eta)
end

#point_at(angle) ⇒ Object

Returns the point at angle degrees on the ellipse.

Note that the point may not lie on the arc itself!



150
151
152
# File 'lib/hexapdf/content/graphic_object/arc.rb', line 150

def point_at(angle)
  evaluate(angle_to_param(angle))
end

#start_pointObject

Returns the start point of the elliptical arc.



138
139
140
# File 'lib/hexapdf/content/graphic_object/arc.rb', line 138

def start_point
  evaluate(@start_eta)
end