Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  main.gi   Sprache: unbekannt

 
############################################################################
##
#W  main.gi              JupyterViz Package                  Nathan Carter
##
##  Installation file for functions of the JupyterViz package.
##
#Y  Copyright (C) 2018 University of St. Andrews, North Haugh,
#Y                     St. Andrews, Fife KY16 9SS, Scotland
##


##  Set the three possible values of PlotDisplayMethod to constants.
BindGlobal( "PlotDisplayMethod_Jupyter", MakeImmutable( "PlotDisplayMethod_Jupyter" ) );
BindGlobal( "PlotDisplayMethod_JupyterSimple", MakeImmutable( "PlotDisplayMethod_JupyterSimple" ) );
BindGlobal( "PlotDisplayMethod_HTML", MakeImmutable( "PlotDisplayMethod_HTML" ) );


##  Detect whether the JupyterKernel package is available.
##  If it is, set our default display mode to using JupyterRenderable objects.
##  Otherwise, we'll use plain HTML.
if IsBoundGlobal( "JupyterRenderable" ) then
    PlotDisplayMethod := PlotDisplayMethod_Jupyter;
else
    PlotDisplayMethod := PlotDisplayMethod_HTML;
fi;


JUPVIZSetUpJupyterRenderable := function ()
    if IsBoundGlobal( "JupyterRenderable" )
       and not IsBoundGlobal( "JUPVIZFileContentsType" ) then
        BindGlobal( "JUPVIZFileContentsType",
            NewType( NewFamily( "JUPVIZFileContentsFamily" ),
                     JUPVIZIsFileContentsRep ) );
        InstallMethod( JUPVIZFileContents, "for a string", [ IsString ],
        function( content )
            return Objectify( ValueGlobal( "JUPVIZFileContentsType" ),
                              rec( content := content ) );
        end );
        InstallMethod( ValueGlobal( "JupyterRender" ),
            [ JUPVIZIsFileContents ],
            function ( fileContents )
                return Objectify( ValueGlobal( "JupyterRenderableType" ),
                    rec( data := rec( text\/plain := fileContents!.content ),
                        metadata := rec( text\/plain := "" ) ) );
            end );
    fi;
end;
JUPVIZSetUpJupyterRenderable();


InstallGlobalFunction( RunJavaScript, function ( script, returnHTML... )
    local html, filename, file;
    if PlotDisplayMethod = PlotDisplayMethod_HTML then
        html := Concatenation(
            "<html>\n",
            "  <body>\n",
            "    <div id='element'></div>\n",
            "  </body>\n",
            "  <script src='https://code.jquery.com/jquery-3.3.1.min.js'\n",
            "    integrity='sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8='\n",
            "    crossorigin='anonymous'>\n",
            "  </script>\n",
            "  <script src='https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js'>\n",
            "  </script>\n",
            "  <script language='javascript'>\n",
            "    var element = document.getElementById( 'element' );\n",
            "    ", script, "\n",
            "  </script>\n",
            "</html>"
        );
        if Length( returnHTML ) > 0 and returnHTML[1] = true then
            return html;
        fi;
        filename := Filename( DirectoryTemporary(), "visualization.html" );
        file := OutputTextFile( filename, false );
        SetPrintFormattingStatus( file, false );
        PrintTo( file, html );
        CloseStream( file );
        if ARCH_IS_MAC_OS_X() then
            Exec( "open ", filename );
        elif ARCH_IS_WINDOWS() then
            Exec( "start ", filename );
        elif ARCH_IS_UNIX() then
            Exec( "xdg-open ", filename );
        fi;
        return Concatenation( "Displaying result stored in ", filename, "." );
    else
        # Ensure that we have the global variables we need.
        JUPVIZSetUpJupyterRenderable();
        if ( not IsBoundGlobal( "JupyterRenderable" ) ) then
            Error( "The JupyterKernel package is required for this feature." );
        fi;
        # The output element in the notebook will be passed called "element" in
        # the script's environment, which we capture with the closure wrapper
        # below, so that any callbacks or asynchronous code can rely on its having
        # that name indefinitely.
        # We use ValueGlobal to suppress the warning that ensues if you directly
        # use JupyterRenderable as a global variable that is sometimes not
        # defined when this package is loaded.
        return ValueGlobal( "JupyterRenderable" )( rec(
            application\/javascript := Concatenation(
                # use newlines to prevent // comments from harming code
                "( function ( element ) {\n", script,
                "\n} )( element.get( 0 ) )"
            )
        ), rec(
            application\/javascript := ""
        ) );
    fi;
end );


