Objective: Draw an ellipse centered at (X, Y) pts with eccentricity e
used to stretch a circle along the X-Axis using the existing brush
color, or (R,G,B) values if they are specified.
Although stretching out a circle is simpler than creating an ellipse
from scratch, it has a disadvantage that the width of the line also
stretches, making the boundary tenuous near the minor axis. That
might look O.K. for smaller ellipses or ones having only a slight
eccentricity (1 = perfectly round). I checked that the center of the
circle did not change when stretching by marking the center of the
original circle after the ellipse was created and the graphics
environment restored, but I didn't actually measure it on the printed
page, so it's possible that the origin moves when the circle is
stretched.
I worked some more on getting the PDF ellipses smoother, plus I'm
including the code I used to create the layout as input to the
PDFLayoutViewer (modified with a Landscape option):
https://files.oakland.edu/users/fortune/web/TrueEllipse2.pdf
Note that I specify the lengths of the major and minor axes instead of
using the eccentricity. It wouldn't be difficult to free the delta
angle from its current limitations.
'Code behind form
Dim strOut As String
Dim strFileOut As String
Dim strCR As String
strCR = Chr(13)
strFileOut = "C:\TrueEllipseLayout.txt"
'Mark the center of the ellipse with a crosshair
strOut = "0.3 w" & strCR
strOut = strOut & "195 500 m" & strCR
strOut = strOut & "205 500 l" & strCR
strOut = strOut & "200 495 m" & strCR
strOut = strOut & "200 505 l" & strCR
strOut = strOut & "S" & strCR
strOut = strOut & DrawTrueEllipse(200, 500, 30, 80, 50, 0, 0, 0.42353)
Open strFileOut For Output As #1
Print #1, strOut
Close
MsgBox ("Done.")
'End Code behind form
'Begin Module Code
Public Type Curve
DX As Double
DY As Double
L1 As Double
Alpha As Double
L4 As Double
Beta As Double
End Type
Public Type PiecewiseCurve
Curves(300) As Curve
End Type
Public Function DrawTrueEllipse(dblX As Double, dblY As Double, N As
Integer, dblA As Double, dblB As Double, Optional dblR As Double = -1,
Optional dblG As Double, Optional dblBlue As Double) As String
Dim strTemp As String
Dim strCR As String
Dim thePiecewiseCurve As PiecewiseCurve
Dim I As Integer
Dim DeltaTheta As Integer
Dim Theta As Integer
Dim dblCurX As Double
Dim dblCurY As Double
Dim X1 As Double
Dim X2 As Double
Dim Y1 As Double
Dim Y2 As Double
Dim NSeg As Integer
Dim boolSuccess As Boolean
Dim dblRefAngle As Double
Const DegToRad = 0.0174532925
'dblA = Half the major axis in pts.
'dblB = Half the minor axis in pts.
'For now use N's that go into 360 evenly
DeltaTheta = 360 / N
NSeg = 0
dblCurX = 0
dblCurY = 0
X1 = 0
Y1 = 0
X2 = 0
Y2 = 0
For Theta = 0 To (360 - DeltaTheta) Step DeltaTheta
NSeg = NSeg + 1
X1 = X2
Y1 = Y2
boolSuccess = GetXY(dblA, dblB, CDbl(Theta + DeltaTheta), dblCurX,
dblCurY)
X2 = dblCurX
Y2 = dblCurY
dblRefAngle = GetReferenceAngle(CDbl(Theta + DeltaTheta), dblCurX,
dblCurY, dblA, dblB)
thePiecewiseCurve.Curves(NSeg).Beta = GetBezierAngle(CDbl(Theta +
DeltaTheta), dblRefAngle, "Beta")
If NSeg = 1 Then 'No previous computation
boolSuccess = GetXY(dblA, dblB, CDbl(Theta), dblCurX, dblCurY)
X1 = dblCurX
Y1 = dblCurY
dblRefAngle = GetReferenceAngle(CDbl(Theta), dblCurX, dblCurY,
dblA, dblB)
thePiecewiseCurve.Curves(NSeg).Alpha = 90
Else
thePiecewiseCurve.Curves(NSeg).Alpha = thePiecewiseCurve.Curves
(NSeg - 1).Beta
'Use the X2, Y2 values from the previous loop for X1, Y1
End If
thePiecewiseCurve.Curves(NSeg).DX = X2 - X1
thePiecewiseCurve.Curves(NSeg).DY = Y2 - Y1
thePiecewiseCurve.Curves(NSeg).L1 = 10 * dblA * Sin(DeltaTheta *
DegToRad) / N
thePiecewiseCurve.Curves(NSeg).L4 = 10 * dblA * Sin(DeltaTheta *
DegToRad) / N
Next Theta
'Start at the right edge of the ellipse
strTemp = DrawPiecewiseCurve(dblX + dblA, dblY, 0.2,
thePiecewiseCurve, NSeg, dblR, dblG, dblBlue, False)
DrawTrueEllipse = strTemp
End Function
Public Function DrawPiecewiseCurve(dblX As Double, dblY As Double,
dblThickness As Double, thePiecewiseCurve As PiecewiseCurve, N As
Integer, Optional dblR As Double = -1, Optional dblG, Optional dblB,
Optional boolFill As Boolean = False) As String
Dim I As Integer
Dim J As Integer
Dim strTemp As String
Dim strCR As String
Dim X2 As String
Dim Y2 As String
Dim X3 As String
Dim Y3 As String
Dim X4 As String
Dim Y4 As String
Dim X As Double
Dim Y As Double
Dim dblSumX As Double
Dim dblSumY As Double
Const DegToRad = 0.0174532925
strCR = Chr(13)
strTemp = "%PiecewiseCurve" & strCR
strTemp = strTemp & "q" & strCR
strTemp = strTemp & CStr(dblThickness) & " w" & strCR
If dblR <> -1 Then
strTemp = strTemp & CStr(dblR) & " " & CStr(dblG) & " " & CStr(dblB)
& " RG" & strCR
End If
strTemp = strTemp & CStr(dblX) & " " & CStr(dblY) & " m" & strCR
X = dblX
Y = dblY
For I = 1 To N
With thePiecewiseCurve.Curves(I)
'Calculate coordinates of P2 and P3
X4 = CStr(X + .DX)
Y4 = CStr(Y + .DY)
X2 = CStr(X + .L1 * Cos(.Alpha * DegToRad))
Y2 = CStr(Y + .L1 * Sin(.Alpha * DegToRad))
X3 = CStr(X4 - .L4 * Cos(.Beta * DegToRad))
Y3 = CStr(Y4 - .L4 * Sin(.Beta * DegToRad))
X = X + .DX
Y = Y + .DY
strTemp = strTemp & X2 & " " & Y2 & " " & X3 & " " & Y3 & " " &
CStr(X4) & " " & CStr(Y4) & " c" & strCR
End With
Next I
If boolFill = True Then
If dblR <> -1 Then
strTemp = strTemp & CStr(dblR) & " " & CStr(dblG) & " " & CStr
(dblB) & " rg" & strCR
End If
strTemp = strTemp & "b" & strCR
End If
strTemp = strTemp & "S" & strCR
strTemp = strTemp & "Q" & strCR
DrawPiecewiseCurve = strTemp
End Function
'End Module Code
Once a reasonable number of arcs is chosen, the main technique for
smoothing out the ellipse is to adjust the parameters L1 and L4, which
correspond to the distance of the Bezier control points from their
respective origin. Lengthening or shortening those distances seem to
tighten or loosen the control point's "draw" on the Bezier Curve. Too
much of either is not good. You can end up with "corners" along your
ellipse or "loops" if the lengths become too short or too long. I
stopped when I got the ellipse substantially smooth, but could have
gotten it smoother with more effort. Besides, at 0.2 line width it's
pretty easy to catch major flaws. It turns out I needed to be able to
create filled ellipses anyway in order to make a vector version of the
company's new logo to put on PDF reports.
James A. Fortune
(e-mail address removed)
Disclaimer: Any programming examples shown are for illustration
purposes only, without warranty either expressed or implied. This
includes, but is not limited to, the implied warranties of
merchantability or fitness for a particular purpose. This post assumes
that you are familiar with the programming language that is being
demonstrated and with the tools that are used to create and to debug
procedures. I might explain the functionality of a particular
procedure, but I am under no obligation to modify these examples to
provide added functionality or to construct procedures to meet your
specific requirements. Any code samples posted contain no known
hidden material defects. However, anyone who uses any code sample
posted does so with the understanding that they are responsible for
any testing of any illustrative code sample for any particular use.
Furthermore, anyone using an illustrative code sample I provide or
code derived from it does so at their own risk.