From b81b6b40b8bc92f0a0d7f6ada1fc48bd5254b2a0 Mon Sep 17 00:00:00 2001 From: James Hollway Date: Wed, 26 Aug 2026 17:57:28 +0200 Subject: [PATCH] Fixed "valence" layout --- DESCRIPTION | 2 +- NEWS.md | 14 ++++++++++++++ R/layout_valence.R | 22 ++++++++++++++++++---- cran-comments.md | 19 +++++++++++++++++++ tests/testthat/helper-tutorials.R | 19 +++++++++++++++++++ tests/testthat/test-layout_valence.R | 1 + 6 files changed, 72 insertions(+), 5 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index d38ccf14..0e18f8e6 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: autograph Title: Automatic Plotting and Theming of Many Graphs -Version: 1.2.0 +Version: 1.2.1 Description: Visual exploration and presentation of networks should not be difficult. This package includes functions for plotting networks and network-related metrics with sensible and pretty defaults. It includes 'ggplot2'-based plot methods for many popular network package classes. diff --git a/NEWS.md b/NEWS.md index 2b8a3f9b..fb357149 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,17 @@ +# autograph 1.2.1 + +## Layouts + +- Fixed "valence" layout returning a poor layout for some random starts + - The nodes now start on a circle, so one run resembles the next + - The repulsion force between two close nodes is now bounded + - Each step is now capped and cools over the iterations + +## Tests + +- Fixed the tutorial test failing on a deprecation warning that the tutorial code does not raise itself +- Fixed the "valence" layout test depending on the random seed + # autograph 1.2.0 ## Package diff --git a/R/layout_valence.R b/R/layout_valence.R index b06b7e24..ff0c5431 100644 --- a/R/layout_valence.R +++ b/R/layout_valence.R @@ -41,7 +41,13 @@ layout_valence <- function(.data, times = 500, center = NULL, circular = FALSE, rep(1, manynet::net_ties(graph)) weights[is.na(weights)] <- 1 - coords <- matrix(stats::runif(n * 2, min = -1, max = 1), ncol = 2) + # The nodes start on a circle rather than at random points, so that the + # layout is stable from one run to the next. A random start can place two + # nodes on top of each other, which the repulsion force then throws apart. + # A small jitter breaks the symmetry of the circle. + angle <- 2 * pi * seq_len(n) / n + coords <- cbind(cos(angle), sin(angle)) + + matrix(stats::runif(n * 2, min = -0.01, max = 0.01), ncol = 2) for (i in 1:times) { delta <- matrix(0, nrow = n, ncol = 2) @@ -52,7 +58,10 @@ layout_valence <- function(.data, times = 500, center = NULL, circular = FALSE, vec <- coords[k, ] - coords[j, ] dist <- sqrt(sum(vec^2)) + 1e-4 dir <- vec / dist - force <- repulsion_coef / dist^2 + # The distance is floored, since two nodes that start close together + # would otherwise repel each other with a force large enough to throw + # the whole layout apart, which it never recovers from. + force <- repulsion_coef / max(dist, 0.1)^2 delta[j, ] <- delta[j, ] - force * dir delta[k, ] <- delta[k, ] + force * dir @@ -75,8 +84,13 @@ layout_valence <- function(.data, times = 500, center = NULL, circular = FALSE, delta[t_id, ] <- delta[t_id, ] - force * dir } - # Position update with damping - coords <- coords + 0.1 * delta + # Position update with damping, a capped step, and cooling. + # The cap keeps one large force from moving a node further than the layout + # is wide, and the cooling lets the layout settle over the iterations. + step <- sqrt(rowSums(delta^2)) + limit <- ifelse(step > 1, 1 / step, 1) + temp <- 1 - (i - 1) / times + coords <- coords + 0.1 * temp * delta * limit } coords <- as.data.frame(coords) names(coords) <- c("x", "y") diff --git a/cran-comments.md b/cran-comments.md index 10a33d8a..d63d9d17 100644 --- a/cran-comments.md +++ b/cran-comments.md @@ -1,3 +1,22 @@ +## Resubmission + +This is a resubmission of 1.2.0, which failed the incoming checks with a test error +on Windows and on Debian. Both are fixed here. + +* `layout_valence()` started its nodes at random points, and a start that placed two + nodes close together gave a force large enough to spoil the layout. The nodes now + start on a circle, the force is bounded, and the test sets a seed. +* On Windows, the tutorial test failed on a deprecation warning that the tutorial code + does not raise itself: `netrics::tie_by_closeness()` (0.4.1) calls + `manynet::to_ties()`, which manynet deprecates in 2.3.0. netrics 1.0.0, to be + submitted, calls the current function. The test now fails only if the tutorial calls + a deprecated function itself, so it passes with either netrics version. + +The full test suite passes with each of these three pairs: manynet 2.2.3 with +netrics 0.4.0, manynet 2.3.1 with netrics 0.4.1 (the pair that failed on Windows), +and manynet 2.3.0 with netrics 1.0.0. manynet 2.3.1 and netrics 1.0.0 are to be +submitted; this version does not require either of them. + ## Test environments * local R installation, aarch64-apple-darwin20, R 4.5.1 diff --git a/tests/testthat/helper-tutorials.R b/tests/testthat/helper-tutorials.R index f3adf96f..af6de9a3 100644 --- a/tests/testthat/helper-tutorials.R +++ b/tests/testthat/helper-tutorials.R @@ -89,6 +89,25 @@ check_tute_functions <- function(path, skip = "ergm\\(|grapht\\("){ if (!grepl("deprecate|defunct|moved", msg, ignore.case = TRUE)) { w <- NULL } + + # Only fail if the tutorial calls the deprecated function itself. A + # dependency that calls a deprecated function of its own dependency + # raises the same warning, and no edit to this tutorial can silence it. + if (!is.null(w)) { + code <- paste(deparse(exprs[[i]]), collapse = " ") + # The name a deprecation warning reports, in either the base R form + # ("'to_ties' is deprecated") or the {lifecycle} form + # ("`to_ties()` was deprecated in manynet 2.3.0"). + hits <- regmatches(msg, gregexpr( + "[`'\"][^`'\"]+[`'\"][^[:alpha:]]*(is|was|has been)[[:space:]]+(deprecated|defunct|moved)", + msg))[[1]] + named <- gsub("^[`'\"]([^`'\"]+)[`'\"].*$", "\\1", hits) + named <- gsub("\\(\\)$", "", named) + if (length(named) > 0 && + !any(vapply(named, grepl, logical(1), x = code, fixed = TRUE))) { + w <- NULL + } + } } # Now test what happened diff --git a/tests/testthat/test-layout_valence.R b/tests/testthat/test-layout_valence.R index 41af0eba..6c3de2bb 100644 --- a/tests/testthat/test-layout_valence.R +++ b/tests/testthat/test-layout_valence.R @@ -1,4 +1,5 @@ test_that("valence layout works", { + set.seed(123) edges <- data.frame(from = c("A", "B"), to = c("B", "C"), weight = c(3, 3),