Calculate Geometry NURBSTo row values from Points in Scratch

Started by RailwayKen, August 23, 2024, 02:35:17 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

RailwayKen

Background
I am designing a model railroad in Visio. A real railroad connects tangent track to circular track using various kinds of spirals-- to avoid theoretically infinite angular acceleration of the bogeys/trucks as the train enters a circular curve. Sane model railroaders will approximate this by tracing a track centerline using thin strip of wood bent between the tangent and the circular curve. (INSERT EDIT: sane model railroaders *who use easement curves* will use a thin strip of wood. Not suggesting that model railroaders who use sectional track are somehow less sane.)

Just because I can (thought I could anyhow), I am trying to use a Talbot (Euler) Spiral-- the common spiral used by the major railroads in North America. The formulas for x and y coordinates at each point on the spiral are defined as functions of the length of the spiral from the tangent-spiral point to the point in question.

I have working VBA code that takes the length of the spiral (Ls) and the radius of the circular curve (r) to generate an an array of points for DrawSpline. This works. However, I would like to be able to instead adjust a User.Ls and User.r cell on the shapesheet and have the spiral conform to the new definition.

Question
I have a list of X, Y coordinates for a spiral in the Scratch section of a ShapeSheet.
I am looking to use ShapeSheet formulas to generate the values for the Geometry NURBSTo row from this list of coordinates, including especially the NURBSTo() function in cell E.

I have done a lot of internet searches, including
https://learn.microsoft.com/en-us/office/client-developer/visio/nurbsto-row-geometry-section
and
https://learn.microsoft.com/en-us/office/client-developer/visio/nurbs-function
and haven't gotten very far.

I am now trying to "reverse engineer" the NURBSTo() formula in one of the spirals I have made with VBA code. I will keep plugging at that if that if necessary.

However, I would not turn down someone who might be able to explain or who has a better solution.

My backup plan is to use a CALLTHIS to run VBA code to draw a temporary shape matching the revised Ls, r values and then mine the cells from temp shape to adjust the original shape-- but that seems inelegant.

RailwayKen

...really I suppose my question is, "How does one calculate the values for knot1, knot2, ... knotLast?"

wapperdude

Interesting.  Curiousity.  What scale is your model railtoad?  I'm mostly familiar with O and HO. 

First question, how accurate do you need to model this curve?  (For the smaller scale, I think generally the circular was good enough.)

Second question, is the math such that the 2 end points plus your 2 parameters sufficient info?

I'm wondering if there's a way to make an approximation using elliptical fcns?  A concern is the overall number of points needed per curve and impact on file size.

Found this post:  https://visguy.com/vgforum/index.php?topic=942.0
Visio 2019 Pro

wapperdude

I did some research...the use of a Talbot came about as result of increased train speeds and reduced landscape.  Be that as it may, a reasonable approximation may be obtained using a series of arcs of decreasing radii. 

For example, O scale has fixed diameters, e.g. 27", 36", up to 96".  Using 1/16 of an arc, 22.5°, for each circular segment, a transition might be built using 4 segments:  72->60->48->36 to yield a 90° turn.  Setting up arc segments based upon given, standardised available curves might be a more expedient means to build such transitions.  Just a thought.
Visio 2019 Pro

RailwayKen

HO scale (for the uninitiated, this is defined as 3.5 mm = 1 foot). I've entered this scale into Visio and am drawing directly in scale feet.

An "easement" between the tangent and the circular portion of the curve is necessary to avoid alignment issues at the couplers as you enter the curve and to avoid the cars looking like they are lurching-- especially with the full scale modern North American equipment that I intend to run. But again, the sane model railroaders would not do what I am doing-- they would literally use a strip of wood to connect an offset circular curve with a tangent. This is plenty accurate enough for model railroad freight and passengers to stay on the rails!

However, I work in the industry and have access to the AREMA (American Railway Engineering and Maintenance of Way) formulas used to calculate the various values. So I am using actual spirals just for the joy of knowing that that is what they are...

RailwayKen

Yes, if you buy sectional track, you can buy circular curves sections. I am using flex track and may hand lay some track. If anyone is truly interested, John Armstrong's Track Planning for Realistic Operation speaks at length to the value of easements on model RR curves.

I have done a sensitivity analysis comparing the resulting NURBS from a DrawSpline in VBA if my x,y coordinate array has 5, 10, 100, 1000 points. At least for the particular Ls and r values I tried, the difference between 5 and 1000 points was insignificant. (Sure, if you plot enough points, you could merely connect them with line segments!)

The "all shapesheet" version of this has 10 x,y coordinate rows in the Scratch table.

