Code Monkey home page Code Monkey logo

particles's Introduction

particles

R-CMD-check Codecov test coverage CRAN_Release_Badge CRAN_Download_Badge

This package implements the d3-force algorithm developed by Mike Bostock in R, thus providing a way to run many types of particle simulations using its versatile interface.

While the first goal is to provide feature parity with its JavaScript origin, the intentions is to add more forces, constraints, etc. down the line. While d3-force is most well-known as a layout engine for visualising networks, it is capable of much more. Therefore, particles is provided as a very open framework to play with. Eventually ggraph will provide some shortcut layouts based on particles with the aim of facilitating network visualisation.

Usage

particles builds upon the framework provided by tidygraph and adds a set of verbs that defines the simulation:

  • simulate() : Creates a simulation based on the input graph, global parameters, and a genesis function that sets up the initial conditions of the simulation.
  • wield() : Adds a force to the simulation. All forces implemented in d3-force are available as well as some additionals.
  • impose() : Adds a constraint to the simulation. This function is a departure from d3-force, as d3-force only allowed for simple fixing of x and/or y coordinates through the use of the fx and fy accessors. particles formalises the use of simulation constraints and adds new functionalities.
  • evolve() : Progresses the simulation, either a predefined number of steps, or until the simulated annealing has cooled down.

Example

A recreation of the Les Miserable network in https://bl.ocks.org/mbostock/4062045

library(tidyverse)
library(ggraph)
library(tidygraph)
library(particles)
# Data preparation
d3_col <- c(
  '0' = "#98df8a",
  '1' = "#1f77b4",
  '2' = "#aec7e8",
  '3' = "#ff7f0e",
  '4' = "#ffbb78",
  '5' = "#2ca02c",
  '6' = "#d62728",
  '7' = "#ff9896",
  '8' = "#9467bd",
  '9' = "#c5b0d5",
  '10' =  "#8c564b"
)

raw_data <- 'https://gist.githubusercontent.com/mbostock/4062045/raw/5916d145c8c048a6e3086915a6be464467391c62/miserables.json'
miserable_data <- jsonlite::read_json(raw_data, simplifyVector = TRUE)
miserable_data$nodes$group <- as.factor(miserable_data$nodes$group)
miserable_data$links <- miserable_data$links |>  
  mutate(from = match(source, miserable_data$nodes$id),
         to = match(target, miserable_data$nodes$id))

# Actual particles part
mis_graph <- miserable_data |> 
  simulate() |> 
  wield(link_force) |> 
  wield(manybody_force) |> 
  wield(center_force) |> 
  evolve() |> 
  as_tbl_graph()

# Plotting with ggraph
ggraph(mis_graph, 'nicely') + 
  geom_edge_link(aes(width = sqrt(value)), colour = '#999999', alpha = 0.6) + 
  geom_node_point(aes(fill = group), shape = 21, colour = 'white', size = 4, 
                  stroke = 1.5) + 
  scale_fill_manual('Group', values = d3_col) + 
  scale_edge_width('Value', range = c(0.5, 3)) + 
  coord_fixed() +
  theme_graph()
#> Warning: Existing variables `x`, `y` overwritten by layout variables

If you intend to follow the steps of the simulation it is possible to attach an event handler that gets called ofter each generation of the simulation. If the handler produces a plot the result will be an animation of the simulation:

# Random overlapping circles
graph <- as_tbl_graph(igraph::erdos.renyi.game(100, 0)) |> 
  mutate(x = runif(100) - 0.5, 
         y = runif(100) - 0.5, 
         radius = runif(100, min = 0.1, 0.2))

# Plotting function
graph_plot <- function(sim) {
  gr <- as_tbl_graph(sim)
  p <- ggraph(gr, layout = as_tibble(gr)) +
    geom_node_circle(aes(r = radius), fill = 'forestgreen', alpha = 0.5) + 
    coord_fixed(xlim = c(-2.5, 2.5), ylim = c(-2.5, 2.5)) + 
    theme_graph()
  plot(p)
}