InstallGlobalFunction( JUPVIZAbsoluteJavaScriptFilename,
function ( relativeFilename )
    if not EndsWith( relativeFilename, ".js" ) then
        relativeFilename := Concatenation( relativeFilename, ".js" );
    fi;
    return Filename( DirectoriesPackageLibrary(
        "jupyterviz", "lib/js" )[1], relativeFilename );
end );


BindGlobal( "JUPVIZLoadedJavaScriptCache", rec( ) );
InstallGlobalFunction( LoadJavaScriptFile, function ( filename )
    local absolute, result;
    if IsBound( JUPVIZLoadedJavaScriptCache.( filename ) ) then
        return JUPVIZLoadedJavaScriptCache.( filename );
    fi;
    absolute := JUPVIZAbsoluteJavaScriptFilename(
        Concatenation( filename, ".min" ) );
    if not IsExistingFile( absolute ) then
        absolute := JUPVIZAbsoluteJavaScriptFilename( filename );
    fi;
    if not IsExistingFile( absolute ) then
        return fail;
    fi;
    result := ReadAll( InputTextFile( absolute ) );
    JUPVIZLoadedJavaScriptCache.( filename ) := result;
    return result;
end );


InstallGlobalFunction( InstallVisualizationTool,
function ( toolName, script )
    local key;
    key := Concatenation( "viz-tool-", toolName );
    if IsBound( JUPVIZLoadedJavaScriptCache.( key ) ) then
        return false;
    fi;
    JUPVIZLoadedJavaScriptCache.( key ) := script;
    return true;
end );
InstallGlobalFunction( InstallVisualizationToolFromTemplate,
function ( toolName, functionBody, CDNURL... )
    local key, template, record;
    key := Concatenation( "viz-tool-", toolName );
    record := rec( toolName := toolName, functionBody := functionBody );
    if Length( CDNURL ) = 0 then
        template := "template-for-viz-tools-without-cdn";
    else
        template := "template-for-viz-tools-with-cdn";
        record.toolString := GapToJsonString( toolName );
        if EndsWith( CDNURL, ".js" ) then
            CDNURL := CDNURL{[1..Length(CDNURL)-3]};
        fi;
        record.CDNURL := GapToJsonString( CDNURL );
    fi;
    return InstallVisualizationTool( toolName,
        JUPVIZFillInJavaScriptTemplate( template, record ) );
end );


InstallGlobalFunction( JUPVIZFillInJavaScriptTemplate,
function ( filename, dictionary )
    local key, result;
    result := LoadJavaScriptFile( filename );
    if result = fail then
        return fail;
    fi;
    for key in RecNames( dictionary ) do
        result := ReplacedString( result,
            Concatenation( "$", key ),
            # to permit //-style comments, we must add \n:
            Concatenation( String( dictionary.( key ) ), "\n" )
        );
    od;
    return result;
end );


InstallGlobalFunction( JUPVIZRunJavaScriptFromTemplate,
function ( filename, dictionary, returnHTML... )
    return CallFuncList( RunJavaScript, Concatenation(
        [ JUPVIZFillInJavaScriptTemplate( filename, dictionary ) ],
        returnHTML ) );
end );


InstallGlobalFunction( JUPVIZRunJavaScriptUsingRunGAP,
function ( jsCode, returnHTML... )
    return CallFuncList( JUPVIZRunJavaScriptFromTemplate,
        Concatenation( [ "using-runGAP", rec( runThis := jsCode ) ],
            returnHTML ) );
end );


