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 - www.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).



90
91
92
93
94
95
96
97
98
99
# File 'lib/hexapdf/content/graphic_object/arc.rb', line 90

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



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

def a
  @a
end

#bObject (readonly)

Length of semi-minor axis



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

def b
  @b
end

#clockwiseObject (readonly)

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



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

def clockwise
  @clockwise
end

#cxObject (readonly)

x-coordinate of center point



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

def cx
  @cx
end

#cyObject (readonly)

y-coordinate of center point



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

def cy
  @cy
end

#end_angleObject (readonly)

End angle in degrees



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

def end_angle
  @end_angle
end

#inclinationObject (readonly)

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



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

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.



62
63
64
# File 'lib/hexapdf/content/graphic_object/arc.rb', line 62

def max_curves
  @max_curves
end

#start_angleObject (readonly)

Start angle in degrees



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

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.



53
54
55
# File 'lib/hexapdf/content/graphic_object/arc.rb', line 53

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.



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

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)



179
180
181
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
# File 'lib/hexapdf/content/graphic_object/arc.rb', line 179

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.



159
160
161
162
163
# File 'lib/hexapdf/content/graphic_object/arc.rb', line 159

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 {|curve| canvas.curve_to(*curve)}
end

#end_pointObject

Returns the end point of the elliptical arc.



140
141
142
# File 'lib/hexapdf/content/graphic_object/arc.rb', line 140

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!



147
148
149
# File 'lib/hexapdf/content/graphic_object/arc.rb', line 147

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

#start_pointObject

Returns the start point of the elliptical arc.



135
136
137
# File 'lib/hexapdf/content/graphic_object/arc.rb', line 135

def start_point
  evaluate(@start_eta)
end