how to fix the coordinates of the lines? - vba

I have two lines - the first one is straight horizontal line with x1y1 as start point and x2y2 as end point. There is another line with start point as x1y1 and end point as x3y3.
Is there any way that I can fix the coordinate x1y1 of the lines so that if I rotate the second line the point x1y1 is not detached?
I tried grouping the lines but it didn't work.
Set p1 = ActiveSheet.Shapes.AddConnector(msoConnectorStraight, x1, y1, x2, y2)
p1.Select
Selection.ShapeRange.Line.BeginArrowheadStyle = msoArrowheadOval
Selection.ShapeRange.Line.EndArrowheadStyle = msoArrowheadOval
Set p2 = ActiveSheet.Shapes.AddConnector(msoConnectorStraight, x1, y1, x3, y3)
p2.Select
Selection.ShapeRange.Line.BeginArrowheadStyle = msoArrowheadOval
Selection.ShapeRange.Line.EndArrowheadStyle = msoArrowheadOval
Dim R As Variant
Set R = ActiveSheet.Shapes.Range(Array(p1.Name, p2.Name))
R.Group

Problem and code
If I understood correctly, you want to input an angle and to obtain the coordinates of the point (x3,y3) to redraw a line.
The solution can be done on the coordinates x3 and y3, since, as #SJR said "Rotation is around the midpoint of the line". So you need to use geometry to do it.
Using the Law of Sines code on Math.Stackexchange answered by Jean Marie, the following code can be done:
'Initial Values
x1 = 100
y1 = 100
x2 = 300
y2 = 100
DesiredAngle = 45
'Find coordinates
Angle1 = Application.WorksheetFunction.Radians(DesiredAngle)
Angle2 = Application.WorksheetFunction.Radians((180 - DesiredAngle) / 2)
Deltax = x2 - x1
Deltay = y2 - y1
a3 = Sqr(Deltax ^ 2 + Deltay ^ 2)
Angle3 = Application.WorksheetFunction.Pi() - Angle1 - Angle2
a2 = a3 * Sin(Angle2) / Sin(Angle3)
RHS1 = x1 * Deltax + y1 * Deltay + a2 * a3 * Cos(Angle1)
RHS2 = y2 * Deltax - x2 * Deltay + a2 * a3 * Sin(Angle1)
x3 = (1 / a3 ^ 2) * (Deltax * RHS1 - Deltay * RHS2)
y3 = (1 / a3 ^ 2) * (Deltay * RHS1 + Deltax * RHS2)
Debug.Print x3 & " " & y3
'Draw Lines
Set Line1 = ActiveSheet.Shapes.AddLine(x1, y1, x2, y2)
Set Line2 = ActiveSheet.Shapes.AddLine(x1, y1, x3, y3)
'Verify angle to know if it worked
'Method1 to obtain angle of 3 points
alpha = Application.WorksheetFunction.Atan2((y2 - y1), (x2 - x1))
beta = Application.WorksheetFunction.Atan2((y3 - y1), (x3 - x1))
Debug.Print Application.WorksheetFunction.Degrees(beta - alpha)
'Method2
m1 = (y2 - y1) / (x2 - x1)
m2 = (y3 - y1) / (x3 - x1)
Debug.Print Application.WorksheetFunction.Degrees(Atn((m1 - m2) / (1 + m1 * m2)))
'Check Length
Debug.Print Sqr((x3 - x1) ^ 2 + (y3 - y1) ^ 2)
On the code, the example is that the initial value is a line as you drew and after inputting the DesiredAngle, a line is drawn with this angle, with the new x3 and y3 coordinates.
Result
On the Result, the example uses a DesiredAngle of 45°.
Further References
You can refer to many questions about this on Math.Stackexchange, like this, this, this.
EDIT:
To test it, you can make a simple For loop and check that a circle is made, i.e., the circle radius is the same length:
'Initial Values
x1 = 500
y1 = 300
x2 = 700
y2 = 300
For i = 1 To 360
On Error Resume Next
DesiredAngle = i
'Find coordinates
Angle1 = Application.WorksheetFunction.Radians(DesiredAngle)
Angle2 = Application.WorksheetFunction.Radians((180 - DesiredAngle) / 2)
Deltax = x2 - x1
Deltay = y2 - y1
a3 = Sqr(Deltax ^ 2 + Deltay ^ 2)
Angle3 = Application.WorksheetFunction.Pi() - Angle1 - Angle2
a2 = a3 * Sin(Angle2) / Sin(Angle3)
RHS1 = x1 * Deltax + y1 * Deltay + a2 * a3 * Cos(Angle1)
RHS2 = y2 * Deltax - x2 * Deltay + a2 * a3 * Sin(Angle1)
x3 = (1 / a3 ^ 2) * (Deltax * RHS1 - Deltay * RHS2)
y3 = (1 / a3 ^ 2) * (Deltay * RHS1 + Deltax * RHS2)
Debug.Print x3 & " " & y3
'Draw Lines
Set Line1 = ActiveSheet.Shapes.AddLine(x1, y1, x2, y2)
Set Line2 = ActiveSheet.Shapes.AddLine(x1, y1, x3, y3)
Next i

