R/vg.R

Defines functions vg.default vg.NULL vg.list vg

Documented in vg vg.default vg.list vg.NULL

#' Animated graph viewer
#' 
#' \code{vg} creates and starts an animated graphical user interface for 
#' positioning the vertices of a graph in 2 dimensions.
#'
#'
#' @author Alun Thomas
#' 
#'
#' @param grob is a graphical object state saved as a \code{list}
#' from a previous run 
#' of \code{vg} using the \code{save()} function.
#' If \code{grob} is \code{NULL} or not a \code{list},
#' \code{vg} starts with an empty graph.
#'
#' @param directed indicates whether or not the graph is directed. By default
#' \code{directed = FALSE}.
#' If the graph is directed, the edges have arrows indicating direction, and 
#' there are three slide bars to control the repulsion parameters. For an 
#' undirected graph there is a single control.
#'
#' @param running indicates whether or not the viewer is started 
#' with the animation running. By default \code{running = TRUE}.
#' 
#' 
#' @details
#' Creates and starts a 'Java' graphical user interface
#' (GUI) showing a real time animation of a 
#' Newton-Raphson optimization of a force function specified 
#' between the vertices of an arbitrary graph.
#' There are attractive forces between 
#' adjacent vertices and repulsive forces between all vertices.
#' The repulsions go smoothly to zero in a finite distance between 
#' vertices so that, unlike some other methods, different 
#' components don't send each other off to infinity.
#' 
#' The program is controlled by a slide bar, some buttons, the arrow, home and
#' shift keys, but mostly by mouse operations. All three mouse buttons are used.
#' These operations are described below.
#'
#' \code{vg} will replace \code{rViewGraph}, although the latter is
#' still currently available.
#' \code{vg} allows for far more control of the graph than \code{rViewGraph}, 
#' including adding and removing vertices and edges, and changing 
#' the appearance of the vertices,
#' all of which can be done while the animation is running.
#' It has a different set of force parameter controls that are useful for 
#' directed acyclic graphs (DAGs) specifically, but also, 
#' to a lesser extent, for arbitrary directed graphs. 
#' It also provides functions for saving and restoring a graphical state
#' including vertices and edges, vertex positions, and vertex appearances.
#' 
#' \code{vg} is intended primarily for interactive use. 
#' When used in a non-interactive environment
#' it will run without a visible GUI, however, a graph structure can be
#' created and saved and viewed in future, interactive, R sessions.
#'
#' @return
#' \code{vg} returns a list of functions that 
#' specify and query the graph, coordinates and appearance, and control the viewer.
#'
#' \item{add(i)}{If the graph does not contain vertices indexed by \code{i},
#' they are added to it. \code{i} may be any integer or integer vector.}
#'
#' \item{remove(i)}{If the graph contains vertices indexed by \code{i}, 
#' they are disconnected from
#' their neighbours and removed from the graph. \code{i} may be 
#' any integer or integer vector.}
#'
#' \item{connect(i,j)}{If the graph does not have edges between vertices 
#' indexed by \code{i}
#' and \code{j}, they are made. 
#' If \code{i} and \code{j} are of different lengths, only the 
#' first \code{min(length(i),length(j))} values are used.
#' If the relevant vertices are are not already in the graph,
#' they are first made and added.
#' \code{i} and \code{j} 
#' may be any integers or integer vectors.}
#'
#' \item{disconnect(i,j)}{If the graph has edges between vertices 
#' indexed by \code{i} and 
#' \code{j} they are removed. If \code{i} and \code{j} are of 
#' different lengths, only the 
#' first \code{min(length(i),length(j))} values are used.
#' The vertices themselves are not removed, even if they have no
#' other neighbours. \code{i} and \code{j} may be any integers 
#' or integer vectors.}
#'
#' \item{clear()}{All vertices and edges are removed from the graph, 
#' however, any customizations
#' made to the appearance of the vertices will persist 
#' until \code{clearMap()} is called.}
#'
#'
#' \item{isDirected()}{Returns \code{TRUE} if the graph's edges are directed,
#' \code{FALSE} otherwise.}
#'
#' \item{getIds()}{Returns a vector of all the vertex 
#' indices that the viewer has 
#' encountered, whether or not they are currently in the graph.}
#'
#' \item{contains(id)}{Returns a vector of booleans indicating whether the
#' vertices corresponding to the given indices are currently in the graph.}
#'
#' \item{connects(i,j)}{Returns a vector of booleans indicating whether the
#' vertices with the indices in vector \code{i} are 
#' connected to the corresponding
#' vertices in \code{j}. If \code{i} and \code{j} are of 
#' different lengths, only 
#' first \code{min(length(i),length(j))} values are queried.}
#'
#' \item{neighbours(i)}{Returns a list of vectors containing
#' the neighbours of the vertices indexed by \code{i}. If the graph is
#' directed, both in and out neighbours are included. If a vertex is
#' unconnected, an empty vector is returned. If an index is for a vertex
#' that is not currently in the graph, \code{NULL} is returned.}
#'
#' \item{neighbors(i)}{Alias for \code{neighbours(i)}.}
#'
#' \item{inNeighbours(i)}{Returns a list of vectors containing the
#' indices of vertices with edges from them to the ones in \code{i}.
#' If the graph is undirected, this is the same as \code{neighbours(i)}.}
#' 
#' \item{inNeighbors(i)}{Alias for \code{inNeighbours(i)}.}
#'
#' \item{outNeighbours(i)}{Returns a list of vectors containing the
#' indices of vertices with edges to them from the ones in \code{i}.
#' If the graph is undirected, this is the same as \code{neighbours(i)}.}
#' 
#' \item{outNeighbors(i)}{Alias for \code{outNeighbours(i)}.}
#'
#'
#' \item{getVertices()}{Returns a vector of the indices of 
#' all vertices currently 
#' in the graph.}
#'
#' \item{getEdges()}{Returns a matrix with 2 columns and a row for each edge
#' in the current graph. Each row gives the indices of the vertices that the
#' edge connects. If the edges are directed, 
#' the connections are oriented from vertices
#' in the first column to those in the second.}
#'
#'
#' \item{getX(id)}{Returns the current horizontal 
#' coordinates of the vertices with
#' indices in \code{id}. Returns 0 if an index has not previously been seen.}
#'
#' \item{getY(id)}{Returns the current vertical coordinates of the vertices with
#' indices in \code{id}. Returns 0 if an index has not previously been seen.}
#'
#' \item{setXY(id, x, y)}{Sets the horizontal and vertical coordinates for the
#' vertices with indices in \code{id}. If \code{x} or \code{y} are not as long
#' as \code{id} their values are repeated cyclically to get vectors of the
#' right length.}
#'
#'
#' \item{label(i,lab=i)}{Sets the strings shown on the 
#' vertices indexed by \code{i} to those specified
#' by \code{lab}. If \code{lab} is not as long as \code{i}, 
#' its values are repeated cyclically to
#' get a vector of the right length. 
#' An error will occur if \code{lab} can't be interpreted as 
#' a vector of strings.}
#'
#' \item{colour(i,col="yellow")}{Sets the colours of the 
#' vertices indexed by \code{i} to those specified
#' by \code{col}. If \code{col} is not as long as \code{i}, 
#' its values are repeated cyclically to 
#' get a vector of the right length. 
#' Colours can be specified in the usual \code{R} ways.}
#'
#' \item{color(i,col="yellow")}{Alias for \code{colours(i,col)}.}
#'
#' \item{shape(i,shp=0)}{Sets the shapes of the vertices 
#' indexed by \code{i} to those specified by 
#' \code{shp}. If \code{shp} is not as long as \code{i}, 
#' its values are repeated cyclically to
#' get a vector or the right length. Shapes are specified as integers:
#' \itemize{
#' \item 0 = rectangle
#' \item 1 = oval
#' \item 2 = diamond
#' \item any other values are taken as 0.
#' }}
#'
#' \item{size(i,width=-1,height=width)}{Sets the sizes of the 
#' vertices indexed by \code{i} to those specified by 
#' \code{width} and \code{height}. 
#' If, for a certain index, both \code{width} and \code{height} are
#' non-negative, the size of the vertex is fixed.
#' If either of \code{width} or \code{height} is negative, 
#' the size of the vertex
#' is chosen adaptively to fit the current label.
#' If \code{width} or \code{height} are not as long as \code{i},
#' their values are repeated cyclically to get vectors of the right length.
#' }
#'
#' \item{map(i,lab=i,col="yellow",shp=0,width=-1,height=width}{Combines 
#' \code{label()}, \code{colour()}, \code{shape()} and \code{size()} in a 
#' single index-to-appearance mapping function. 
#' These functions can be called before or
#' after the vertices have been added to the graph, 
#' and the representations will persist for
#' vertices removed from, and replaced in, the graph until 
#' until they are explicitly changed or \code{clearMap()} is called.}
#'
#' \item{clearMap()}{Resets all vertices to the default 
#' appearance of yellow rectangle labeled
#' with the index.}
#'
#'
#' \item{run()}{Starts the GUI running if it's not already doing so.}
#'
#' \item{stop()}{Stops the GUI running if it's running.}
#'
#' \item{isRunning()}{Returns \code{TRUE} if the animation is running, 
#' \code{FALSE} otherwise.}
#'
#' \item{show()}{Shows the GUI. If it was running 
#' when \code{hide()} was called, it starts running again.}
#'
#' \item{hide()}{Stops the GUI and hides it.}
#'
#' \item{showPaper(size = "letter", landscape = TRUE)}{Indicates, 
#' with a different colour, the portion of the
#' plane corresponding to a choice of paper for printing. 
#' \code{size} can be any of 
#' \code{letter}, \code{A4}, \code{A3}, \code{A2}, \code{A1}, 
#' \code{A0}, \code{C1}, or \code{C0}.
#' \code{landscape} can be either \code{TRUE} or \code{FALSE}, 
#' in which case portrait orientation
#' is used. The default is to show the portion of the plane 
#' that would be printed on
#' US letter in landscape orientation.}
#'
#' \item{hidePaper()}{By default the GUI indicates, 
#' with a different colour, the portion
#' of the plane that corresponds to the current 
#' choice of paper for printing. This function
#' removes that area.}
#'
#' \item{showAxes()}{Shows the axes if they are hidden.}
#'
#' \item{hideAxes()}{By default, axes are shown to indicate the origin. This
#' function hides them.}
#'
#' \item{ps()}{Starts a \code{Java} PostScript print job dialog box that can be
#' used send the current view of the graph to a printer or to write a 
#' PostScript file. The plot
#' produced should closely match what is indicated by \code{showPaper}.}
#'
#' \item{save()}{Returns a \code{list} specifying the current state of the
#' graph structure, vertex coordinates, and vertex appearance map.}
#'
#' \item{restore(grob)}{Restores a saved graph and map state.}
#'
#' 
#' @section Interactive mouse, key and slide bar controls:
#'
#' \itemize{
#' \item Slide bars at the bottom of the GUI control the 
#' repulsive force in the energy
#' equation used to set the coordinates.
#' If the graph is undirected, there is a single 'Repulsion' parameter, 
#' if directed, there
#' are 'X-Repulsion' and 'Y-Repulsion' parameters, 
#' and a 'Gravity' parameter that influences
#' how these are combined.
#'
#' \item Mouse operations without shift key and without control key pressed.
#' \enumerate{
#' \item Left mouse: Drags a vertex. Vertex is free on release. 
#' \item Middle mouse: Drags a vertex. Vertex is fixed at release position. 
#' \item Right mouse: Translates the view by the amount dragged. 
#' A bit like putting
#' your finger on a piece of paper and moving it. 
#' \item Double click with any mouse button in the background: 
#' Resets the vertices to new random positions. 
#' }
#'
#' \item Mouse operations with shift key but without control key pressed.
#' \enumerate{
#' \item Left mouse: Drags a vertex and the component it is in. 
#' Vertex and component free on release.
#' \item Middle mouse: Drags a vertex and the component it is in. 
#' Vertex and component are fixed at release positions.
#' \item Right mouse: Translates the positions of the vertices relative to 
#' the position of the canvas by the amount dragged. This is useful to center 
#' the picture on the canvas ready for outputting.
#' }
#'
#' \item Mouse operations without shift key but with control key pressed.
#' \enumerate{
#' \item Left mouse: Click on a vertex to un-hide any hidden neighbours.
#' \item Middle mouse: Click on a vertex to hide it.
#' \item Double click left mouse: Un-hides all hidden vertices.
#' \item Double click middle mouse: Hides all vertices.
#' }
#'
#' \item Mouse operations with shift key and with control key pressed.
#' \enumerate{
#' \item Left mouse: Click on a vertex to un-hide all vertices 
#' in the same component.
#' \item Middle mouse: Click on a vertex to hide it and the component it is in.
#' }
#'
#' \item Key functions without shift key pressed. 
#' Mouse has to be in the picture canvas.
#' \enumerate{
#' \item Up arrow: Increases the scale of viewing by 10\%.
#' \item Down arrow: Decreases the scale of viewing by 10\%.
#' \item Left arrow: Rotates the view by 15 degrees clockwise.
#' \item Right arrow: Rotates the view by 15 degrees anticlockwise.
#' \item Home key: Undoes all scalings and rotations and places the origin at
#' the top left corner of the canvas.
#' }
#'
#' \item Key functions with shift key pressed. 
#' Mouse has to be in the picture canvas.
#' \enumerate{
#' \item Up arrow: Increases the vertex positions by 10\% 
#' relative to the scale of the canvas.
#' \item Down arrow: Decreases the vertex positions by 10\% 
#' relative to the scale of the canvas.
#' \item Left arrow: Rotates the vertex positions by 15 
#' degrees clockwise relative to the canvas orientation.
#' \item Right arrow: Rotates the vertex positions by 15 
#' degrees anticlockwise relative to the canvas orientation.
#' }
#' }
#'
#' 
#'
#' @references 
#' A full description of the force function and algorithm used is given by
#' C Cannings and A Thomas, 
#' Inference, simulation and enumeration of genealogies.
#' In D J Balding, M Bishop, and C Cannings, editors, 
#' The Handbook of Statistical
#' Genetics. Third Edition, pages 781-805. John Wiley & Sons, Ltd, 2007.
#'
#'
#' @seealso rViewGraph
#' 
#' 
#' @examples
#' 
#' # Start the viewer.
#' # This will print a warning message if not run interactively.
#' require(rviewgraph)
#' v = vg()
#'
#' # Generate some random vertices to connect with edges.
#' from = sample(1:20,15,TRUE)
#' to = sample(1:20,15,TRUE)
#' 
#' # Connect these edges in the viewer.
#' v$connect(from,to)
#' 
#' # Negative vertex indices are also allowed.
#' v$connect(-from,-to)
#' 
#' # Add some new vertices, unconnected to any others.
#' v$add(30:35)
#' 
#' # Remove some vertices.
#' v$rem((-5:5)*2)
#'
#' # Query some of the structure of the graph.
#' v$contains(1)
#' v$contains(-1)
#' v$connects(1,-1)
#' v$contains(1:50)
#' v$connects(from,to)
#' v$connects(from,-to+2)
#' v$neighbours(from)
#' 
#' # Change what some of the vertices look like.
#' v$map(-10:10, lab = "", col=1, shp=1, width=1:15)
#' v$label(10:36, lab=LETTERS)
#' v$colour((-1000:-1),"cyan")
#' v$shape((-1000:-1)*2,2)
#'
#' # Hide the axes.
#' v$hideAxes()
#'
#' # Stop the animation. 
#' # Not necessary for outputting but sometimes helpful.
#' v$stop()
#'
#' # Change the paper size and orientation to A4 portrait.
#' v$showPaper("A4",FALSE)
#'
#' # Start the print dialog box.
#' #v$ps()
#'
#' # Restart the animation, and check that it's running.
#' v$run()
#' v$isRunning()
#'
#' # Save the application.
#' s = v$save()
#'
#' # Change the graph and appearance.
#' v$colour(-1000:1000,"red")
#' v$connect(rep(1,100),1:100)
#'
#' # Then decide you didn't like the changes so restore 
#' # the saved state.
#' v$restore(s)
#'
#' # Can also restore a state in a new GUI.
#' v2 = vg(s)
#'
#' # Get a vector of all the indices that the viewer has seen.
#' ids = v$getIds()
#'
#' # Get a vector of the indices of the vertices currently 
#' # in the graph.
#' verts = v$getVertices()
#'
#' # Get a matrix with 2 columns specifying the current edges 
#' # of the graph.
#' edges = v$getEdges()
#'
#' # Get the current coordinates of the specified vertices. 
#' x = v$getX(verts)
#' y = v$getY(verts)
#'
#' # Change the current coordinates of the vertices.
#' v$setXY(verts,2*x,0.5*y+2)
#'
#'
#' @keywords graph animation edges vertices 
#' @import rJava
#' @import grDevices
#' @export