InstallGlobalFunction( JUPVIZRunJavaScriptUsingLibraries,
function ( libraries, jsCode, returnHTML... )
    local result, library, libName;
    if IsString( libraries ) then
        libraries := [ libraries ];
    fi;
    if PlotDisplayMethod = PlotDisplayMethod_Jupyter then
        result := jsCode;
        for library in Reversed( libraries ) do
            result := JUPVIZFillInJavaScriptTemplate( "using-library",
                rec( library := GapToJsonString( library ),
                     runThis := result ) );
        od;
        return CallFuncList( JUPVIZRunJavaScriptFromTemplate,
            Concatenation( [ "using-runGAP", rec( runThis := result ) ],
                returnHTML ) );
    else
        result := "if ( !window.JUPVIZLibs ) window.JUPVIZLibs = { };\n";
        for library in libraries do
            libName := GapToJsonString( library );
            result := Concatenation( result,
                "if ( !window.JUPVIZLibs[", libName, "] ) {\n",
                LoadJavaScriptFile( library ), "\n",
                "    window.JUPVIZLibs[", libName, "] = 'loaded';\n",
                "}\n"
            );
        od;
        return CallFuncList( RunJavaScript, Concatenation(
            [ Concatenation( result, jsCode ) ], returnHTML ) );
    fi;
end );


InstallGlobalFunction( CreateVisualization, function ( json, code... )
    local libraries, toolFile;
    libraries := [ "main" ];
    toolFile := Concatenation( "viz-tool-", json.tool );
    if IsExistingFile( JUPVIZAbsoluteJavaScriptFilename( toolFile ) )
    or IsBound( JUPVIZLoadedJavaScriptCache.( toolFile ) ) then
        Add( libraries, toolFile );
    fi;
    if Length( code ) = 0 then
        code := "";
    else
        code := code[1];
    fi;
    return JUPVIZRunJavaScriptUsingLibraries( libraries,
        JUPVIZFillInJavaScriptTemplate(
            "create-visualization",
            rec( data := GapToJsonString( json ), after := code ) ) );
end );


InstallGlobalFunction( JUPVIZMakePlotDataSeries, function ( args... )
    # Define a function that we will use to give any usage errors.
    local usageError, isReal;
    usageError := function ()
        Error( Concatenation(
            "The data for a plot can be given in the following ways: ",
            "1. a list of x values and a list of y values. ",
            "2. a list of x values and a function to apply to them. ",
            "3. a list of (x,y) pairs. ",
            "4. a list of y values (with indices used as x values). ",
            "5. a function (small integers will be used for x values)." ) );
    end;
    if Length( args ) = 0 or Length( args ) > 3 then
        usageError();
    fi;
    # We expect the final argument to be an options object.  If it's not
    # present, just fill in a default options object (an empty record) and
    # re-call.
    if not IsRecord( args[Length( args )] ) then
        Add( args, rec() );
        return CallFuncList( JUPVIZMakePlotDataSeries, args );
    fi;
    # Several times below we will want to check whether something is a
    # real number.  We make this function for that purpose.
    isReal := x -> IsInt( x ) or IsRealFloat( x ) or IsRat( x );
    # Now consider each case we support, as documented above in the usage
    # error function.
    #
    # 1. a list of x values and a list of y values
    # The xs can be any values but the ys must be numbers.
    #
    # This is the situation in which we can just easily store the data in
    # a record and be done.  It is already in the form we want it to be.
    if Length( args ) = 3 and IsList( args[2] )
                          and ForAll( args[2], isReal ) then
        return rec(
            x := args[1],
            y := args[2],
            options := args[3]
        );
    fi;
    #
    # 2. a list of x values and a function to apply to them.
    #
    # We just apply the function to the x values and recur.
    # This then becomes an instance of case 1.
    if Length( args ) = 3 and IsFunction( args[2] ) then
        return JUPVIZMakePlotDataSeries(
            args[1], List( args[1], args[2] ), args[3] );
    fi;
    #
    # 3. a list of (x,y) pairs.
    #
    # We just unzip them into x and y lists separately and recur.
    # This then becomes an instance of case 1.
    if Length( args ) = 2 and IsList( args[1] ) and
       ForAll( args[1], x -> IsList(x) and Length(x) = 2 ) then
        return JUPVIZMakePlotDataSeries(
            List( args[1], L -> L[1] ),
            List( args[1], L -> L[2] ),
            args[2]
        );
    fi;
    #
    # 4. a list of y values (with indices used as x values).
    #
    # It is easy in GAP to construct the list of indices into a given list.
    # This then becomes an instance of case 1.
    if Length( args ) = 2 and IsList( args[1] )
                          and ForAll( args[1], isReal ) then
        return JUPVIZMakePlotDataSeries(
            [ 1 .. Length( args[1] ) ], args[1], args[2] );
    fi;
    #
    # 5. a function (small integers will be used for x values).
    #
    # We simply fill in the first few small integers and recur, letting this
    # degenerate to case 2.
    if Length( args ) = 2 and IsFunction( args[1] ) then
        return JUPVIZMakePlotDataSeries( [1,2,3,4,5], args[1], args[2] );
    fi;
    #
    # If none of those things happened, it's a usage error.
    usageError();
end );