Related

Visual basic function for intermittent antibiotic dosing

I am a beginner in VB. I wrote a little program to simulate dosing regimens of antibiotics using some exponential equations and pharmacokinetic data.
The problem I have is that I want to display on a graph the following mathematical function:
That simulates the concentration variation at different intervals of time:
Where:
b(t) is the concentration at time t that will be plotted as Y axis, t is time (plotted on the x-axis).
b(0) is the concentration at time 0 and it is a known variable.
u(t-a1) is a function that has the value u(t-a1)=b(o) if t=a1 or 0 if t<>a1
a1 is the time at which a next dose is given.
alpha is the elimination rate constant, a variable that is known.
What I have so far:
Dim y, x As Double
For x = 0 To 24 Step 1
For n As Double = 1 To 24 / tau
y = (1 - test_condition(n * tau, x)) * css * Math.Exp(-ke * x) + test_condition(n * tau, x) * css * Math.Exp(-ke * (x - n * tau))
Chart1.Series("Concentratie1").Points.AddXY(x, y)
Next
Next
The test_condition:
if x=tau then test_condition= 1 else 0
It is close but I don't get an exponential decay after a dose ... don't know how to make that happen.
This works!! for tau (dosing interval) every 4 hours. Can it be rearranged somehow because the tau (dosing interval) will vary (sometimes 4 hours, sometimes every 6 hours)?:
Dim y, x, y2, x2, y3, x3, y4, x4, x5, x6, y5, y6 As Double
For x = 0 To tau Step 1
y = exponential_decay(css, ke, x) + test_condition(tau, x) * (css - Val(mic))
Chart1.Series("Bolus 1").Points.AddXY(x, y)
Next
For x2 = tau To 2 * tau Step 1
y2 = exponential_decay(css, ke, x2 - tau) + test_condition(2 * tau, x2) * (css - Val(mic))
Chart1.Series("Bolus 2").Points.AddXY(x2, y2)
Next
For x3 = 2 * tau To 3 * tau Step 1
y3 = exponential_decay(css, ke, x3 - 2 * tau) + test_condition(3 * tau, x3) * (css - Val(mic))
Chart1.Series("Bolus 3").Points.AddXY(x3, y3)
Next
For x4 = 3 * tau To 4 * tau Step 1
y4 = exponential_decay(css, ke, x4 - 3 * tau) + test_condition(4 * tau, x4) * (css - Val(mic))
Chart1.Series("Bolus 4").Points.AddXY(x4, y4)
Next
For x5 = 4 * tau To 5 * tau Step 1
y5 = exponential_decay(css, ke, x5 - 4 * tau) + test_condition(5 * tau, x5) * (css - Val(mic))
Chart1.Series("Bolus 4").Points.AddXY(x5, y5)
Next
For x6 = 5 * tau To 32 Step 1
y6 = exponential_decay(css, ke, x6 - 5 * tau)
Chart1.Series("Bolus 4").Points.AddXY(x6, y6)
Next
End Sub
I managed to solve the problem:
this function f relates time (t) to dosing interval (tau)
Private Function f(ByVal t As Double, ByVal tau As Double)
Dim n As Integer
For n = 0 To 24 / tau
If t = n * tau Then
f = n * tau
ElseIf t < tau Then
f = 0
ElseIf t > n * tau And t < (n + 1) * tau Then
f = n * tau
ElseIf t >= (n + 1) * tau Then
f = n * tau
End If
Next
End Function
And this is what I draw on the chart:
For x = 0 To 36 Step 0.5
y = exponential_decay(css, ke, x - f(x, tau))
Chart1.Series("Intermitent Dosage").Points.AddXY(x, y)
Next