vg <- function(grob, directed, running)
{
	UseMethod("vg")
}

#' @rdname vg
#' @method vg list
#' @export

vg.list <- function(grob, directed = grob$directed, running = grob$running)
{
	if (!is.logical(directed))
	{
		warning("Argument 'directed' must be logical. Setting to 'FALSE'.")
		directed = FALSE
	}
	if (length(directed) > 1)
	{
		warning("Using only first element of argument 'directed'.")
		directed = directed[1]
	}

	if (!is.logical(running))
	{
		warning("Argument 'running' must be logical. Setting to 'TRUE'.")
		running = TRUE
	}
	if (length(running) > 1)
	{
		warning("Using only first element of argument 'running'.")
		running = running[1]
	}

	vgCore(grob,directed,running)
}

#' @rdname vg
#' @method vg NULL
#' @export

vg.NULL <- function(grob = NULL, directed = FALSE, running = TRUE)
{
	vg.list(NULL,directed,running)
}

#' @rdname vg
#' @method vg default
#' @export

vg.default <- function(grob = NULL, directed = FALSE, running = TRUE)
{
	if (missing(grob))
	{
	}
	else if (is.logical(grob) && is.logical(directed))
	{
		running = directed
		directed = grob
#		message = paste("Argument 'grob' should be a 'list' or 'NULL'.",
#			"\n\tUsing: grob = NULL, directed = ",directed,", running = ",running,".",sep="")
#		warning(message)
	}
	else
	{
		warning("Argument 'grob' must be 'list' or 'NULL'. Setting to 'NULL'.")
	}

	vg.NULL(NULL,directed,running)
}