InstallGlobalFunction( JUPVIZRecordKeychainLookup,
function ( record, keychain, default )
    if not IsList( keychain ) then return fail; fi;
    if Length( keychain ) = 0 then return record; fi;
    if not IsRecord( record ) then return default; fi;
    if not IsBound( record.( keychain[1] ) ) then return default; fi;
    return JUPVIZRecordKeychainLookup( record.( keychain[1] ),
        keychain{[2..Length(keychain)]}, default );
end );


InstallGlobalFunction( JUPVIZRecordsKeychainLookup,
function ( records, keychain, default )
    local record, attempt;
    for record in records do
        attempt := JUPVIZRecordKeychainLookup( record, keychain, default );
        if not ( attempt = default ) then return attempt; fi;
    od;
    return default;
end );


InstallGlobalFunction( JUPVIZFetchWithDefault,
function ( record, others, chain, default, action )
    if IsString( chain ) then chain := [ chain ]; fi;
    action( JUPVIZRecordKeychainLookup( record, chain,
        JUPVIZRecordsKeychainLookup( others, chain, default ) ) );
end );


InstallGlobalFunction( JUPVIZFetchIfPresent,
function ( record, others, chain, action )
    local result;
    if IsString( chain ) then chain := [ chain ]; fi;
    result := JUPVIZRecordKeychainLookup( record, chain,
        JUPVIZRecordsKeychainLookup( others, chain, fail ) );
    if not ( result = fail ) then action( result ); fi;
end );


BindGlobal( "ConvertDataSeriesForTool", rec() );


ConvertDataSeriesForTool.plotly := function ( series )
    local result;
    result := rec( data := StructuralCopy( series ) );
    Perform( result.data, function ( s )
        JUPVIZFetchWithDefault( s, series, [ "options", "type" ], "line",
            function ( t ) s.type := t; end );
        JUPVIZFetchIfPresent( s.options, [], "name",
            function ( n ) s.name := n; end );
    end );
    JUPVIZFetchWithDefault( 0, series, [ "options", "type" ], "line",
        function ( t )
            if t = "pie" then
                Perform( result.data, function ( s )
                    s.labels := s.x;
                    s.values := s.y;
                    Unbind( s.x );
                    Unbind( s.y );
                end );
            fi;
        end );
    JUPVIZFetchWithDefault( 0, series, [ "options", "height" ], 400,
        function ( h ) result.layout := rec( height := h ); end );
    JUPVIZFetchIfPresent( 0, series, [ "options", "width" ],
        function ( w ) result.layout.width := w; end );
    JUPVIZFetchIfPresent( 0, series, [ "options", "title" ],
        function ( t ) result.layout.title := t; end );
    JUPVIZFetchIfPresent( 0, series, [ "options", "xaxis" ],
        function ( x ) result.layout.xaxis := rec( title := x ); end );
    JUPVIZFetchIfPresent( 0, series, [ "options", "yaxis" ],
        function ( y ) result.layout.yaxis := rec( title := y ); end );
    Perform( result.data, function ( s ) Unbind( s.options ); end );
    return result;
