Plot 38
##############################################################
### Copyright (c) Richard Creamer 2019 - All Rights Reserved
### Inquiries: email 2to32minus1@gmail.com
##############################################################
### Globals
fontScale = 1.2Â Â # Global font size variable
lf    = 1.2  # Enlarge font of plot labels
verbose  = FALSE # Verbose printing
lw    = 2   # Line width
xMargin  = 1   # X-axis margin for plots
yMargin  = 1   # Y-axis margin for plots
de    = 0.01  # Small constant for estimating derivatives numerically
smlPts  = 1   # Small data point/circle
medPts  = 1.5  # Medium data point/circle
lrgPts  = 2.0  # Large data point/circle
### Run this function to generate this plot
runFunc = function() {
dw = 1000
dh = 750
path = "D:/Code/R/LeastSquaresPlay/PlotsAndAssocCode/Plot38/"
savePngVerbose( paste(path, "Plot38.png", sep=""), plotCubicWithDeriv, w=1.2*600, h=1.2*900, un="px" )
}
### Plot 5th order polynomial - hard coded example/function
plotCubicWithDeriv = function() {
x = c( 1, 5, 6, 10 )
y = c( 8, 3, 5, 1 )
clrs = list( cubicClr="blue", derivClr="red", vLineClr="chartreuse4", tanClr="tan" )
legText = c( "Cubic", "Cubic's Derivative", "Derivative Zeros", "Cubic Tangents at Derivative's Zeros" )
legClrs = c( clrs$cubicClr, clrs$derivClr, clrs$vLineClr, clrs$tanClr )
plotTitle = "Cubic Polynomial and its Derivative"
plot( x, y, main=plotTitle, axes = FALSE, asp=1, cex.lab=lf, xlab="", ylab="", cex=lrgPts, lwd=lw+1, cex.main=1.3*fontScale, type="n" )
addAxisLabels()
box(); grid()
axis( side = 1, at = 1:10, cex.axis=1.3 )
axis( side = 2, at = -3:11, las=2, cex.axis=1.3 )
lmFit = lm( y ~ x + I(x^2) + I(x^3) )
predX = seq( x[1] - 1, x[length(x)] + 1, length.out=400 )
predY = evalPoly( predX, lmFit$coefficients )
lines( predX, predY, col=clrs$cubicClr, lwd=lw+1 )
cubicCoeff = lmFit$coefficients
derivCoeff = getPolyDerivCoeff( cubicCoeff )
derivPredX = seq( 1, 10, length.out = 400 )
derivPredY = evalPoly( derivPredX, derivCoeff )
lines( derivPredX, derivPredY, col=clrs$derivClr, lwd=lw+1 )
derivRoots = qe( derivCoeff )
cubicZeroSlopeX = c( derivRoots )
cubicZeroSlopeY = evalPoly( cubicZeroSlopeX, cubicCoeff )
plotTangents( cubicZeroSlopeX, cubicCoeff, lineLength=0.7, tanOffset=0.3, lwd=lw+1, col=clrs$tanClr )
lines( c( gpl()$xLim[1], gpl()$xLim[2] ), c(0,0), col="black", lwd=lw+1 ) # horiz x-axis line
x1 = derivRoots[1]
x2 = derivRoots[2]
y1 = evalPoly( x1, cubicCoeff )
y2 = evalPoly( x2, cubicCoeff )
lines( c( x1, x1 ), c( y1, -3 ), type="l", lty=2, col=clrs$vLineClr, lwd=lw+1 )
lines( c( x2, x2 ), c( y2, -3 ), type="l", lty=2, col=clrs$vLineClr, lwd=lw+1 )
legend( x="topright", y="top", legend=legText, col=legClrs, lty=c(1,1,2,1), lwd=lw+1, cex=1.3*fontScale, inset = c(0.06,0.06) )
}
### Compute polynomial coefficients for derivative of argument polynomial
getPolyDerivCoeff = function( coeff ) {
if ( length(coeff) < 2 ) return( c(0) )
derivCoeff = c()
for ( i in 2:length( coeff ) ) {
termPower = i - 1
derivCoeff[ length( derivCoeff ) + 1 ] = termPower * coeff[i]
}
return( derivCoeff )
}
### Plot tangent(s) to polynomial at x[i], y[i]
### Will not look correct unless both x and y axes are same scale (1:1 aspect ratio)
plotTangents = function( x, coeff, lineLength=1, tanOffset=0.2, col="red", lwd=2 ) {
if ( length(coeff) < 2 ) return()Â
y = evalPoly( x, coeff )
polyDerivCoeff = getPolyDerivCoeff( coeff )
tanLineSlopes = evalPoly( x, polyDerivCoeff )
tanLineYInts = y - tanLineSlopes * x
for ( i in 1:length(x) ) {
lineCoeff = c( tanLineYInts[i], tanLineSlopes[i] )
plotTangentLine( x[i], y[i], lineCoeff, lineLength=lineLength, tanOffset=tanOffset, col=col, lwd=lwd )
}
}
### Get tangentl line ends point about center point (x,y)
getTanLineEndPts = function( x, y, lineCoeff, lineLength=1.0, tanOffset=0.2 ) {
x1 = x - 1
x2 = x + 1
y1 = evalPoly( x1, lineCoeff )
y2 = evalPoly( x2, lineCoeff )
xComp = x2 - x1
yComp = y2 - y1
vMag = sqrt( xComp^2 + yComp^2 )
vUnit = c( xComp/vMag, yComp/vMag )
vOffset = rotVecCcw( vUnit[1], vUnit[2], 90 )
ox = vOffset[1] * tanOffset
oy = vOffset[2] * tanOffset
xComp = xComp/vMag * lineLength
yComp = yComp/vMag * lineLength
fromX = x - xComp/2 - ox
fromY = y - yComp/2 - oy
toX = x + xComp/2 - ox
toY = y + yComp/2 - oy
return( list(fromX=fromX, fromY=fromY, toX=toX, toY=toY) )
}
### Rotate vector CCW
rotVecCcw = function( xComp, yComp, deg ) {
theta = deg/180 * pi
rotXComp = cos(theta) * xComp + sin(theta) * yComp;
rotYComp = -sin(theta) * xComp + cos(theta) * yComp;
return( c(rotXComp, rotYComp) )
}
### Plot one tangent line segment
plotTangentLine = function( x, y, lineCoeff, lineLength=1, tanOffset=0.2, col="red", lwd=1 ) {
endPts = getTanLineEndPts( x, y, lineCoeff, lineLength=lineLength, tanOffset=tanOffset )
lines( c(endPts$fromX, endPts$toX), c(endPts$fromY, endPts$toY), col=col, lwd=lwd )
}
### Naive implementation of quadratic equation solver: ax^2 + bx + c = 0
### Only special cases handled: 1) radicand < 0, and 2) only 1 root
qe = function( coeff ) {
C = coeff[1]
B = coeff[2]
A = coeff[3]
D = B^2 - 4*A*C
if ( D < 0 ) {
return( c() )
} else {
r1 = ( -B + sqrt(D) )/(2*A) # (A == 0) ? boom!
r2 = ( -B - sqrt(D) )/(2*A)
if ( r1 == r2 )
return( c(r1) )
return( c(r1, r2) )
}
}
### Compute f(x) for a polynomial and a vector of x-coordinates
evalPoly = function( x, coeff ) {
if ( length( coeff ) < 1 ) return( c(0) )Â
termSum = 0
for ( i in 1:length(coeff) ) {
termSum = termSum + coeff[i] * x^(i-1)
}
return( termSum )
}
### Convience method to add x/y axis labels
addAxisLabels = function( xLabel="x", yLabel="y", cexVal=1.3 ) {
mtext( text=xLabel, side=1, line=2.5, cex=cexVal )
mtext( text=yLabel, side=2, line=2.5, cex=cexVal )
}
### Get Plot Limits : gpl()$xLim[1] --> left x-coord
gpl = function() {
u = par( "usr" )
return( list( xLim=u[1:2], yLim=u[3:4] ) )
}
### Save to PNG file, specify width and height
savePngVerbose = function( path, plotFunc, w=512, h=512, un="px", doCopyright=TRUE, ... ) {
png( filename = path, type="cairo", units=un, width=w, height=h, pointsize=12, res=96 )
plotFunc( ... )
if ( doCopyright )
addCopyright()
dev.off()
}
### Add copyright notice to plot via text()
addCopyright = function() {
mtext( "Copyright \uA9 2019 Richard Creamer - All Rights Reserved", side=4, line=0, adj=0, cex=1.1 )
mtext( "Email: 2to32minus1@gmail.com", side=4, line=1, adj=0, cex=1.1 )
}
runFunc()