cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
The Discovery Summit 2025 Call for Content is open! Submit an abstract today to present at our premier analytics conference.
See how to use to use Text Explorer to glean valuable information from text data at April 25 webinar.
Choose Language Hide Translation Bar
View Original Published Thread

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.

Comments
Craige_Hales
Super User

Eleven strandsEleven strands