end;


ConvertDataSeriesForTool.chartjs := function ( series )
    local result;
    Perform( series, function ( s )
        if not ( s.x = series[1].x ) then
            Error( Concatenation(
                "Multiple arrays of x values ",
                "not supported yet in ChartJS." ) );
        fi;
    end );
    result := rec(
        data := rec(
            labels := StructuralCopy( series[1].x ),
            datasets := [ ]
        )
    );
    Perform( series, function ( s )
        local next;
        next := rec( data := s.y );
        JUPVIZFetchIfPresent( s, [], [ "options", "name" ],
            function ( n ) next.name := n; end );
        JUPVIZFetchIfPresent( next, [], [ "name" ],
            function ( n ) next.label := n; Unbind( next.name ); end );
        Add( result.data.datasets, next );
    end );
    JUPVIZFetchWithDefault( 0, series, [ "options", "type" ], "line",
        function ( t ) result.type := t; end );
    JUPVIZFetchIfPresent( 0, series, [ "options", "width" ],
        function ( w ) result.width := w; end );
    JUPVIZFetchIfPresent( 0, series, [ "options", "height" ],
        function ( h ) result.height := h; end );
    result.options := rec();
    JUPVIZFetchIfPresent( 0, series, [ "options", "xaxis" ],
        function ( x )
            result.options.scales := rec(
                xAxes := [
                    rec(
                        scaleLabel := rec(
                            labelString := x,
                            display := true
                        )
                    )
                ]
            );
        end );
    JUPVIZFetchIfPresent( 0, series, [ "options", "yaxis" ],
        function ( y )
            if not IsBound( result.options.scales ) then
                result.options.scales := rec();
            fi;
            result.options.scales.yAxes := [
                rec(
                    scaleLabel := rec(
                        labelString := y,
                        display := true
                    )
                )
            ];
        end );
    JUPVIZFetchIfPresent( 0, series, [ "options", "title" ],
        function ( t )
            result.options.title := rec(
                display := true,
                text := t
            );
        end );
    return result;
end;


ConvertDataSeriesForTool.canvasjs := function ( series )
    local result;
    result := rec( data := [ ] );
    Perform( series, function ( s )
        local next;
        next := rec(
            dataPoints := List(
                [ 1 .. Length( s.x ) ],
                i -> rec( x := s.x[i], y := s.y[i] )
            )
        );
        JUPVIZFetchWithDefault( s, series, [ "options", "type" ], "line",
            function ( t ) next.type := t; end );
        JUPVIZFetchIfPresent( s.options, [], [ "name" ],
            function ( n )
                next.legendText := n;
                next.showInLegend := true;
            end );
        Add( result.data, next );
    end );
    JUPVIZFetchWithDefault( 0, series, [ "options", "height" ], 400,
        function ( h ) result.height := h; end );
    JUPVIZFetchIfPresent( 0, series, [ "options", "width" ],
        function ( w ) result.width := w; end );
    JUPVIZFetchWithDefault( 0, series, [ "options", "animationEnabled" ],
        true, function ( e ) result.animationEnabled := e; end );
    JUPVIZFetchIfPresent( 0, series, [ "options", "theme" ],
        function ( t ) result.theme := t; end );
    JUPVIZFetchIfPresent( 0, series, [ "options", "xaxis" ],
        function ( x ) result.axisX := rec( title := x ); end );
    JUPVIZFetchIfPresent( 0, series, [ "options", "yaxis" ],
        function ( y ) result.axisY := rec( title := y ); end );
    JUPVIZFetchIfPresent( 0, series, [ "options", "title" ],
        function ( t ) result.title := rec( text := t ); end );
    return result;