#VALUE error when trying to output value to a cell excel VBA

I have written a macro which computes x and y values. I am having trouble trying to write those values to cells on Excel.
I get #VALUE error when I try to display the values on the cell.
I have added my code below. Any suggestion about what is wrong with the code will be really helpful and appreciated?
Thanks in advance!
'Compute Points
Function ComputePoints(x1, y1, x2, y2, distance) As Double
'Calculate slope m
Dim m As Double
m = (y2 - y1) / (x2 - x1)
'Calculate intercept
Dim Intercept As Double
Intercept = y1 - m * x1
'Calculate x for distFinal
Dim message As String
Dim a As Double
Dim b As Double
Dim c As Double
Dim root1 As Double
Dim root2 As Double
Dim det As Double
Dim det1 As Double
Dim wb As Workbook
Dim ws As Worksheet
Dim x1Rng As Range
Dim x2Rng As Range
Dim yRng As Range
a = (m ^ 2 + 1)
b = 2 * (Intercept * m - m * y2 - x2)
c = x2 ^ 2 + (Intercept - y2) ^ 2 - distance ^ 2
det = ((b ^ 2) - (4 * a * c))
det1 = Sqr(det)
message = "There is no solution to your equation"
If det < 0 Then
MsgBox message, vbOKOnly, "Error"
Else
root1 = Round((-b + det1) / (2 * a), 2)
root2 = Round((-b - det1) / (2 * a), 2)
End If
'Compute y
Dim y As Double
y = m * root2 + Intercept
' Trying to set cell values to root1, root2, y
Set wb = ActiveWorkbook
Set ws = wb.Sheets("Sheet9")
Set x1Rng = ws.Range("N2")
Set x2Rng = ws.Range("O2")
Set yRng = ws.Range("P2")
x1Rng.Value2 = root1
x2Rng.Value2 = root2
yRng.Value2 = y
ComputePoints = y
End Function
I modified your code slightly to get values directly in excel cells. You need to select 3 horizontal cells, press F2 / =, enter your formula and then press Ctrl Shift Enter to make it an array formula.
This will give you the three output values in the cells.
Function ComputePoints(x1, y1, x2, y2, distance)
Dim results(3) As Variant ' #nightcrawler23
'Calculate slope m
Dim m As Double
m = (y2 - y1) / (x2 - x1)
'Calculate intercept
Dim Intercept As Double
Intercept = y1 - m * x1
'Calculate x for distFinal
Dim message As String
Dim a As Double
Dim b As Double
Dim c As Double
Dim root1 As Double
Dim root2 As Double
Dim det As Double
Dim det1 As Double
a = (m ^ 2 + 1)
b = 2 * (Intercept * m - m * y2 - x2)
c = x2 ^ 2 + (Intercept - y2) ^ 2 - distance ^ 2
det = ((b ^ 2) - (4 * a * c))
det1 = Sqr(det)
message = "There is no solution to your equation"
If det < 0 Then
MsgBox message, vbOKOnly, "Error"
Else
root1 = Round((-b + det1) / (2 * a), 2)
root2 = Round((-b - det1) / (2 * a), 2)
End If
'Compute y
Dim y As Double
y = m * root2 + Intercept
results(1) = root1 ' #nightcrawler23
results(2) = root2 ' #nightcrawler23
results(3) = y ' #nightcrawler23
ComputePoints = results ' #nightcrawler23
End Function
You need to add some code to output error message, when no roots are found

VB.NET spirograph program