# Simulation
graph %>% simulate(velocity_decay = 0.7, setup = predefined_genesis(x, y)) |> 
  wield(collision_force, radius = radius, n_iter = 2) |> 
  wield(x_force, x = 0, strength = 0.002) |> 
  wield(y_force, y = 0, strength = 0.002) |> 
  evolve(on_generation = graph_plot)

Click here for resulting animation (GitHub don’t allow big gifs in readme)

Installation

You can install particles from CRAN using install.packages("particles") or alternatively install the development version from github with:

# install.packages("devtools")
devtools::install_github("thomasp85/particles")

Immense Thanks

  • A huge “Thank You” to Mike Bostock is in place. Without d3-force, particles wouldn’t exist and without d3 in general the world would be a sadder place.
  • The C++ quad tree implementation that powers manbody_force and collision_force is a modification of the implementation made by Andrei Kashcha and made available under MIT license. Big thanks to Andrei as well.

particles's People

Contributors

thomasp85 avatar waschina avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

particles's Issues

Release particles 0.2.3

Prepare for release:

  • git pull
  • Check current CRAN check results
  • Polish NEWS
  • devtools::build_readme()
  • urlchecker::url_check()
  • devtools::check(remote = TRUE, manual = TRUE)
  • devtools::check_win_devel()
  • rhub::check_for_cran()
  • rhub::check(platform = 'ubuntu-rchk')
  • rhub::check_with_sanitizers()
  • revdepcheck::revdep_check(num_workers = 4)
  • Update cran-comments.md
  • git push

Submit to CRAN:

  • usethis::use_version('patch')
  • devtools::submit_cran()
  • Approve email

Wait for CRAN...

  • Accepted 🎉
  • git push
  • usethis::use_github_release()
  • usethis::use_dev_version()
  • git push

Error in layout_fun(graph, circular = circular, ...) : unused argument (node.position = as_tibble(gr))

Tried running the code on your readme page verbatim, and received the following error:

Error in layout_fun(graph, circular = circular, ...) : unused argument (node.position = as_tibble(gr))

Am I missing something?

R version 3.6.1 (2019-07-05)
Platform: x86_64-apple-darwin15.6.0 (64-bit)
Running under: macOS Mojave 10.14.6

Matrix products: default
BLAS: /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/3.6/Resources/lib/libRlapack.dylib

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

attached base packages:
[1] stats graphics grDevices utils datasets methods base

other attached packages:
[1] forcats_0.4.0 stringr_1.4.0 dplyr_0.8.3 purrr_0.3.2 readr_1.3.1 tidyr_1.0.0
[7] tibble_2.1.3 tidyverse_1.2.1 particles_0.2.2 tidygraph_1.1.2 ggraph_2.0.0 ggplot2_3.2.1

Possible issue with velocity decay

Hi Thomas, hope you're good.