end;


ConvertDataSeriesForTool.anychart := function ( series )
    local result, i, s;
    result := rec(
        chart := rec(
            series := [ ],
            legend := rec( position := "top" )
        )
    );
    Perform( series, function ( s )
        local next;
        next := rec(
            data := List(
                [ 1 .. Length( s.x ) ],
                i -> rec( x := s.x[i], value := s.y[i] )
            )
        );
        JUPVIZFetchWithDefault( s, series, [ "options", "type" ], "line",
            function ( t ) next.seriesType := t; end );
        JUPVIZFetchIfPresent( s, [], [ "options", "name" ],
            function ( n ) next.name := n; end );
        Add( result.chart.series, next );
    end );
    JUPVIZFetchWithDefault( 0, series, [ "options", "type" ], "line",
        function ( t ) result.chart.type := t; end );
    JUPVIZFetchIfPresent( 0, series, [ "options", "width" ],
        function ( w ) result.width := w; end );
    JUPVIZFetchIfPresent( 0, series, [ "options", "height" ],
        function ( h ) result.height := h; end );
    JUPVIZFetchIfPresent( 0, series, [ "options", "xaxis" ],
        function ( x )
            result.chart.xAxes := [
                rec(
                    orientation := "bottom",
                    title := rec( text := x )
                )
            ];
        end );
    JUPVIZFetchIfPresent( 0, series, [ "options", "yaxis" ],
        function ( y )
            result.chart.yAxes := [
                rec(
                    orientation := "left",
                    title := rec( text := y )
                )
            ];
        end );
    JUPVIZFetchIfPresent( 0, series, [ "options", "title" ],
        function ( t ) result.chart.title := t; end );
    return result;
end;


InstallGlobalFunction( JUPVIZPlotDataSeriesList, function ( series... )
    local result;
    JUPVIZFetchWithDefault( 0, series, [ "options", "tool" ], "plotly",
        function ( tool )
            if not IsBound( ConvertDataSeriesForTool.( tool ) ) then
                Error( Concatenation(
                    "Not a known plot visualization tool: ", tool ) );
            fi;
            result := CreateVisualization( rec(
                tool := tool,
                data := ConvertDataSeriesForTool.( tool )( series )
            ), "" );
        end );
    return result;
end );


InstallGlobalFunction( Plot, function ( args... )
    if Length( args ) = 0 then
        Error( "Plot requires at least one argument." );
    fi;
    # If the last argument is a record, we assume it's an options object,
    # and thus Plot was called with the same arguments as we should pass to
    # JUPVIZMakePlotDataSeries.  If the last argument is a function, we assume
    # it's to be applied to the first argument, and thus again, we pass the
    # arguments directly on to JUPVIZMakePlotDataSeries.
    if IsRecord( args[Length( args )] ) or
       IsFunction( args[Length( args )] ) then
        return JUPVIZPlotDataSeriesList(
            CallFuncList( JUPVIZMakePlotDataSeries, args ) );
    fi;
    # At this point, we know we have either been called as
    # Plot(xList,yList), as Plot(xyList), as Plot(yList), or
    # Plot(argset1,argset2,...).  In all cases, all args are lists.
    # Verify that and signal an error if it's not so.
    if not ForAll( args, x -> IsList( x ) ) then
        Error( "Invalid argument types given to Plot." );
    fi;
    # If there's just one argument and it's a list of pairs, we assume we
    # are in the Plot(xyList) case, which means just pass that directly to
    # JUPVIZMakePlotDataSeries.
    if Length( args ) = 1 and
       ForAll( args[1], x -> IsList( x ) and Length( x ) = 2 ) then
        return JUPVIZPlotDataSeriesList(
            CallFuncList( JUPVIZMakePlotDataSeries, args ) );
    fi;
    # If there's just one argument and it's a list of numbers, we assume we
    # are in the Plot(yList) case, which means just pass that directly to
    # JUPVIZMakePlotDataSeries.
    if Length( args ) = 1 and ForAll( args[1],
           x -> IsInt( x ) or IsRealFloat( x ) or IsRat( x ) ) then
        return JUPVIZPlotDataSeriesList(
            CallFuncList( JUPVIZMakePlotDataSeries, args ) );
    fi;
    # Before doing the comparison further below, we verify that args[1][1]
    # isn't an error.  If it is, they're passing empty lists, which is not
    # sensible for Plot().
    if Length( args[1] ) = 0 then
        Error( "Empty list passed as first argument to Plot." );
    fi;
    # To distinguish the Plot(xs,ys) case from the
    # Plot(argset1,argset2,...) case, we see if the first argument is a
    # list of lists.  If not, it's the first case, which we can pass
    # directly on to JUPVIZMakePlotDataSeries.
    if Length( args ) = 2 and
       ( not IsList( args[1][1] ) or IsString( args[1][1] ) ) and
       not IsFunction( args[1][1] ) then
        return JUPVIZPlotDataSeriesList(
            CallFuncList( JUPVIZMakePlotDataSeries, args ) );
    fi;
    # The only case remaining is the Plot(argset1,argset2,...) case, which
    # means we must iterate over all the arguments, call JUPVIZMakePlotDataSeries
    # on each, and pile them all into an array.
    return CallFuncList( JUPVIZPlotDataSeriesList,
        List( args, x -> CallFuncList( JUPVIZMakePlotDataSeries, x ) ) );
end );