I'm trying to create a a program that will draw hypotrochoids (spirograph). The program below compiles fine. But when I run it I only get a portion of the drawing.. I'm not sure what I'm doing wrong. I'm fairly new to VB.. Any help is appreciated. Thanks.
Here is the screenshot http://imgur.com/a/KxFWk
Public Class Form1
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
End Sub
Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint
Dim x As Integer
Dim y As Integer
Dim p As Integer
Dim x1 As Integer
Dim y1 As Integer
Dim x2 As Integer
Dim y2 As Integer
x = 75
y = 15
p = 15
x1 = (x + y) * Math.Cos(0) + p * Math.Cos(0)
y1 = (x + y) * Math.Sin(0) + p * Math.Sin(0)
For t = 0 To 500 Step 0.1
x2 = (x + y) * Math.Cos(t) + p * Math.Cos((x + y) * t / y)
y2 = (x + y) * Math.Sin(t) + p * Math.Sin((x + y) * t / y)
e.Graphics.DrawLine(Pens.Blue, x1, y1, x2, y2)
x1 = x2
y1 = y2
Next
End Sub
End Class
The results of the Sin and Cos calculations result in negative numbers where the parameter is greater than 90 for cos and greater than 180 for sin.
To see the whole image, you need to change the offset for x2 and y2 - see the code below. Alter the number 200 in each of the four lines to a value appropriate for your picturebox
x1 = 200 + CInt((x + y) * Math.Cos(0) + p * Math.Cos(0))
y1 = 200 + CInt((x + y) * Math.Sin(0) + p * Math.Sin(0))
For t As Double = 0 To 500 Step 0.1
x2 = 200 + CInt((x + y) * Math.Cos(t) + p * Math.Cos((x + y) * t / y))
y2 = 200 + CInt((x + y) * Math.Sin(t) + p * Math.Sin((x + y) * t / y))
e.Graphics.DrawLine(Pens.Blue, x1, y1, x2, y2)
x1 = x2
y1 = y2
Next

NLopt error in Julia and Ipopt alternative

I have the following simple problem that I want to solve with NLopt:
using JuMP
using NLopt
"""
min_x = x1 * x4* (x1 + x2 + x3) + x3
s.t.
x1 * x2 * x3 * x4 >= 25
x_1^2 + x_2^2 + x_3^2 + x_4^2 = 40
1 <= x1,x2,x3,x4 <= 5
starting values: vec(x) = (x1 = 1, x2 = 5, x3 = 5, x4 = 1)
"""
tic()
m = Model(solver=NLoptSolver(algorithm=:LD_MMA))
#defVar(m, 1 <= x1 <= 5)
#defVar(m, 1 <= x2 <= 5)
#defVar(m, 1 <= x3 <= 5)
#defVar(m, 1 <= x4 <= 5)
#setNLObjective(m, Min, x1 * x4 * (x1 + x2 + x3) + x3)
#addNLConstraint(m, x1^2 + x2^2 + x3^2 + x4^2 == 40)
#addNLConstraint(m, x1 * x2 * x3 * x4 >= 25)
setValue(x1, 1)
setValue(x2, 5)
setValue(x3, 5)
setValue(x4, 1)
status = solve(m)
println("got ", getObjectiveValue(m), " at ", [getValue(x1),getValue(x2), getValue(x3), getValue(x4)])
toc()
However I get an argument error. Is there any way to make this work with NLopt and if not how this code can change so as to use it with the other free optimizers that can be installed in Julia (maybe Ipopt but not Gurobi)?
Well, I was unable to solve the problem using NLopt, but instead I managed to solve it with Ipopt.
The solution is simple for using Ipopt. Firstly you have to download Ipopt (I used the Windows version for now and I will also try in Linux) from this site and put it in the path (if you put it in the path and go to the command line and type ipopt it must show no error-it will actually show ipopt options). Just go at the very end to find the newest version.
Then I sliglty modified the code that I provided before to account for Ipopt in this way:
using JuMP
using Ipopt
"""
The problem that I want to solve has 4 variables and 6 constraints.
It is the following:
min_x = x1x4(x1+x2+x3) + x3
s.t.
x1*x2*x3*x4 >= 25
x_1^2 + x_2^2 + x_3^2 + x_4^2 = 40
1 <= x1,x2,x3,x4 <= 5
starting values: x0 = (x1 = 1, x2 = 5, x3 = 5, x4 = 1)
"""
tic()
m = Model(solver=IpoptSolver())
#defVar(m, 1 <= x1 <= 5)
#defVar(m, 1 <= x2 <= 5)
#defVar(m, 1 <= x3 <= 5)
#defVar(m, 1 <= x4 <= 5)
#setNLObjective(m, Min, x1 * x4 * (x1 + x2 + x3) + x3)
#addNLConstraint(m, x1^2 + x2^2 + x3^2 + x4^2 == 40)
#addNLConstraint(m, x1 * x2 * x3 * x4 >= 25)
setValue(x1, 1)
setValue(x2, 5)
setValue(x3, 5)
setValue(x4, 1)
status = solve(m)
println("got ", getObjectiveValue(m), " at ", [getValue(x1),getValue(x2),
getValue(x3), getValue(x4)])
toc()
More information about the right name of the solvers etc. can be found here: https://jump.readthedocs.org/en/latest/installation.html#getting-solvers