# Core graph viewing method.

vgCore <- function (saved, directed=FALSE, running=TRUE) 
{
	# Check that this is running in an interactive session.
	nogui = FALSE
	if (!interactive())
	{
		nogui = TRUE
		print("Running without a visible GUI.")
		
	}

	# Start Java if it is not already started.
	.jinit()

	# Check the Java version.
	jv <- .jcall("java/lang/System", "S", "getProperty", "java.runtime.version")

	if (substr(jv, 1L, 2L) == "1.") 
	{
  		jvn <- as.numeric(paste0(strsplit(jv, "[.]")[[1L]][1:2], collapse = "."))
  		if (jvn < 1.8) 
			stop("Java version >= 8 is needed for this package but not available.")
	}

	# Define the functions that run the GUI.

    	init = function() 
	{
		if (is.jnull(viewer)) 
		{
	    		viewer <<- .jnew("rviewgraph/Rvg", directed, running, nogui)
			showPaper("letter")
			showAxes()
		}
    	}

	my.jcall = function(...)
	{
		init()
		.jcall(...)
	}

	map = function(i, lab = i, col = "yellow", shp = 0, width = -1, height = width)
	{
		x = as.vector(i, mode = "integer")
		n = length(x)

		lab = as.vector(rep(lab,length.out=n), mode = "character")

		col = col2rgb(rep(col,length.out=n))
		r = as.vector(col["red",], mode = "integer")
		g = as.vector(col["green",], mode = "integer")
		b = as.vector(col["blue",], mode = "integer")
	
		shp = as.vector(rep(shp,length.out=n), mode = "integer")

		width = as.vector(rep(width,length.out=n), mode = "integer")
		height = as.vector(rep(height,length.out=n), mode = "integer")

		my.jcall(viewer,"V","map",.jarray(x),.jarray(lab),
			.jarray(r),.jarray(g),.jarray(b),.jarray(shp),.jarray(width),.jarray(height))
	}

	clearMap = function()
	{
		my.jcall(viewer, "V", "clearMap")
	}

	label = function(i, lab = i)
	{
		x = as.vector(i, mode = "integer")
		n = length(x)
		lab = as.vector(rep(lab,length.out=n), mode = "character")
		my.jcall(viewer,"V","name",.jarray(x),.jarray(lab))
	}

	colour = function(i, col = "yellow")
	{
		x = as.vector(i, mode = "integer")
		n = length(x)
		col = col2rgb(rep(col,length.out=n))
		r = as.vector(col["red",], mode = "integer")
		g = as.vector(col["green",], mode = "integer")
		b = as.vector(col["blue",], mode = "integer")
		my.jcall(viewer,"V","colour",.jarray(x),.jarray(r),.jarray(g),.jarray(b))
	}

	size = function(i, width = -1, height = width)
	{
		x = as.vector(i, mode = "integer")
		n = length(x)
		width = as.vector(rep(width,length.out=n), mode = "integer")
		height = as.vector(rep(height,length.out=n), mode = "integer")
		my.jcall(viewer,"V","size",.jarray(x),.jarray(width),.jarray(height))
	}

	shape = function(i, shp = 0)
	{
		x = as.vector(i, mode = "integer")
		n = length(x)
		shp = as.vector(rep(shp,length.out=n), mode = "integer")
		my.jcall(viewer,"V","shape",.jarray(x),.jarray(shp))
	}

	add = function(i)
	{
		x = as.vector(i, mode="integer")
		my.jcall(viewer,"V","add",.jarray(x))
	}

	contains = function(i)
	{
		x = as.vector(i, mode="integer")
		my.jcall(viewer,"[Z","contains",.jarray(x))
	}

	neighbours = function(i)
	{
		l = NULL
		for (x in as.vector(i, mode="integer"))
			l = append(l,list(my.jcall(viewer,"[I","neighbours",x)))
		l
	}

	inNeighbours = function(i)
	{
		l = NULL
		for (x in as.vector(i, mode="integer"))
			l = append(l,list(my.jcall(viewer,"[I","inNeighbours",x)))
		l
	}

	outNeighbours = function(i)
	{
		l = NULL
		for (x in as.vector(i, mode="integer"))
			l = append(l,list(my.jcall(viewer,"[I","outNeighbours",x)))
		l
	}

	rem = function(i)
	{
		x = as.vector(i, mode="integer")
		my.jcall(viewer,"V","remove",.jarray(x))
	}

	connect = function(i,j)
	{
		n = min(length(i),length(j))
		x = as.vector(i, mode="integer")[1:n]
		y = as.vector(j, mode="integer")[1:n]
		my.jcall(viewer,"V","connect",.jarray(x),.jarray(y))
	}

	connects = function(i,j)
	{
		n = min(length(i),length(j))
		x = as.vector(i, mode="integer")[1:n]
		y = as.vector(j, mode="integer")[1:n]
		my.jcall(viewer,"[Z","connects",.jarray(x),.jarray(y))
	}

	disconnect = function(i,j)
	{
		n = min(length(i),length(j))
		x = as.vector(i, mode="integer")[1:n]
		y = as.vector(j, mode="integer")[1:n]
		my.jcall(viewer,"V","disconnect",.jarray(x),.jarray(y))
	}

	clear = function()
	{
		my.jcall(viewer,"V","clear")
	}

	run = function(running = TRUE) 
	{
		if (running)
			my.jcall(viewer, "V", "run")
		else
			my.jcall(viewer, "V", "stop")
    	}
    
	stop = function() 
	{
		my.jcall(viewer, "V", "stop")
    	}

	show = function() 
	{
		my.jcall(viewer, "V", "show")
    	}

    	hide = function() 
	{
		my.jcall(viewer, "V", "hide")
    	}

	writePostScript = function()
	{
		error = my.jcall(viewer,"I","writePostScript")
		message = "Done."
		if (error == 1)
			message = "Cannot find Java Toolkit."
		if (error == 2)
			message = "PostScript job canceled."
		if (error == 3)
			message = "Null Java Graphics error."
		message
	}

	showPaper = function(size="letter", landscape=T)
	{
		i = 0

		if (size=="Letter" || size=="letter")
			i = 1
		if (size=="A4" || size=="a4")
			i = 2
		if (size=="A3" || size=="a3")
			i = 3
		if (size=="A2" || size=="a2")
			i = 4
		if (size=="A1" || size=="a1")
			i = 5
		if (size=="A0" || size=="a0")
			i = 6
		if (size=="C1" || size=="c1")
			i = 7
		if (size=="C0" || size=="c0")
			i = 8

		i = 2*i - 1 

		if (landscape)
			i = i+1
		
		my.jcall(viewer,"V","showPaper",as.integer(i))
	}

	hidePaper = function()
	{
		my.jcall(viewer,"V","showPaper",as.integer(0))
	}

	showAxes = function()
	{
		my.jcall(viewer,"V","showAxes")
	}

	hideAxes = function()
	{
		my.jcall(viewer,"V","hideAxes")
	}

	getIds = function()
	{
		my.jcall(viewer,"[I","getIndexes")
	}

	getVertices = function()
	{
		my.jcall(viewer,"[I","getVertices")
	}

	getEdges = function()
	{
		f = my.jcall(viewer,"[I","getFrom")
		t = my.jcall(viewer,"[I","getTo")
		cbind(f,t)
	}

	getX = function(id)
	{
		id = as.vector(id, mode="integer")
		my.jcall(viewer,"[D","getXCoords",.jarray(id))
	}

	getY = function(id)
	{
		id = as.vector(id, mode="integer")
		my.jcall(viewer,"[D","getYCoords",.jarray(id))
	}

	setXY = function(id, x, y)
	{
		id = as.vector(id, mode="integer")
		x = as.vector(rep(x,length.out=length(id)))
		y = as.vector(rep(y,length.out=length(id)))
		my.jcall(viewer,"V","setCoords",.jarray(id),.jarray(x),.jarray(y))
	}

	isDirected = function()
	{
		my.jcall(viewer,"Z","isDirected")
	}

	isRunning = function()
	{
		my.jcall(viewer,"Z","isRunning")
	}

	save = function()
	{
		id = my.jcall(viewer,"[I","getIndexes")
				
		list (
			running = my.jcall(viewer,"Z","isRunning"),
			directed = my.jcall(viewer,"Z","isDirected"),

			id = id,

			v = my.jcall(viewer,"[I","getVertices"),

			f = my.jcall(viewer,"[I","getFrom"),
			t = my.jcall(viewer,"[I","getTo"),

			l = my.jcall(viewer,"[Ljava/lang/String;","getNames",.jarray(id)),
			#l = my.jcall(viewer,"[S","getNames",.jarray(id)),

			r = my.jcall(viewer,"[I","getRed",.jarray(id)),
			g = my.jcall(viewer,"[I","getGreen",.jarray(id)),
			b = my.jcall(viewer,"[I","getBlue",.jarray(id)),

			s = my.jcall(viewer,"[I","getShape",.jarray(id)),

			w = my.jcall(viewer,"[I","getWidth",.jarray(id)),
			h = my.jcall(viewer,"[I","getHeight",.jarray(id)),

			x = my.jcall(viewer,"[D","getXCoords",.jarray(id)),
			y = my.jcall(viewer,"[D","getYCoords",.jarray(id)),

			NULL
		)
	}

	restore = function(g)
	{
		if (!is.null(g))
		{
			stop()
			clear()
			clearMap()
			add(g$v)
			connect(g$f,g$t)
			setXY(g$id,g$x,g$y)
			map(g$id,g$l,rgb(g$r/255,g$g/255,g$b/255),g$s,g$w,g$h)
			if (g$running)
				run()
		}
	}


	# Create the GUI and set the state.

	viewer = NULL
    	init()
	stop()
	restore(saved)
	run(running)

	# Return the GUI object.

    	list	(
		add = add,
		remove = rem,
		connect = connect,
		disconnect = disconnect,
		clear = clear,

		contains = contains,
		connects = connects,
		neighbours = neighbours, neighbors = neighbours,
		outNeighbours = outNeighbours, outNeighbors = outNeighbours,
		inNeighbours = inNeighbours, inNeighbors = inNeighbours,
		getIds = getIds,
		getVertices = getVertices,
		getEdges = getEdges,
		
		getX = getX,
		getY = getY,
		setXY = setXY,
		isDirected = isDirected,
		isRunning = isRunning,

		map = map,
		clearMap = clearMap,
		label = label,
		colour = colour, color = colour,
		size = size,
		shape = shape,

		run = run, 
		stop = stop, 
		show = show, 
		hide = hide,
		showPaper = showPaper,
		hidePaper = hidePaper,
		showAxes = showAxes,
		hideAxes = hideAxes,
		ps = writePostScript,

		save = save,
		restore = restore
	)
}

Try the rviewgraph package in your browser

Any scripts or data that you put into this service are public.

rviewgraph documentation built on May 31, 2023, 8:29 p.m.