RailwayKen

Finally, I had looked at the https://visguy.com/vgforum/index.php?topic=942.0 post as well.

I'll look at it again, but it did not get me across the finish line. I'll also resume trying to reverse engineer cell E in the NURBS geometry row. Thanks!


RailwayKen

Sorry, forgot to respond to something from @wapperdude
QuoteSecond question, is the math such that the 2 end points plus your 2 parameters sufficient info?

In the coordinate system of any given spiral shape, Ls (Length of Spiral), r (radius) and a TurnLeft/TurnRight Boolean are sufficient to calculate everything else. (From these, one calculates Degree of Curve, a constant equaling change-in-degree-of-curve-per-length; and a constant "s" which is Ls/(n-1), where n is the number of x,y coordinates I am calculating. Then x = f(s) and y = g(s).

wapperdude

Yes.  Of course you set curves as desired with flex track.  Hadn't actually thought about the coupler issue, that's an interesting point.

Would enjoy seeing updates showing progress, diagrams that you've committed to Visio.

Cheers.
Visio 2019 Pro

Nikolay



I would stop at the step 3, providing some UI (form or menu) for your VBA code so that user can specify the parameters.

Yacine

May be some 2 cheap cents.

I've tried to play in the past with nurbs. Terrible!

The two options I came up with:

1. very small sections defining a polyline. That is very convenient structure. The data are of course computed externally (VBA, or else). With some rounding the result was visually correct. (That's the equivalent to your CALLTHIS plan B)

2. Working with bezier curves in other graphics apps, you see this knot points and the handles for adjusting the tangent and its force. Whilst in Visio this function is also available in the GUI, it is not in the shapesheet.
solution: you set up a group of curve segments. By macro you link them ONCE by setting the end of one segment as begin of the next one. Also the tangent points are available.
This is a solution that - after macro preparation - relies only on shapesheets. It is still cumbersome to set up and you are of course limited to a certain number of sub-shapes (curve sections).
Yacine

RailwayKen

Quote from: Nikolay on August 24, 2024, 11:00:08 AMI would stop at the step 3, providing some UI (form or menu) for your VBA code so that user can specify the parameters.

And the fifth image would be a puff of smoke being sucked into a black hole... the remnants of the supernova that occurs when you try to do it using the ShapeSheet and your brain explodes.

I am at your step 3 already. This image shows a sample tangent, spiral, circular arc.
You cannot view this attachment.
And this image shows the same spiral, but with different colors and extends the circular arc to complete the circle so that it's easier to see what the curve does.
You cannot view this attachment.

Appreciate everyone's interest.


Yacine

Doctor, no one listens to me.  ;D

So I made a group. Gave it the props segment length, curvature and maxSegments.

And oh so clever: I put the angle calculation as ARG formula into the group, instead of sub-shapes. (https://www.visguy.com/2007/01/01/user-defined-shapesheet-functions-in-visio-2007/) This way you can modify your formula in one central place and tweak it to your needs.

In the group I put a line segment. It's length is constant and it's angle is calculated on the base of its index (prop.Nr). That's where this ARG and EVALCELL mentioned earlier come to value.
The length is a simple COS and SIN calculation.

The segment is visible or not based on its index (prop.Nr) compared to the prop.MaxSegments of the parent.,

You then open the group and duplicate the line as many times as you consider reasonable. You then run a macro to link them:

from visiopy import loaded_docs, vInit
vInit(new=True, globals_dict=globals())

parent = vWin.Selection(1)
lines = {}
i = 0
for shp in parent.Shapes:
    shp.Cells('prop.Nr').Formula = i
    shpNr = int(shp.Cells('prop.Nr').ResultIU)
    lines[shpNr] = shp
    i += 1

for i in range(len(lines)):
    shp = lines[i+1]
    shp_previous = lines[i]
    previous_ID = shp_previous.ID
    print(shp, shp_previous)
    shp.Cells('BeginX').Formula = 'Sheet.' + str(previous_ID) + '!EndX'
    shp.Cells('BeginY').Formula = 'Sheet.' + str(previous_ID) + '!EndY'



CAVEAT: Abrupt changes in the values of the curvature and the segment length lead to the segments behaving "funny". They get off-place of their actual shapesheet values. Modifiying both the segment length and the curvature re-aligns them.

Long story short: No Nurbs, but still pure shapesheet , with a little help of code.


Unbenannt.PNG

Yacine

Yacine

Feel free to experiment with the files.
I recommend checking the shapesheets of the group and of one of the segments.
Yacine

RailwayKen

I have not programmed in Python (though my 13-year old son has), so I only looked at the Visio file. Curious the advantage vs. a group of line segments, rather than a single shape with the points as multiple rows in a Geometry section.

Below is a file that plots a user-configurable number of points and connects them with LineTo rows in the Geometry Section of a single shape. Shape Data (prop cells) allow the user to set radius and length of spiral.

DISCLAIMER: This file contains formulas derived from AREMA (American Railway Engineering and Maintenance-of-Way Association) formulas. My formulation herein is definitely NOT for use in designing a real railroad. Note the disclaimer and "fair use" rationale within the file.

You cannot view this attachment.

To make the Geometry and Scratch formulas, I wrote the below code:

Sub PopScratchSpiralFormulas(vsoShape As Visio.Shape)
    Dim i As Integer, j As Integer, k As Integer
    Dim PinX As String, PinY As String, w As String, h As String
   
    With vsoShape
        PinX = .Cells("PinX").Formula
        PinY = .Cells("PinY").Formula
       
        j = CInt(.Cells("User.IncrCount")) 'number of points to plot
        For i = 1 To j
            'Scratch
            k = i + 1 'Scratch row 1 is (maybe not permanently) a header row to lable what each thing is
            If Not (.CellExists("Scratch.X" & k, False)) _
            Then .AddRow visSectionScratch, k, visTagDefault
           
            .Cells("Scratch.X" & k).Formula = "=0 ft+(100*Scratch.C" & k & "-(762/1000000)*(User.k^2)*(Scratch.C" & k & "^5))*12"
            .Cells("Scratch.Y" & k).Formula = "=0 ft+((291/1000)*User.k*(Scratch.C" & k & "^3)-(158/100000000)*(User.k^3)*(Scratch.C" & k & "^7))*12"
            .Cells("Scratch.A" & k).Formula = "=" & k - 2
            .Cells("Scratch.B" & k).Formula = "=User.Ls*(Scratch.A" & k & "/(User.IncrCount-1))"
            .Cells("Scratch.C" & k).Formula = "=Scratch.B" & k & "/100 ft"
            .Cells("Scratch.D" & k).Formula = "=PNT(Scratch.X" & k & ",Scratch.Y" & k & ")"
       
            'Geometry1
            If Not (.RowExists(visSectionFirstComponent, i, False)) Then
                .AddRow visSectionFirstComponent, i, visTagLineTo
            ElseIf i > 1 Then
                .RowType(visSectionFirstComponent, i) = visTagLineTo 'future change to visTagArcTo??
            End If
            .Cells("Geometry1.X" & i).Formula = "Scratch.X" & k
            .Cells("Geometry1.Y" & i).Formula = "Scratch.Y" & k

            'Debug.Assert i < 14
        Next i
       
        'remove any excess rows
        For i = .RowCount(visSectionScratch) To j + 1 Step -1
            k = i + 1
            .DeleteRow visSectionScratch, k
        Next i
        For i = .RowCount(visSectionFirstComponent) To j + 1 Step -1
            .DeleteRow visSectionFirstComponent, i
        Next i
       
        .Cells("PinX").Formula = PinX
        .Cells("PinY").Formula = PinY
        .Cells("Width").Formula = "=Geometry1.X" & j
        .Cells("Height").Formula = "=Geometry1.Y" & j
       
    End With

End Sub

The ShapeSheet has a DEPENDSON function linked to a CALLTHIS, with arguments being passed via the VBA code reads User.cells. (Could also have them read the prop.cells directly.)

For any one spiral in isolation, this works accurately enough. However, it has a key limitation. I am wanting to use the ANGLEALONGPATH(Geometry1.path, 1) + radius to plot the origin of the circular arc part of the curve. (To make a turn, a train will traverse tangent--entrance spiral--circular cure--exit spiral--tangent.)

The file illustrates that as the number of points plotted increases, the ANGLEALONGPATH approaches the correct instantaneous angle at "spiral to [circular] curve point" at the end of the spiral.


Browser ID: smf (possibly_robot)
Templates: 4: index (default), Display (default), GenericControls (default), GenericControls (default).
Sub templates: 6: init, html_above, body_above, main, body_below, html_below.
Language files: 4: index+Modifications.english (default), Post.english (default), Editor.english (default), Drafts.english (default).
Style sheets: 4: index.css, attachments.css, jquery.sceditor.css, responsive.css.
Hooks called: 456 (show)
Files included: 34 - 1306KB. (show)
Memory used: 1299KB.
Tokens: post-login.
Cache hits: 15: 0.00136s for 26,593 bytes (show)
Cache misses: 5: (show)
Queries used: 17.

[Show Queries]