cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
Check out the JMP® Marketplace featured Capability Explorer add-in
Choose Language Hide Translation Bar
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:18 PM
Comments