|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| .jigsaw_env <- new.env()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| init_jigsaw <- function(seed = NULL, tabsize = 6, jitter = 4,
|
| width = 300, height = 200,
|
| unit = "mm", dpi = 96, radius = 2.0,
|
| xn = 15, yn = 10,
|
| min_tab_size = NULL, max_tab_size = NULL) {
|
|
|
| if (is.null(seed)) {
|
| seed <- as.integer(runif(1) * 10000)
|
| }
|
|
|
|
|
| .jigsaw_env$seed_initial <- seed
|
| .jigsaw_env$seed <- seed
|
| .jigsaw_env$tabsize <- tabsize
|
| .jigsaw_env$jitter <- jitter
|
| .jigsaw_env$width <- width
|
| .jigsaw_env$height <- height
|
| .jigsaw_env$unit <- unit
|
| .jigsaw_env$dpi <- dpi
|
| .jigsaw_env$radius <- radius
|
| .jigsaw_env$xn <- xn
|
| .jigsaw_env$yn <- yn
|
| .jigsaw_env$offset <- 0.0
|
|
|
|
|
| .jigsaw_env$min_tab_size <- min_tab_size
|
| .jigsaw_env$max_tab_size <- max_tab_size
|
|
|
|
|
| .jigsaw_env$t_base <- tabsize / 100.0
|
| .jigsaw_env$t <- .jigsaw_env$t_base
|
| .jigsaw_env$j <- jitter / 100.0
|
|
|
|
|
|
|
| rng_count <- calc_rect_rng_count(xn, yn)
|
| .jigsaw_env$rng <- create_rng_iterator(seed, rng_count)
|
| }
|
|
|
|
|
|
|
| random <- function() {
|
| .jigsaw_env$rng$next_val()
|
| }
|
|
|
| uniform <- function(min_val, max_val) {
|
| .jigsaw_env$rng$uniform(min_val, max_val)
|
| }
|
|
|
| rbool <- function() {
|
| .jigsaw_env$rng$rbool()
|
| }
|
|
|
|
|
| first <- function() {
|
| .jigsaw_env$e <- uniform(-.jigsaw_env$j, .jigsaw_env$j)
|
| .jigsaw_env$flip <- NULL
|
| next_tab()
|
| }
|
|
|
| next_tab <- function() {
|
| flipold <- if (exists("flip", envir = .jigsaw_env)) .jigsaw_env$flip else NULL
|
| .jigsaw_env$flip <- rbool()
|
| .jigsaw_env$a <- if (is.null(flipold) || .jigsaw_env$flip == flipold) -.jigsaw_env$e else .jigsaw_env$e
|
| .jigsaw_env$b <- uniform(-.jigsaw_env$j, .jigsaw_env$j)
|
| .jigsaw_env$c <- uniform(-.jigsaw_env$j, .jigsaw_env$j)
|
| .jigsaw_env$d <- uniform(-.jigsaw_env$j, .jigsaw_env$j)
|
| .jigsaw_env$e <- uniform(-.jigsaw_env$j, .jigsaw_env$j)
|
|
|
|
|
| apply_tab_constraints()
|
| }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| apply_tab_constraints <- function() {
|
|
|
| if (is.null(.jigsaw_env$min_tab_size) && is.null(.jigsaw_env$max_tab_size)) {
|
| .jigsaw_env$t <- .jigsaw_env$t_base
|
| return()
|
| }
|
|
|
|
|
| edge_length <- sl()
|
|
|
|
|
| tab_height <- 3.0 * .jigsaw_env$t_base * edge_length
|
|
|
| t <- .jigsaw_env$t_base
|
|
|
|
|
| if (!is.null(.jigsaw_env$min_tab_size) && tab_height < .jigsaw_env$min_tab_size) {
|
| t <- .jigsaw_env$min_tab_size / (3.0 * edge_length)
|
|
|
|
|
|
|
|
|
| if (4.0 * t > 0.7) {
|
|
|
| t <- 0.175
|
| }
|
| }
|
|
|
|
|
| if (!is.null(.jigsaw_env$max_tab_size) && tab_height > .jigsaw_env$max_tab_size) {
|
| t <- .jigsaw_env$max_tab_size / (3.0 * edge_length)
|
| }
|
|
|
| .jigsaw_env$t <- t
|
| }
|
|
|
|
|
| sl <- function() {
|
| return(if (.jigsaw_env$vertical) .jigsaw_env$height / .jigsaw_env$yn else .jigsaw_env$width / .jigsaw_env$xn)
|
| }
|
|
|
| sw <- function() {
|
| return(if (.jigsaw_env$vertical) .jigsaw_env$width / .jigsaw_env$xn else .jigsaw_env$height / .jigsaw_env$yn)
|
| }
|
|
|
| ol <- function() {
|
| return(.jigsaw_env$offset + sl() * (if (.jigsaw_env$vertical) .jigsaw_env$yi else .jigsaw_env$xi))
|
| }
|
|
|
| ow <- function() {
|
| return(.jigsaw_env$offset + sw() * (if (.jigsaw_env$vertical) .jigsaw_env$xi else .jigsaw_env$yi))
|
| }
|
|
|
| l <- function(v) {
|
| ret <- ol() + sl() * v
|
| return(round(ret * 100) / 100)
|
| }
|
|
|
| w <- function(v) {
|
| ret <- ow() + sw() * v * (if (.jigsaw_env$flip) -1.0 else 1.0)
|
| return(round(ret * 100) / 100)
|
| }
|
|
|
|
|
| p0l <- function() l(0.0)
|
| p0w <- function() w(0.0)
|
| p1l <- function() l(0.2)
|
| p1w <- function() w(.jigsaw_env$a)
|
| p2l <- function() l(0.5 + .jigsaw_env$b + .jigsaw_env$d)
|
| p2w <- function() w(-.jigsaw_env$t + .jigsaw_env$c)
|
| p3l <- function() l(0.5 - .jigsaw_env$t + .jigsaw_env$b)
|
| p3w <- function() w(.jigsaw_env$t + .jigsaw_env$c)
|
| p4l <- function() l(0.5 - 2.0 * .jigsaw_env$t + .jigsaw_env$b - .jigsaw_env$d)
|
| p4w <- function() w(3.0 * .jigsaw_env$t + .jigsaw_env$c)
|
| p5l <- function() l(0.5 + 2.0 * .jigsaw_env$t + .jigsaw_env$b - .jigsaw_env$d)
|
| p5w <- function() w(3.0 * .jigsaw_env$t + .jigsaw_env$c)
|
| p6l <- function() l(0.5 + .jigsaw_env$t + .jigsaw_env$b)
|
| p6w <- function() w(.jigsaw_env$t + .jigsaw_env$c)
|
| p7l <- function() l(0.5 + .jigsaw_env$b + .jigsaw_env$d)
|
| p7w <- function() w(-.jigsaw_env$t + .jigsaw_env$c)
|
| p8l <- function() l(0.8)
|
| p8w <- function() w(.jigsaw_env$e)
|
| p9l <- function() l(1.0)
|
| p9w <- function() w(0.0)
|
|
|
|
|
| gen_dh <- function() {
|
| str <- ""
|
| .jigsaw_env$vertical <- 0
|
|
|
| for (yi in 1:(.jigsaw_env$yn - 1)) {
|
| .jigsaw_env$yi <- yi
|
| .jigsaw_env$xi <- 0
|
| first()
|
| str <- paste0(str, "M ", p0l(), ",", p0w(), " ")
|
|
|
| while (.jigsaw_env$xi < .jigsaw_env$xn) {
|
| str <- paste0(str, "C ", p1l(), " ", p1w(), " ", p2l(), " ", p2w(), " ", p3l(), " ", p3w(), " ")
|
| str <- paste0(str, "C ", p4l(), " ", p4w(), " ", p5l(), " ", p5w(), " ", p6l(), " ", p6w(), " ")
|
| str <- paste0(str, "C ", p7l(), " ", p7w(), " ", p8l(), " ", p8w(), " ", p9l(), " ", p9w(), " ")
|
| next_tab()
|
| .jigsaw_env$xi <- .jigsaw_env$xi + 1
|
| }
|
| }
|
| return(str)
|
| }
|
|
|
| gen_dv <- function() {
|
| str <- ""
|
| .jigsaw_env$vertical <- 1
|
|
|
| for (xi in 1:(.jigsaw_env$xn - 1)) {
|
| .jigsaw_env$xi <- xi
|
| .jigsaw_env$yi <- 0
|
| first()
|
| str <- paste0(str, "M ", p0w(), ",", p0l(), " ")
|
|
|
| while (.jigsaw_env$yi < .jigsaw_env$yn) {
|
| str <- paste0(str, "C ", p1w(), " ", p1l(), " ", p2w(), " ", p2l(), " ", p3w(), " ", p3l(), " ")
|
| str <- paste0(str, "C ", p4w(), " ", p4l(), " ", p5w(), " ", p5l(), " ", p6w(), " ", p6l(), " ")
|
| str <- paste0(str, "C ", p7w(), " ", p7l(), " ", p8w(), " ", p8l(), " ", p9w(), " ", p9l(), " ")
|
| next_tab()
|
| .jigsaw_env$yi <- .jigsaw_env$yi + 1
|
| }
|
| }
|
| return(str)
|
| }
|
|
|
| gen_db <- function() {
|
| offset <- .jigsaw_env$offset
|
| width <- .jigsaw_env$width
|
| height <- .jigsaw_env$height
|
| radius <- .jigsaw_env$radius
|
|
|
| str <- ""
|
| str <- paste0(str, "M ", (offset + radius), " ", (offset), " ")
|
| str <- paste0(str, "L ", (offset + width - radius), " ", (offset), " ")
|
| str <- paste0(str, "A ", (radius), " ", (radius), " 0 0 1 ", (offset + width), " ", (offset + radius), " ")
|
| str <- paste0(str, "L ", (offset + width), " ", (offset + height - radius), " ")
|
| str <- paste0(str, "A ", (radius), " ", (radius), " 0 0 1 ", (offset + width - radius), " ", (offset + height), " ")
|
| str <- paste0(str, "L ", (offset + radius), " ", (offset + height), " ")
|
| str <- paste0(str, "A ", (radius), " ", (radius), " 0 0 1 ", (offset), " ", (offset + height - radius), " ")
|
| str <- paste0(str, "L ", (offset), " ", (offset + radius), " ")
|
| str <- paste0(str, "A ", (radius), " ", (radius), " 0 0 1 ", (offset + radius), " ", (offset), " ")
|
| return(str)
|
| }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| generate_jigsaw_svg <- function(seed = NULL, tabsize = 6, jitter = 4,
|
| width = 300, height = 200,
|
| unit = "mm", dpi = 96, radius = 2.0,
|
| xn = 15, yn = 10) {
|
|
|
|
|
| if (unit == "px") {
|
| width_mm <- width * 25.4 / dpi
|
| height_mm <- height * 25.4 / dpi
|
| radius_mm <- radius * 25.4 / dpi
|
| } else {
|
| width_mm <- width
|
| height_mm <- height
|
| radius_mm <- radius
|
| }
|
|
|
|
|
| init_jigsaw(seed, tabsize, jitter, width_mm, height_mm, unit, dpi, radius_mm, xn, yn)
|
|
|
|
|
| horizontal_paths <- gen_dh()
|
| vertical_paths <- gen_dv()
|
| border_paths <- gen_db()
|
|
|
|
|
| if (unit == "px") {
|
| svg_width <- sprintf("%.0f", width)
|
| svg_height <- sprintf("%.0f", height)
|
|
|
| viewbox_w <- width_mm
|
| viewbox_h <- height_mm
|
| } else {
|
| svg_width <- sprintf("%.0fmm", width)
|
| svg_height <- sprintf("%.0fmm", height)
|
| viewbox_w <- width
|
| viewbox_h <- height
|
| }
|
|
|
|
|
| svg_content <- paste0(
|
| '<svg xmlns="http://www.w3.org/2000/svg" version="1.0" ',
|
| 'width="', svg_width, '" height="', svg_height, '" ',
|
| 'viewBox="0 0 ', viewbox_w, ' ', viewbox_h, '">',
|
| '<path fill="none" stroke="DarkBlue" stroke-width="0.1" d="',
|
| horizontal_paths,
|
| '"></path>',
|
| '<path fill="none" stroke="DarkRed" stroke-width="0.1" d="',
|
| vertical_paths,
|
| '"></path>',
|
| '<path fill="none" stroke="Black" stroke-width="0.1" d="',
|
| border_paths,
|
| '"></path>',
|
| '</svg>'
|
| )
|
|
|
| return(list(
|
| svg = svg_content,
|
| horizontal = horizontal_paths,
|
| vertical = vertical_paths,
|
| border = border_paths,
|
| parameters = list(
|
| seed = .jigsaw_env$seed_initial,
|
| tabsize = tabsize,
|
| jitter = jitter,
|
| width = width,
|
| height = height,
|
| unit = unit,
|
| dpi = dpi,
|
| width_mm = width_mm,
|
| height_mm = height_mm,
|
| radius = radius,
|
| xn = xn,
|
| yn = yn
|
| )
|
| ))
|
| }
|
|
|
|
|
|
|
|
|
| save_jigsaw_svg <- function(puzzle_data, filename = "jigsaw.svg") {
|
|
|
| output_parent <- dirname(filename)
|
| if (nzchar(output_parent) && output_parent != "." && !dir.exists(output_parent)) {
|
| dir.create(output_parent, recursive = TRUE)
|
| }
|
|
|
| writeLines(puzzle_data$svg, filename)
|
| log_success("Saved jigsaw puzzle to: {.file {filename}}")
|
| }
|
|
|
|
|
|
|
| print_puzzle_info <- function(puzzle_data) {
|
| params <- puzzle_data$parameters
|
| log_subheader("Jigsaw Puzzle Parameters")
|
| log_params("Puzzle Configuration", list(
|
| Seed = params$seed,
|
| Size = paste0(params$width, " x ", params$height, " mm"),
|
| Pieces = paste0(params$xn, " x ", params$yn, " = ", params$xn * params$yn, " total"),
|
| "Tab size" = paste0(params$tabsize, "%"),
|
| Jitter = paste0(params$jitter, "%"),
|
| "Corner radius" = paste0(params$radius, " mm")
|
| ))
|
| }
|
|
|
|
|
|
|
|
|