BindGlobal( "ConvertGraphForTool", rec() );


ConvertGraphForTool.cytoscape := function ( graph )
    local result, edgestyle;
    result := rec(
        elements := [ ],
        layout := rec(),
        style := [
            rec(
                selector := "node",
                style := rec( content := "data(id)" )
            )
        ]
    );
    JUPVIZFetchWithDefault( graph, [], [ "options", "layout" ], "cose",
        function ( lname ) result.layout.name := lname; end );
    JUPVIZFetchIfPresent( graph, [], [ "options", "vertexwidth" ],
        function ( w ) result.style[1].style.width := w; end );
    JUPVIZFetchIfPresent( graph, [], [ "options", "vertexheight" ],
        function ( h ) result.style[1].style.height := h; end );
    JUPVIZFetchIfPresent( graph, [], [ "options", "vertexcolor" ],
        function ( c ) result.style[1].style.( "background-color" ) := c;
        end );
    edgestyle := rec();
    JUPVIZFetchIfPresent( graph, [], [ "options", "edgewidth" ],
        function ( w ) edgestyle.width := w; end );
    JUPVIZFetchIfPresent( graph, [], [ "options", "edgecolor" ],
        function ( c ) edgestyle.( "line-color" ) := c; end );
    JUPVIZFetchWithDefault( graph, [], [ "options", "directed" ], false,
        function ( directed )
            if directed then
                edgestyle.( "mid-target-arrow-shape" ) := "vee";
                JUPVIZFetchIfPresent( graph, [], [ "options", "edgecolor" ],
                    function ( c )
                        edgestyle.( "mid-target-arrow-color" ) := c;
                    end );
                JUPVIZFetchIfPresent( graph, [],
                    [ "options", "arrowscale" ],
                    function ( s ) edgestyle.( "arrow-scale" ) := s; end );
            fi;
        end );
    if Length( RecNames( edgestyle ) ) > 0 then
        Add( result.style, rec( selector := "edge", style := edgestyle ) );
    fi;
    Perform( graph.vertices, function ( v )
        local vertex;
        if IsRecord( v ) and IsBound( v.name ) and
           IsBound( v.x ) and IsBound( v.y ) then
            vertex := rec(
                data := rec( id := v.name ),
                position := rec( x := v.x, y := v.y )
            );
        else
            vertex := rec( data := rec( id := PrintString( v ) ) );
        fi;
        Add( result.elements, vertex );
    end );
    Perform( graph.edges, function ( e )
        Add( result.elements,
            rec(
                data := rec(
                    source := PrintString( e[1] ),
                    target := PrintString( e[2] )
                )
            )
        );
    end );
    return result;