I make generative art/animation (mostly in R) and I've been exploring your brilliant Particles library (inspired by your art) as I think there's massive potential for some really interesting pieces. Hoping I can get your help with an issue which could be a bug (but more likely I'm just misunderstanding something)

I'm working on some code where I want to create a simulation which reacts to a new (randomly generated) field force every 10 evolutions (10 is an arbitrary number - eventually this will be triggered by an audio input). Each evolution is plotted and exporting, capturing the simulation.

The idea is that the particles in the simulation will be 're-energised' every 10 frames, energetically reacting to the new field force.

What I'm finding however is that even though I have the velocity decay set to 0, the velocity decays to 0 after 20 frames or so and the following field forces don't have any effect.

I suspect I'm doing something wrong with the 'reheat' function but I can't find much in the way of documentation/examples.

Was wondering if you could possibly take a look at my code and let me know if you can see where I'm going wrong?

library(particles)
library(tidygraph)
library(ggplot2)
library(tidyr)
library(scales)
library(av)

root_dir <- "C:\\Local workspace\\Art\\simulations\\frames"

# function  to create flow field
generate_field_function <- function(size=10){
  
  # generate input
  grid <- long_grid(seq(1, size, length.out = size*size), seq(1, size, length.out = size*size))
  
  # generate perlin grid
  grid$noise <- gen_perlin(grid$x, grid$y)
  
  # rescale and reformat
  grid <- as.data.frame(grid)
  grid$noise <- scales::rescale(grid$noise, to=c(0,7))
  grid <- grid %>% pivot_wider(names_from = x, values_from = noise)
  colnames(grid) <- paste0("V",1:size*size)
  grid <- as.matrix(grid)
}


plot_function <- function(sim){
  
  # count number of existing files (used as counter)
  count_existing_files <- length(list.files(root_dir))
  new_file <- count_existing_files + 1
  
  # plot
  plt <-
    ggplot(as_tibble(sim)) + 
    geom_point(aes(x, y), size = 0.5, stroke=0.5, alpha=1) +
    theme_void()
  
  # save plot to directory
  frame_no <- str_pad(new_file, 4, pad = "0")
  dir.create(file.path(root_dir), showWarnings = FALSE)
  ggsave(paste0(frame_no,".png"),  plot = plt,  device = png, path = root_dir, height=600, width=600,  units="px")
  
  # if frame multiple of 10 then generate new flow field and reheat simulation
  if(new_file %% 10 == 0){
    
    print("new grid")
    
    new_grid <- generate_field_function(10)
    
    new_sim <- sim %>%
      unwield(1) %>%
      wield(field_force, angle=new_grid, vel=1) %>%
      reheat(1) 
    
    return(new_sim)
  }
}

# generate initial flow gird
grid <- generate_field_function(10)
  
# run simulation
create_empty(1000) %>% 
  simulate(setup = aquarium_genesis(), alpha_decay=0) %>% 
  wield(field_force, angle=grid, vel=1) %>% 
  evolve(100, on_generation= plot_function) 

# generate video from exported plots
png_files <- list.files(root_dir, pattern = ".*png$", full.names = TRUE)
av_encode_video(png_files, "C:\\Local workspace\\Art\\simulations\\simulation_export_1.mp4",
                framerate = 10)

Any ideas/tips you can give are appreciated

Thanks

Joe

Installation issue tidygraph is_dag

Having trouble installing the package. First time around it complained about the dependency digest not being installed. Second try after installing digest:

installing to D:/R-3.4.3/library/particles/libs/x64
** R
** preparing package for lazy loading
Error : object 'is_dag' is not exported by 'namespace:tidygraph'
ERROR: lazy loading failed for package 'particles'
* removing 'D:/R-3.4.3/library/particles'
In R CMD INSTALL

Do I have the wrong version of tidygraph?

devtools::install_github('thomasp85/tidygraph') gives me:

Skipping install of 'tidygraph' from a github remote, the SHA1 (791c4a24) has not changed since last install.
  Use `force = TRUE` to force installation
> sessionInfo()
R version 3.4.3 (2017-11-30)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows >= 8 x64 (build 9200)

Matrix products: default

locale:
[1] LC_COLLATE=Swedish_Sweden.1252  LC_CTYPE=Swedish_Sweden.1252    LC_MONETARY=Swedish_Sweden.1252
[4] LC_NUMERIC=C                    LC_TIME=Swedish_Sweden.1252    

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

loaded via a namespace (and not attached):
 [1] httr_1.3.1       compiler_3.4.3   R6_2.2.2         tools_3.4.3      withr_2.1.0.9000 curl_3.0        
 [7] yaml_2.1.15      memoise_1.1.0    git2r_0.19.0     digest_0.6.13    devtools_1.13.4 

Issue with polygon_constraint

Hoping to simulate particles moving in an enclosed, reflective polygon. Noting that particles in simulation with imposed polygon_constraint have velocity set at zero upon hitting boundary. In the resultant GIF, it appears that particles on the right and bottom are reflecting off the boundary, whereas particles on the top and left are sticking and forming a line along those two boundaries. Is there a way to maintain particle velocities and just reflect off the boundary?

Load packages in example code

Not sure which package(s) you're attaching before the first pipe, but, as is, it's not a known function (I'd do a PR, but I don't want to mess up your order of operations).

failure to install particles

I get the following when I try to install particles. Any suggestions? Thanks.

  • installing source package 'particles' ...
    ** libs
    c:/Rtools/mingw_64/bin/g++ -m64 -std=gnu++11 -I"C:/PROGRA1/MICROS3/ROPEN1/R-341.2/include" -DNDEBUG -I"C:/Users/pl39/Documents/R/win-library/3.4/Rcpp/include" -I"C:/swarm/workspace/External-R-3.4.2/vendor/extsoft/include" -O2 -Wall -mtune=core2 -c RcppExports.cpp -o RcppExports.o
    c:/Rtools/mingw_64/bin/g++ -m64 -std=gnu++11 -I"C:/PROGRA1/MICROS3/ROPEN1/R-341.2/include" -DNDEBUG -I"C:/Users/pl39/Documents/R/win-library/3.4/Rcpp/include" -I"C:/swarm/workspace/External-R-3.4.2/vendor/extsoft/include" -O2 -Wall -mtune=core2 -c collision.cpp -o collision.o
    collision.cpp: In function 'Rcpp::NumericMatrix collision(Rcpp::NumericMatrix, Rcpp::NumericMatrix, Rcpp::NumericVector, double)':
    collision.cpp:13:17: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
    for (i = 0; i < pos.nrow(); ++i) {
    ^
    collision.cpp:26:17: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
    for (i = 0; i < pos.nrow(); ++i) {
    ^
    collision.cpp:29:17: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
    for (i = 0; i < pos.nrow(); ++i) {
    ^
    c:/Rtools/mingw_64/bin/g++ -m64 -std=gnu++11 -I"C:/PROGRA1/MICROS3/ROPEN1/R-341.2/include" -DNDEBUG -I"C:/Users/pl39/Documents/R/win-library/3.4/Rcpp/include" -I"C:/swarm/workspace/External-R-3.4.2/vendor/extsoft/include" -O2 -Wall -mtune=core2 -c nbody.cpp -o nbody.o
    nbody.cpp: In function 'Rcpp::NumericMatrix nbody(Rcpp::NumericMatrix, Rcpp::NumericVector, double, double, double, double)':
    nbody.cpp:13:17: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
    for (i = 0; i < pos.nrow(); ++i) {
    ^
    nbody.cpp:23:17: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
    for (i = 0; i < pos.nrow(); ++i) {
    ^
    nbody.cpp:26:17: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
    for (i = 0; i < pos.nrow(); ++i) {
    ^
    c:/Rtools/mingw_64/bin/g++ -m64 -std=gnu++11 -I"C:/PROGRA1/MICROS3/ROPEN1/R-341.2/include" -DNDEBUG -I"C:/Users/pl39/Documents/R/win-library/3.4/Rcpp/include" -I"C:/swarm/workspace/External-R-3.4.2/vendor/extsoft/include" -O2 -Wall -mtune=core2 -c pathdist.cpp -o pathdist.o
    c:/Rtools/mingw_64/bin/g++ -m64 -shared -s -static-libgcc -o particles.dll tmp.def RcppExports.o collision.o nbody.o pathdist.o -LC:/swarm/workspace/External-R-3.4.2/vendor/extsoft/lib/x64 -LC:/swarm/workspace/External-R-3.4.2/vendor/extsoft/lib -LC:/PROGRA1/MICROS3/ROPEN1/R-341.2/bin/x64 -lR
    installing to C:/Users/pl39/Documents/R/win-library/3.4/particles/libs/x64
    ** R
    ** preparing package for lazy loading
    Error : object 'graph_is_dag' is not exported by 'namespace:tidygraph'
    ERROR: lazy loading failed for package 'particles'
  • removing 'C:/Users/pl39/Documents/R/win-library/3.4/particles'
    Installation failed: Command failed (1)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.