# Moebius

    include<GHOUL/Config.scad>

    // Set the viewpoint and animation.
    $vpt=[0,0,0];
    // Animation is a simple viewpoint rotation, two Z-turns to see the entire surface of the Möbius band, one simultaneous Y-turn to keep the current 'Index' facing the viewer.
    $vpr=[0,0+$t*360,$t*720];
    $vpd=170;
    // Parse the rotation angles for display.
    vpr=[round($vpr.x%360),round($vpr.y%360),round($vpr.z%360)];

    // Set a few parameters.
    Width=6;
    Thickness=0.9;
    // Pad a 2D ellipse vertex array to 3D.
    Elly=PadArray(Ellipse(20,30,true,true),3,0);
    TotalVertices=len(Elly);
    Len=TotalVertices-1;

    // Möbius band makes 180 degree twist in one full turn.
    // Generate an array with JUST the offset vectors to width/2.
    // An offset vector, perpendicular to the curve and in the Z=0 plane is rotated over an angle proportional to the current vertex number over the array.
    Nelly=[ for(I=[0:Len])
            AffineTransformArray(
                RotationMatrix(
                // Rotation axis is vector between current vertex and the previous.
                MoebiusArray(Elly,I+1)-Elly[I],
                    // Angle is proportional to vertex number.
                    I/(Len+1)*180
                ),
                // The offset vector is perpendicular to the curve in Z=0 plane.
                [LPerpVector(MoebiusArray(Elly,I+1)-Elly[I],Width/2)]
            // The 'TransformArray' is only one vector...
            )[0]
    ];
    // As above, but now rotate a small vertical (i.e. perpendicular to the width-offset vector) vector representing the band's thickness.
    // Offset to thickness/2.
    Telly=[ for(I=[0:Len])
            AffineTransformArray(
                RotationMatrix(
                // Rotation axis vector and angle.
                MoebiusArray(Elly,I+1)-Elly[I],
                    I/Len*180
                ),
                // Offset vector.
                [[0,0,Thickness/2]]
            )[0]
    ];
    // Add +/- offsets for width and thickness to generate the 4 curves forming the 4 corners of the band cross-section.
    Jelly=[for(I=[0:Len]) Elly[I]+Nelly[I]-Telly[I]];
    Belly=[for(I=[0:Len]) Elly[I]-Nelly[I]-Telly[I]];
    Kelly=[for(I=[0:Len]) Elly[I]+Nelly[I]+Telly[I]];
    Helly=[for(I=[0:Len]) Elly[I]-Nelly[I]+Telly[I]];
    // I think I've ran out of five lettered names ending with 'elly'...
    // Concat all vertices.
    Vertices=concat(Jelly,Belly,Helly,Kelly);
    // Define bottom, side and top faces and stitch the ends together.
    Faces=[
        for(I=[0:Len-1]) // Bottom faces.
            [I,I+TotalVertices,I+TotalVertices+1,I+1],
        for(I=[0:Len-1]) // Near side faces.
            [I+TotalVertices,I+2*TotalVertices,I+2*TotalVertices+1,I+TotalVertices+1],
        for(I=[0:Len-1]) // Top faces.
            [I+2*TotalVertices,I+3*TotalVertices,I+3*TotalVertices+1,I+2*TotalVertices+1],
        for(I=[0:Len-1]) // Far side faces.
            [I+3*TotalVertices,I,I+1,I+3*TotalVertices+1],
            // The last four faces are not 'regular' as one end has made a 180 degree rotation. Stitch them together explicitly.
            [3*TotalVertices-1,4*TotalVertices-1,1*TotalVertices,0],
            [4*TotalVertices-1,1*TotalVertices-1,2*TotalVertices,TotalVertices],
            [1*TotalVertices-1,2*TotalVertices-1,3*TotalVertices,2*TotalVertices],
            [2*TotalVertices-1,3*TotalVertices-1,0,3*TotalVertices]
    ];

    // Generate the 'Indices' text.
    for(I=[0:41]){
        // Adjust spacing.
        PStep=7.56;
        // Find the nearest vertex on the original curve to place the text.
        Vertex=FindCurveVertex(Elly,I*PStep,true);
        // We need two points for a rotation vector.
        Node1=MoebiusArray(Elly,Vertex);
        Node2=MoebiusArray(Elly,Vertex+1);
        Vector=Node2-Node1;
        // The rotation is proportional to vertex number over array length, like above.
        Roll=Vertex/(Len+1)*180;

        // Give the 'currently viewer facing' Index some colour.
        color(((I)/42<$t&&(I+1)/42>$t)?"red":"gold")
        // Place the text.
        Position(Vector,Node1,Roll)
        // Align text with +Z-axis for Position(), above.
        rotate([90,0,-90])
        // Small correction to center text and raise it above the band surface.
        translate([-0.15,0,0.2])
        SimpleText(str("[",I,"]"),1.5,Thickness=0.5,Center=true,Font="Noto Mono",Spacing=0.8);
    }

    // Generate the Möbius band.
    color("darkslategrey")
    polyhedron(Vertices,Faces,5);

    // Visualise the coordinate system.
    CoordinateSystem(Size=10,Radius=0.25,Point=0.2,Polar=false);

    // Put some info in the picture.
    // Compensate for viewpoint rotation to keep text facing torward the user during animation.
    rotate($vpr)
    // Put it in the top-right corner.
    translate([26,30,0]){
        // Put the square behind the text.
        translate([0,0,-1])
        color("darkslategrey",0.7)
        square([22,5],center=true);
        // Show rotation angles.
        SimpleText(str(vpr),Size=2,Thickness=0.2,Center=true,Font="Noto Mono",Spacing=0.8);
    }