end;


InstallGlobalFunction( JUPVIZMakePlotGraphRecord, function ( args... )
    local vertices, edges, i, j;
    # Ensure we were passed some arguments
    if Length( args ) = 0 then
        Error( "PlotGraph requires at least one argument." );
    fi;
    # Ensure there's an options object at the end
    if not IsRecord( args[Length( args )] ) then
        Add( args, rec() );
        return CallFuncList( JUPVIZMakePlotGraphRecord, args );
    fi;
    # If we're just given an adjacency matrix, convert that to vertices and
    # edges.  All positive entries in the matrix count as forming an edge.
    # Then recur using the vertex and edge sets.
    if Length( args ) = 2 and IsMatrix( args[1] ) and
       IsRectangularTable( args[1] ) and
       Length( args[1] ) = Length( args[1][1] ) then
        vertices := [ 1..Length( args[1] ) ];
        edges := [ ];
        for i in vertices do
            for j in vertices do
                if args[1][i,j] > 0 then Add( edges, [i,j] ); fi;
            od;
        od;
        return JUPVIZMakePlotGraphRecord( vertices, edges, args[2] );
    fi;
    # If we're just given edges, compute a vertex set from that, then recur
    # with both pieces of data.
    if Length( args ) = 2 and IsList( args[1] ) and Length( args[1] ) > 0
       and ForAll( args[1], x -> IsList( x ) and Length( x ) = 2 ) then
        vertices := [ ];
        Perform( args[1], function ( e )
            if not ( e[1] in vertices ) then Add( vertices, e[1] ); fi;
            if not ( e[2] in vertices ) then Add( vertices, e[2] ); fi;
        end );
        return JUPVIZMakePlotGraphRecord( vertices, args[1], args[2] );
    fi;
    # If we were given something other than three arguments, something is
    # wrong.
    if Length( args ) > 3 then
        Error( "Too many arguments given to PlotGraph." );
    fi;
    # If the second argument is a function, then use it to compute the set
    # of edges, and then recur with that data.
    if IsFunction( args[2] ) then
        edges := [ ];
        for i in args[1] do
            for j in args[1] do
                if args[2]( i, j ) then Add( edges, [i,j] ); fi;
            od;
        od;
        return JUPVIZMakePlotGraphRecord( args[1], edges, args[3] );
    fi;
    # The only case remaining should be the vertices and edges case. Verify.
    if not ForAll( args[2], x -> IsList( x ) and Length( x ) = 2 ) then
        Error( Concatenation(
            "Invalid edges list for PlotGraph.  ",
            "It must be a list of two-element lists." ) );
    fi;
    # We create a data structure of vertices, edges, and options.
    # Various conversion functions will make this suitable for each
    # visualization tool.
    return rec(
        vertices := args[1],
        edges := args[2],
        options := args[3]
    );
end );


InstallGlobalFunction( PlotGraph, function ( args... )
    local result, options, tool;
    options := args[Length( args )];
    if not IsRecord( options ) then options := rec(); fi;
    result := CallFuncList( JUPVIZMakePlotGraphRecord, args );
    JUPVIZFetchWithDefault( options, [], [ "tool" ], "cytoscape",
        function ( tool )
            if not IsBound( ConvertGraphForTool.( tool ) ) then
                Error( Concatenation(
                    "Not a known graph visualization tool: ", tool ) );
            fi;
            result := rec(
                tool := tool,
                data := ConvertGraphForTool.( tool )( result )
            );
            JUPVIZFetchWithDefault( options, [], [ "height" ], 400,
                function ( h ) result.height := h; end );
            JUPVIZFetchIfPresent( options, [], [ "width" ],
                function ( w ) result.width := w; end );
            result := CreateVisualization( result, "" );
        end );
    return result;
end );


#E  main.gi  . . . . . . . . . . . . . . . . . . . . . . . . . . . ends here

[ Dauer der Verarbeitung: 0.29 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge