|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| extract_hexagonal_puzzle_structure <- function(rings, seed, diameter = 240,
|
| tabsize = 6, jitter = 5,
|
| do_warp = FALSE, do_trunc = FALSE) {
|
|
|
|
|
| puzzle_result <- generate_hex_jigsaw_svg(
|
| seed = seed,
|
| tabsize = tabsize,
|
| jitter = jitter,
|
| diameter = diameter,
|
| rings = rings,
|
| do_warp = do_warp,
|
| do_trunc = do_trunc
|
| )
|
|
|
|
|
| return(list(
|
| type = "hexagonal",
|
| rings = rings,
|
| diameter = diameter,
|
| seed = seed,
|
| num_pieces = 3 * rings * (rings - 1) + 1,
|
| paths = list(
|
| horizontal = puzzle_result$horizontal,
|
| vertical = puzzle_result$vertical,
|
| border = puzzle_result$border
|
| ),
|
| parameters = puzzle_result$parameters,
|
| svg = puzzle_result$svg
|
| ))
|
| }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| generate_separated_hexagonal_svg <- function(rings = 3, seed = NULL,
|
| diameter = 240, offset = 10,
|
| arrangement = "rectangular",
|
| use_bezier = FALSE,
|
| tabsize = 6, jitter = 5,
|
| do_warp = FALSE, do_trunc = FALSE,
|
| colors = NULL, stroke_width = 1,
|
| background = "none",
|
| palette = NULL,
|
| fill_color = "none",
|
| opacity = 1.0) {
|
|
|
| .Deprecated("generate_puzzle",
|
| msg = paste(
|
| "generate_separated_hexagonal_svg() is deprecated.",
|
| "Use generate_puzzle(type = 'hexagonal', offset = X) instead.",
|
| "Example: generate_puzzle(type = 'hexagonal', grid = c(3), size = c(240), offset = 10)"
|
| )
|
| )
|
|
|
| if (is.null(seed)) {
|
| seed <- as.integer(runif(1) * 10000)
|
| }
|
|
|
|
|
| init_hex_jigsaw(seed = seed, rings = rings, diameter = diameter)
|
|
|
|
|
| num_pieces <- 3 * rings * (rings - 1) + 1
|
|
|
| log_info("Generating separated layout for {num_pieces} pieces")
|
|
|
|
|
|
|
| piece_radius <- diameter / (4 * rings - 2)
|
|
|
|
|
| if (arrangement == "rectangular") {
|
|
|
| cols <- ceiling(sqrt(num_pieces * 1.3))
|
| rows <- ceiling(num_pieces / cols)
|
|
|
| log_info("Using rectangular grid: {cols} x {rows}")
|
|
|
|
|
| total_width <- cols * (2 * piece_radius + offset) + offset
|
| total_height <- rows * (2 * piece_radius + offset) + offset
|
| vb_x <- 0
|
| vb_y <- 0
|
| } else {
|
|
|
|
|
|
|
|
|
| base_spacing <- piece_radius * 2
|
| separation_factor <- 1.0 + (offset / base_spacing)
|
|
|
|
|
| max_radius <- rings * base_spacing * separation_factor * 1.5
|
|
|
|
|
| margin <- piece_radius * 2
|
| total_width <- 2 * (max_radius + margin)
|
| total_height <- 2 * (max_radius + margin)
|
|
|
|
|
| vb_x <- -total_width / 2
|
| vb_y <- -total_height / 2
|
| }
|
|
|
|
|
| if (is.null(colors)) {
|
| colors <- get_puzzle_colors(num_pieces, palette)
|
| }
|
|
|
|
|
| svg_lines <- c(
|
| sprintf('<svg width="%.1fmm" height="%.1fmm" viewBox="%.2f %.2f %.2f %.2f" xmlns="http://www.w3.org/2000/svg">',
|
| total_width, total_height, vb_x, vb_y, total_width, total_height),
|
| sprintf(' <title>Hexagonal Puzzle - %d rings, %d pieces (separated by %dmm)</title>',
|
| rings, num_pieces, offset)
|
| )
|
|
|
|
|
|
|
| if (is.list(background) && !is.null(background$type) && background$type == "gradient") {
|
|
|
| center_color <- background$center
|
| middle_color <- background$middle
|
| edge_color <- background$edge
|
| svg_lines <- c(svg_lines,
|
| ' <defs>',
|
| sprintf(' <radialGradient id="bg-gradient" cx="50%%" cy="50%%" r="50%%">'),
|
| sprintf(' <stop offset="0%%" style="stop-color:%s;stop-opacity:1" />', center_color),
|
| sprintf(' <stop offset="50%%" style="stop-color:%s;stop-opacity:1" />', middle_color),
|
| sprintf(' <stop offset="100%%" style="stop-color:%s;stop-opacity:1" />', edge_color),
|
| ' </radialGradient>',
|
| ' </defs>',
|
| sprintf(' <rect x="%.2f" y="%.2f" width="%.2f" height="%.2f" fill="url(#bg-gradient)"/>',
|
| vb_x, vb_y, total_width, total_height)
|
| )
|
| } else if (is.character(background) && background == "gradient") {
|
|
|
| svg_lines <- c(svg_lines,
|
| ' <defs>',
|
| ' <radialGradient id="bg-gradient" cx="50%" cy="50%" r="50%">',
|
| ' <stop offset="0%" style="stop-color:#e3f2fd;stop-opacity:1" />',
|
| ' <stop offset="50%" style="stop-color:#bbdefb;stop-opacity:1" />',
|
| ' <stop offset="100%" style="stop-color:#90caf9;stop-opacity:1" />',
|
| ' </radialGradient>',
|
| ' </defs>',
|
| sprintf(' <rect x="%.2f" y="%.2f" width="%.2f" height="%.2f" fill="url(#bg-gradient)"/>',
|
| vb_x, vb_y, total_width, total_height)
|
| )
|
| } else if (is.character(background) && background != "none" && background != "") {
|
|
|
| svg_lines <- c(svg_lines,
|
| sprintf(' <rect x="%.2f" y="%.2f" width="%.2f" height="%.2f" fill="%s"/>',
|
| vb_x, vb_y, total_width, total_height, background)
|
| )
|
| }
|
|
|
|
|
| svg_lines <- c(svg_lines, ' <g id="separated-pieces">')
|
|
|
|
|
| if (use_bezier) {
|
|
|
| log_info("Generating pieces with bezier curves and tabs...")
|
|
|
|
|
| if (arrangement == "rectangular") {
|
|
|
| separated <- FALSE
|
| base_spacing <- NULL
|
| } else {
|
|
|
| separated <- TRUE
|
| base_spacing <- piece_radius * 2
|
| separation_factor <- 1.0 + (offset / base_spacing)
|
| }
|
|
|
|
|
| pieces <- generate_hex_pieces_with_edge_map(
|
| rings = rings,
|
| seed = seed,
|
| diameter = diameter,
|
| tabsize = tabsize,
|
| jitter = jitter,
|
| separated = separated,
|
| base_spacing = base_spacing,
|
| separation_factor = if (separated) separation_factor else 1.0,
|
| do_warp = do_warp,
|
| do_trunc = do_trunc
|
| )
|
|
|
|
|
| for (i in 1:num_pieces) {
|
| piece <- pieces[[i]]
|
|
|
|
|
| if (arrangement == "rectangular") {
|
| row <- floor((i - 1) / cols)
|
| col <- (i - 1) %% cols
|
| piece$center_x <- offset + col * (2 * piece_radius + offset) + piece_radius
|
| piece$center_y <- offset + row * (2 * piece_radius + offset) + piece_radius
|
|
|
|
|
|
|
|
|
| }
|
|
|
|
|
| color_index <- ((i - 1) %% length(colors)) + 1
|
| piece_color <- colors[color_index]
|
|
|
| svg_lines <- c(svg_lines,
|
| sprintf(' <path d="%s" fill="%s" stroke="%s" stroke-width="%.1f" opacity="%.2f"/>',
|
| piece$path, fill_color, piece_color, stroke_width, opacity),
|
| sprintf(' <text x="%.2f" y="%.2f" text-anchor="middle" dominant-baseline="central" font-size="8" fill="%s">%d</text>',
|
| piece$center_x, piece$center_y, piece_color, i)
|
| )
|
| }
|
| } else {
|
|
|
| for (i in 1:num_pieces) {
|
|
|
| piece_rotation <- 0
|
|
|
| if (arrangement == "rectangular") {
|
| row <- floor((i - 1) / cols)
|
| col <- (i - 1) %% cols
|
| center_x <- offset + col * (2 * piece_radius + offset) + piece_radius
|
| center_y <- offset + row * (2 * piece_radius + offset) + piece_radius
|
| } else {
|
|
|
|
|
|
|
| base_spacing <- piece_radius * 2
|
| separation_factor <- 1.0 + (offset / base_spacing)
|
|
|
|
|
| position <- calculate_hex_piece_position(
|
| piece_id = i,
|
| rings = rings,
|
| base_spacing = base_spacing,
|
| separation_factor = separation_factor
|
| )
|
|
|
| center_x <- position$x
|
| center_y <- position$y
|
|
|
|
|
|
|
|
|
| piece_rotation <- 0
|
|
|
|
|
| if (i <= 3) {
|
| rot_deg <- round(piece_rotation * 180 / pi, 2)
|
| cx_fmt <- round(center_x, 1)
|
| cy_fmt <- round(center_y, 1)
|
| log_info("Piece {i}: (x={cx_fmt}, y={cy_fmt}, rot={rot_deg} deg)")
|
| }
|
| }
|
|
|
|
|
| color_index <- ((i - 1) %% length(colors)) + 1
|
| piece_color <- colors[color_index]
|
|
|
|
|
| hex_path <- create_hex_placeholder(center_x, center_y, piece_radius * 0.8, piece_rotation)
|
|
|
| svg_lines <- c(svg_lines,
|
| sprintf(' <path d="%s" fill="%s" stroke="%s" stroke-width="%.1f" opacity="%.2f"/>',
|
| hex_path, fill_color, piece_color, stroke_width, opacity),
|
| sprintf(' <text x="%.2f" y="%.2f" text-anchor="middle" dominant-baseline="central" font-size="8" fill="%s">%d</text>',
|
| center_x, center_y, piece_color, i)
|
| )
|
| }
|
| }
|
|
|
| svg_lines <- c(svg_lines, ' </g>', '</svg>')
|
|
|
| return(paste(svg_lines, collapse = "\n"))
|
| }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| create_hex_placeholder <- function(cx, cy, radius, rotation = 0) {
|
|
|
|
|
|
|
|
|
| base_offset <- pi / 6
|
| vertices <- list()
|
| for (i in 0:5) {
|
| angle <- i * pi / 3 + rotation + base_offset
|
| x <- cx + radius * cos(angle)
|
| y <- cy + radius * sin(angle)
|
| vertices[[i + 1]] <- c(x, y)
|
| }
|
|
|
|
|
| path <- sprintf("M %.2f %.2f", vertices[[1]][1], vertices[[1]][2])
|
| for (i in 2:6) {
|
| path <- paste(path, sprintf("L %.2f %.2f", vertices[[i]][1], vertices[[i]][2]))
|
| }
|
| path <- paste(path, "Z")
|
|
|
| return(path)
|
| }
|
|
|