cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
JMP is taking Discovery online, April 16 and 18. Register today and join us for interactive sessions featuring popular presentation topics, networking, and discussions with the experts.
Submit your abstract to the call for content for Discovery Summit Americas by April 23. Selected abstracts will be presented at Discovery Summit, Oct. 21- 24.
Craige_Hales
Super User
Braid

JSL to make the images for this 4K 60 FPS animation attached.

The twisted links were fairly straightforward. Now I'm thinking about braids. There's a group bunch of mathematicians that are into braids. I learned just enough to be dangerous. Here's a little JSL to draw a five-strand braid. I believe all three variations in the picture below are equivalent.

drawpattern = Function( {pattern, gb}, 
    channelToYPos = ["A" => 1, "B" => 2, "C" => 3, "D" => 4, "E" => 5]; // static
    channelToStrandID = ["A" => 1, "B" => 2, "C" => 3, "D" => 4, "E" => 5]; // braiding swaps these around
    strandIDToColor = Heat Color( (1 :: 5) / 6, "jet" );
    	
    erasecolor = gb[Framebox( 1 )] << getbackgroundcolor;
    	
    xleft = 1;
    xstep = 1 / N Items( pattern );
        
    circlesize = .20;
    pensize = 15;
        
    For( x = 1, x <= 10, x += 1,
        For Each( {row}, pattern,
            notfound = Associative Array( channelToYPos << getkeys );
            For Each( {swapchans}, row,
                channel_over = Substr( swapchans, 1, 1 );
                channel_under = Substr( swapchans, 2, 1 );
                // get Y positions of the two adjacent channels that will swap strands...
                channel_over_ypos = channelToYPos[channel_over];
                channel_under_ypos = channelToYPos[channel_under];
                // get the strand ids currently in those channels
                channel_over_StrandID = channelToStrandID[channel_over];
                channel_under_StrandID = channelToStrandID[channel_under];
                // get the colors of the strand ids
                channel_over_Color = strandIDToColor[channel_over_StrandID];
                channel_under_Color = strandIDToColor[channel_under_StrandID];
                // render this swap
                Pen Size( pensize );
                Pen Color( channel_under_Color );
                Line( {xleft, channel_under_ypos}, {xleft + xstep, channel_over_ypos} ); // draw under first
                Fill Color( channel_under_Color );
                Circle( {xleft, channel_under_ypos}, circlesize, "FILL" );
                Pen Size( pensize * 2 - 1 );// erase a wider line before drawing to make the
                Pen Color( erasecolor ); // overpass effect...
                Line( {xleft, channel_over_ypos}, {xleft + xstep, channel_under_ypos} ); // draw over last, erase wider
                Pen Size( pensize );
                Pen Color( channel_over_Color );
                Line( {xleft, channel_over_ypos}, {xleft + xstep, channel_under_ypos} ); // draw over last, within erased
                Fill Color( channel_over_Color );
                Circle( {xleft, channel_over_ypos}, circlesize, "FILL" );
                // swap
                channelToStrandID[channel_over] = channel_under_StrandID;
                channelToStrandID[channel_under] = channel_over_StrandID;
                notfound[channel_over] = 0;
                notfound[channel_under] = 0;
            );
            For Each( {chan}, notfound,
                ypos = channelToYPos[chan];
                strandID = channelToStrandID[chan];
                color = strandIDToColor[strandID];
                Pen Size( pensize );
                Pen Color( color );
                Line( {xleft, ypos}, {xleft + xstep, ypos} ); // draw uncrossed strand
                Fill Color( color );
                Circle( {xleft, ypos}, circlesize, "FILL" );
            );
            xleft += xstep;
        )
    );
    // endcaps, just for pretty
    For Each( {chan, ypos}, channelToYPos,
        strandID = channelToStrandID[chan];
        color = strandIDToColor[strandID];
        Fill Color( color );
        Circle( {xleft, ypos}, circlesize, "FILL" );
    );
);
    
New Window( "braidtest",
    V List Box(
        window:gb1 = Graph Box(
            X Scale( 0, 12 ),
            Y Scale( 0, 6 ),
            framesize( 1800, 300 ),
            xname( "" ),
            yname( "" ),
            suppressaxes,
            <<backgroundcolor( "black" ),
            drawpattern( {{"CB", "ED"}, {"CD", "AB"}}, window:gb1 );// twist:{"CD", "AB"}, {"DE", "BC"}
        ),
        window:gb2 = Graph Box(
            X Scale( 0, 12 ),
            Y Scale( 0, 6 ),
            framesize( 1800, 300 ),
            xname( "" ),
            yname( "" ),
            suppressaxes,
            <<backgroundcolor( "black" ),
            drawpattern( {{"CB"}, {"AB", "ED"},  {"CD"}}, window:gb2 );
        ),
        window:gb3 = Graph Box(
            X Scale( 0, 12 ),
            Y Scale( 0, 6 ),
            framesize( 1800, 300 ),
            xname( "" ),
            yname( "" ),
            suppressaxes,
            <<backgroundcolor( "black" ),
            drawpattern( { {"CB"}, {"ED"}, {"CD"}, {"AB"}}, window:gb3 );
        )
    )
);

The JSL is using channels A, B, C, D, E to refer to the straight horizontal rows and strands 1, 2, 3, 4, 5 to refer to the five colorful strings that dart back and forth between the channels, passing over/below another string. The three variations are defined by a pattern; the middle one is

drawpattern( {{"CB"}, {"AB", "ED"},  {"CD"}}, window:gb2 );

which means channels C and B swap strands with the strand from C passing over the strand from B. Then channel A passes above B simultaneously with channel E passing above D.  Finally, swap C and D, again with C above.

Colorful braids. The top one is smoothest.Colorful braids. The top one is smoothest.

 

If you run the attached code, change AntiAlias = 4 to AntiAlias = 1 to run it faster until you want the high quality images.

Last Modified: Aug 20, 2022 8:13 PM
Comments