Excel VBA compile error: Expected Sub, Function or Property

I am getting a compile error in Excel VBA which says Expected Sub, Function or Property. The function I am using is given below which is trying to copy the rate function in Excel.
Thanks for your help.
Function rate_m(nper As Double, pmt As Double, pv As Double, fv As Double, types As Double, guess As Double) As Variant
Dim y, y0, y1, x0, x1, f, i As Double
Dim FINANCIAL_MAX_ITERATIONS As Double
Dim FINANCIAL_PRECISION As Double
If IsNull(guess) Then guess = 0.01
If IsNull(fv) Then fv = 0
If IsNull(types) Then types = 0
FINANCIAL_MAX_ITERATIONS = 128 'Bet accuracy with 128
FINANCIAL_PRECISION = 0.0000001 '1.0e-8
y , y0, y1, x0, x1, f, i = 0
rate_m = guess
If Abs(rate_m) < FINANCIAL_PRECISION Then
y = pv * (1 + nper * rate_m) + pmt * (1 + rate_m * types) * nper + fv
Else
f = Exp(nper * Log(1 + rate_m))
y = pv * f + pmt * (1 / rate_m + types) * (f - 1) + fv
y0 = pv + pmt * nper + fv
y1 = pv * f + pmt * (1 / rate_m + types) * (f - 1) + fv
End If
'find root by Newton secant method
i , x0 = 0
x1 = rate_m
While Abs(y0 - y1) > FINANCIAL_PRECISION & i < FINANCIAL_MAX_ITERATIONS
rate_m = (y1 * x0 - y0 * x1) / (y1 - y0)
x0 = x1
x1 = rate_m
If Abs(rate_m) < FINANCIAL_PRECISION Then
y = pv * (1 + nper * rate_m) + pmt * (1 + rate_m * types) * nper + fv
Else
f = Exp(nper * Log(1 + rate_m))
y = pv * f + pmt * (1 / rate_m + types) * (f - 1) + fv
End If
y0 = y1
y1 = y
i = i + 1
Wend
End Function
A couple things...
First, you have to assign each variable individually...like this:
y = 0
y0 = 0
y1 = 0
x0 = 0
x1 = 0
f = 0
i = 0
Second, you probably want to DIM your variables all as Double. Unfortunately, this line:
Dim y, y0, y1, x0, x1, f, i As Double
Only declares i as a Double, all the others will be a Variant. You need to declare each one individually, like this:
Dim y As Double
Dim y0 As Double
Dim y1 As Double
Dim x0 As Double
Dim x1 As Double
Dim f As Double
Dim i As Double
Every IF ends with a End If (unless in a single line) and While...loop. You might want to take a look at VBA's syntax:
http://msdn.microsoft.com/en-us/library/office/ee814737(v=office.14).aspx
EDIT: You have to declare variable individually, instead of:
y , y0, y1, x0, x1, f, i = 0
you could do:
y = 0
y0 = 0
y1 = 0
x0 = 0
x1 = 0
f = 0
i = 0