tests/testthat/test-coverage-splot-polygons-41.R

# test-coverage-splot-polygons-41.R - Comprehensive tests for R/splot-polygons.R
# Tests for polygon vertex generation functions

# Make internal functions available for testing
skip_on_cran()

circle_vertices <- cograph:::circle_vertices
square_vertices <- cograph:::square_vertices
rectangle_vertices <- cograph:::rectangle_vertices
triangle_vertices <- cograph:::triangle_vertices
diamond_vertices <- cograph:::diamond_vertices
pentagon_vertices <- cograph:::pentagon_vertices
hexagon_vertices <- cograph:::hexagon_vertices
star_vertices <- cograph:::star_vertices
heart_vertices <- cograph:::heart_vertices
ellipse_vertices <- cograph:::ellipse_vertices
cross_vertices <- cograph:::cross_vertices
regular_polygon_vertices <- cograph:::regular_polygon_vertices
inset_polygon_vertices <- cograph:::inset_polygon_vertices
get_donut_base_vertices <- cograph:::get_donut_base_vertices
gear_vertices <- cograph:::gear_vertices
cloud_vertices <- cograph:::cloud_vertices
brain_vertices <- cograph:::brain_vertices
get_shape_vertices <- cograph:::get_shape_vertices

# ============================================
# CIRCLE VERTICES
# ============================================

test_that("circle_vertices generates correct structure", {
  verts <- circle_vertices(0, 0, 1)

  expect_true(is.list(verts))
  expect_true("x" %in% names(verts))
  expect_true("y" %in% names(verts))
  expect_equal(length(verts$x), 50)  # default n = 50
  expect_equal(length(verts$y), 50)
})

test_that("circle_vertices respects center coordinates", {
  verts <- circle_vertices(5, 10, 1)

  # Center of mass should be near the specified center
  expect_equal(mean(verts$x), 5, tolerance = 0.01)
  expect_equal(mean(verts$y), 10, tolerance = 0.01)
})

test_that("circle_vertices respects radius", {
  r <- 2.5
  verts <- circle_vertices(0, 0, r)

  # All points should be at distance r from center
  distances <- sqrt(verts$x^2 + verts$y^2)
  expect_true(all(abs(distances - r) < 0.01))
})

test_that("circle_vertices respects n parameter", {
  for (n in c(10, 25, 100, 200)) {
    verts <- circle_vertices(0, 0, 1, n = n)
    expect_equal(length(verts$x), n, info = paste("Failed for n =", n))
    expect_equal(length(verts$y), n, info = paste("Failed for n =", n))
  }
})

test_that("circle_vertices handles small n values", {
  # n = 3 creates a triangle-like shape
  verts <- circle_vertices(0, 0, 1, n = 3)
  expect_equal(length(verts$x), 3)
  expect_equal(length(verts$y), 3)
})

test_that("circle_vertices handles negative coordinates", {
  verts <- circle_vertices(-5, -3, 1.5)

  expect_equal(mean(verts$x), -5, tolerance = 0.01)
  expect_equal(mean(verts$y), -3, tolerance = 0.01)
})

# ============================================
# SQUARE VERTICES
# ============================================

test_that("square_vertices generates correct structure", {
  verts <- square_vertices(0, 0, 1)

  expect_true(is.list(verts))
  expect_equal(length(verts$x), 4)
  expect_equal(length(verts$y), 4)
})

test_that("square_vertices respects center coordinates", {
  verts <- square_vertices(3, 7, 1)

  expect_equal(mean(verts$x), 3)
  expect_equal(mean(verts$y), 7)
})

test_that("square_vertices respects half-width parameter", {
  r <- 2
  verts <- square_vertices(0, 0, r)

  # All x values should be +/- r
  expect_true(all(abs(verts$x) == r))
  expect_true(all(abs(verts$y) == r))
})

test_that("square_vertices creates proper corners", {
  verts <- square_vertices(0, 0, 1)

  # Should have corners at (-1,-1), (1,-1), (1,1), (-1,1)
  corners <- data.frame(x = verts$x, y = verts$y)
  expect_true(any(corners$x == -1 & corners$y == -1))
  expect_true(any(corners$x == 1 & corners$y == -1))
  expect_true(any(corners$x == 1 & corners$y == 1))
  expect_true(any(corners$x == -1 & corners$y == 1))
})

# ============================================
# RECTANGLE VERTICES
# ============================================

test_that("rectangle_vertices generates correct structure", {
  verts <- rectangle_vertices(0, 0, 2, 1)

  expect_true(is.list(verts))
  expect_equal(length(verts$x), 4)
  expect_equal(length(verts$y), 4)
})

test_that("rectangle_vertices respects width and height", {
  w <- 3
  h <- 1.5
  verts <- rectangle_vertices(0, 0, w, h)

  # X values should be +/- w
  expect_true(all(abs(verts$x) == w))
  # Y values should be +/- h
  expect_true(all(abs(verts$y) == h))
})

test_that("rectangle_vertices respects center coordinates", {
  verts <- rectangle_vertices(5, -2, 2, 1)

  expect_equal(mean(verts$x), 5)
  expect_equal(mean(verts$y), -2)
})

test_that("rectangle_vertices handles tall rectangle", {
  # Height > Width
  verts <- rectangle_vertices(0, 0, 1, 3)

  # Width extent should be 2, height extent should be 6
  expect_equal(diff(range(verts$x)), 2)
  expect_equal(diff(range(verts$y)), 6)
})

test_that("rectangle_vertices handles equal w and h (becomes square)", {
  verts <- rectangle_vertices(0, 0, 2, 2)

  expect_equal(diff(range(verts$x)), diff(range(verts$y)))
})

# ============================================
# TRIANGLE VERTICES
# ============================================

test_that("triangle_vertices generates correct structure", {
  verts <- triangle_vertices(0, 0, 1)

  expect_true(is.list(verts))
  expect_equal(length(verts$x), 3)
  expect_equal(length(verts$y), 3)
})

test_that("triangle_vertices has vertex at top", {
  verts <- triangle_vertices(0, 0, 1)

  # Top vertex should be at (0, 1) - pi/2 angle
  max_y_idx <- which.max(verts$y)
  expect_equal(verts$x[max_y_idx], 0, tolerance = 0.001)
  expect_equal(verts$y[max_y_idx], 1, tolerance = 0.001)
})

test_that("triangle_vertices respects radius", {
  r <- 2.5
  verts <- triangle_vertices(0, 0, r)

  # All vertices should be at distance r from center
  distances <- sqrt(verts$x^2 + verts$y^2)
  expect_true(all(abs(distances - r) < 0.001))
})

test_that("triangle_vertices respects center coordinates", {
  verts <- triangle_vertices(4, -3, 1)

  expect_equal(mean(verts$x), 4, tolerance = 0.001)
  expect_equal(mean(verts$y), -3, tolerance = 0.001)
})

# ============================================
# DIAMOND VERTICES
# ============================================

test_that("diamond_vertices generates correct structure", {
  verts <- diamond_vertices(0, 0, 1)

  expect_true(is.list(verts))
  expect_equal(length(verts$x), 4)
  expect_equal(length(verts$y), 4)
})

test_that("diamond_vertices has vertices on axes", {
  r <- 1
  verts <- diamond_vertices(0, 0, r)

  # Should have vertices at (r,0), (0,r), (-r,0), (0,-r)
  corners <- data.frame(x = verts$x, y = verts$y)
  expect_true(any(abs(corners$x - r) < 0.001 & abs(corners$y) < 0.001))
  expect_true(any(abs(corners$x) < 0.001 & abs(corners$y - r) < 0.001))
  expect_true(any(abs(corners$x - (-r)) < 0.001 & abs(corners$y) < 0.001))
  expect_true(any(abs(corners$x) < 0.001 & abs(corners$y - (-r)) < 0.001))
})

test_that("diamond_vertices respects center coordinates", {
  verts <- diamond_vertices(2, 3, 1)

  expect_equal(mean(verts$x), 2, tolerance = 0.001)
  expect_equal(mean(verts$y), 3, tolerance = 0.001)
})

# ============================================
# PENTAGON VERTICES
# ============================================

test_that("pentagon_vertices generates correct structure", {
  verts <- pentagon_vertices(0, 0, 1)

  expect_true(is.list(verts))
  expect_equal(length(verts$x), 5)
  expect_equal(length(verts$y), 5)
})

test_that("pentagon_vertices has vertex at top", {
  verts <- pentagon_vertices(0, 0, 1)

  # First vertex should be at top (angle pi/2)
  max_y_idx <- which.max(verts$y)
  expect_equal(verts$x[max_y_idx], 0, tolerance = 0.001)
  expect_equal(verts$y[max_y_idx], 1, tolerance = 0.001)
})

test_that("pentagon_vertices respects radius", {
  r <- 3
  verts <- pentagon_vertices(0, 0, r)

  distances <- sqrt(verts$x^2 + verts$y^2)
  expect_true(all(abs(distances - r) < 0.001))
})

# ============================================
# HEXAGON VERTICES
# ============================================

test_that("hexagon_vertices generates correct structure", {
  verts <- hexagon_vertices(0, 0, 1)

  expect_true(is.list(verts))
  expect_equal(length(verts$x), 6)
  expect_equal(length(verts$y), 6)
})

test_that("hexagon_vertices has flat sides on top and bottom", {
  verts <- hexagon_vertices(0, 0, 1)

  # First vertex should be at angle 0 (rightmost point)
  expect_true(any(abs(verts$x - 1) < 0.001 & abs(verts$y) < 0.001))
})

test_that("hexagon_vertices respects radius", {
  r <- 2.5
  verts <- hexagon_vertices(0, 0, r)

  distances <- sqrt(verts$x^2 + verts$y^2)
  expect_true(all(abs(distances - r) < 0.001))
})

test_that("hexagon_vertices respects center coordinates", {
  verts <- hexagon_vertices(-1, 2, 1)

  expect_equal(mean(verts$x), -1, tolerance = 0.001)
  expect_equal(mean(verts$y), 2, tolerance = 0.001)
})

# ============================================
# STAR VERTICES
# ============================================

test_that("star_vertices generates correct structure with defaults", {
  verts <- star_vertices(0, 0, 1)

  expect_true(is.list(verts))
  # 5 points * 2 = 10 vertices
  expect_equal(length(verts$x), 10)
  expect_equal(length(verts$y), 10)
})

test_that("star_vertices respects n_points parameter", {
  for (n_points in c(3, 4, 5, 6, 8)) {
    verts <- star_vertices(0, 0, 1, n_points = n_points)
    expect_equal(length(verts$x), n_points * 2,
                 info = paste("Failed for n_points =", n_points))
  }
})

test_that("star_vertices respects inner_ratio parameter", {
  r <- 2
  inner_ratio <- 0.3
  verts <- star_vertices(0, 0, r, n_points = 5, inner_ratio = inner_ratio)

  # Outer vertices should be at distance r
  # Inner vertices should be at distance r * inner_ratio
  distances <- sqrt(verts$x^2 + verts$y^2)
  outer_dist <- distances[seq(1, length(distances), 2)]
  inner_dist <- distances[seq(2, length(distances), 2)]

  expect_true(all(abs(outer_dist - r) < 0.001))
  expect_true(all(abs(inner_dist - r * inner_ratio) < 0.001))
})

test_that("star_vertices handles inner_ratio = 0.5", {
  verts <- star_vertices(0, 0, 1, inner_ratio = 0.5)

  expect_equal(length(verts$x), 10)
})

test_that("star_vertices first vertex is at top", {
  verts <- star_vertices(0, 0, 1, n_points = 5)

  # First vertex should be at angle pi/2 (top)
  expect_equal(verts$x[1], 0, tolerance = 0.001)
  expect_equal(verts$y[1], 1, tolerance = 0.001)
})

# ============================================
# HEART VERTICES
# ============================================

test_that("heart_vertices generates correct structure with defaults", {
  verts <- heart_vertices(0, 0, 1)

  expect_true(is.list(verts))
  expect_equal(length(verts$x), 100)  # default n = 100
  expect_equal(length(verts$y), 100)
})

test_that("heart_vertices respects n parameter", {
  for (n in c(50, 100, 200)) {
    verts <- heart_vertices(0, 0, 1, n = n)
    expect_equal(length(verts$x), n, info = paste("Failed for n =", n))
  }
})

test_that("heart_vertices is centered approximately at given coordinates", {
  verts <- heart_vertices(3, -2, 1)

  # Heart shape is not perfectly centered, but should be close
  expect_equal(mean(verts$x), 3, tolerance = 0.1)
  # Heart y is offset due to shape, center should be near given y
  expect_true(abs(mean(verts$y) - (-2)) < 0.5)
})

test_that("heart_vertices respects scale", {
  verts_small <- heart_vertices(0, 0, 0.5)
  verts_large <- heart_vertices(0, 0, 2)

  # Larger scale should have larger extent
  extent_small <- max(abs(c(verts_small$x, verts_small$y)))
  extent_large <- max(abs(c(verts_large$x, verts_large$y)))

  expect_true(extent_large > extent_small)
})

# ============================================
# ELLIPSE VERTICES
# ============================================

test_that("ellipse_vertices generates correct structure", {
  verts <- ellipse_vertices(0, 0, 2, 1)

  expect_true(is.list(verts))
  expect_equal(length(verts$x), 50)  # default n = 50
  expect_equal(length(verts$y), 50)
})

test_that("ellipse_vertices respects rx and ry", {
  rx <- 3
  ry <- 1
  verts <- ellipse_vertices(0, 0, rx, ry)

  # Check x extent is rx
  expect_equal(max(verts$x), rx, tolerance = 0.01)
  expect_equal(min(verts$x), -rx, tolerance = 0.01)

  # Check y extent is ry
  expect_equal(max(verts$y), ry, tolerance = 0.01)
  expect_equal(min(verts$y), -ry, tolerance = 0.01)
})

test_that("ellipse_vertices respects n parameter", {
  verts <- ellipse_vertices(0, 0, 1, 1, n = 100)
  expect_equal(length(verts$x), 100)
})

test_that("ellipse_vertices with equal radii is a circle", {
  r <- 1.5
  verts <- ellipse_vertices(0, 0, r, r, n = 100)

  # All points should be at distance r
  distances <- sqrt(verts$x^2 + verts$y^2)
  expect_true(all(abs(distances - r) < 0.01))
})

test_that("ellipse_vertices respects center coordinates", {
  verts <- ellipse_vertices(5, -3, 2, 1)

  expect_equal(mean(verts$x), 5, tolerance = 0.01)
  expect_equal(mean(verts$y), -3, tolerance = 0.01)
})

# ============================================
# CROSS VERTICES
# ============================================

test_that("cross_vertices generates correct structure", {
  verts <- cross_vertices(0, 0, 1)

  expect_true(is.list(verts))
  expect_equal(length(verts$x), 12)  # 12-point cross
  expect_equal(length(verts$y), 12)
})

test_that("cross_vertices respects thickness parameter", {
  r <- 1
  thickness <- 0.3
  verts <- cross_vertices(0, 0, r, thickness = thickness)

  # The arm should extend to r
  expect_true(max(verts$x) == r)
  expect_true(max(verts$y) == r)
  expect_true(min(verts$x) == -r)
  expect_true(min(verts$y) == -r)
})

test_that("cross_vertices handles different thickness values", {
  for (thickness in c(0.1, 0.3, 0.5, 0.8)) {
    verts <- cross_vertices(0, 0, 1, thickness = thickness)
    expect_equal(length(verts$x), 12,
                 info = paste("Failed for thickness =", thickness))
  }
})

test_that("cross_vertices is symmetric", {
  verts <- cross_vertices(0, 0, 1, thickness = 0.3)

  # Should be symmetric about both axes
  expect_equal(sort(abs(verts$x)), sort(abs(verts$x)))
  expect_equal(sort(abs(verts$y)), sort(abs(verts$y)))
})

test_that("cross_vertices respects center coordinates", {
  verts <- cross_vertices(2, -1, 1)

  expect_equal(mean(verts$x), 2, tolerance = 0.001)
  expect_equal(mean(verts$y), -1, tolerance = 0.001)
})

# ============================================
# REGULAR POLYGON VERTICES
# ============================================

test_that("regular_polygon_vertices generates correct number of vertices", {
  for (n in c(3, 4, 5, 6, 7, 8, 10, 12)) {
    verts <- regular_polygon_vertices(0, 0, 1, n)
    expect_equal(length(verts$x), n, info = paste("Failed for n =", n))
    expect_equal(length(verts$y), n, info = paste("Failed for n =", n))
  }
})

test_that("regular_polygon_vertices default rotation places vertex at top", {
  verts <- regular_polygon_vertices(0, 0, 1, 5)  # default rotation = pi/2

  # First vertex should be at top
  expect_equal(verts$x[1], 0, tolerance = 0.001)
  expect_equal(verts$y[1], 1, tolerance = 0.001)
})

test_that("regular_polygon_vertices respects rotation parameter", {
  r <- 1
  verts <- regular_polygon_vertices(0, 0, r, 4, rotation = 0)  # Square with vertex on right

  # First vertex should be on the right (angle 0)
  expect_equal(verts$x[1], r, tolerance = 0.001)
  expect_equal(verts$y[1], 0, tolerance = 0.001)
})

test_that("regular_polygon_vertices respects radius", {
  r <- 2.5
  verts <- regular_polygon_vertices(0, 0, r, 6)

  distances <- sqrt(verts$x^2 + verts$y^2)
  expect_true(all(abs(distances - r) < 0.001))
})

test_that("regular_polygon_vertices respects center coordinates", {
  verts <- regular_polygon_vertices(3, -2, 1, 5)

  expect_equal(mean(verts$x), 3, tolerance = 0.001)
  expect_equal(mean(verts$y), -2, tolerance = 0.001)
})

test_that("regular_polygon_vertices with n=3 equals triangle_vertices", {
  verts_reg <- regular_polygon_vertices(0, 0, 1, 3, rotation = pi/2)
  verts_tri <- triangle_vertices(0, 0, 1)

  expect_equal(verts_reg$x, verts_tri$x, tolerance = 0.001)
  expect_equal(verts_reg$y, verts_tri$y, tolerance = 0.001)
})

# ============================================
# INSET POLYGON VERTICES
# ============================================

test_that("inset_polygon_vertices generates correct structure", {
  outer <- list(x = c(-1, 1, 1, -1), y = c(-1, -1, 1, 1))
  inner <- inset_polygon_vertices(outer, 0.5)

  expect_true(is.list(inner))
  expect_equal(length(inner$x), length(outer$x))
  expect_equal(length(inner$y), length(outer$y))
})

test_that("inset_polygon_vertices creates smaller polygon", {
  outer <- list(x = c(-1, 1, 1, -1), y = c(-1, -1, 1, 1))
  inner <- inset_polygon_vertices(outer, 0.5)

  # Inner polygon should be smaller
  expect_true(max(abs(inner$x)) < max(abs(outer$x)))
  expect_true(max(abs(inner$y)) < max(abs(outer$y)))
})

test_that("inset_polygon_vertices with ratio 0 collapses to center", {
  outer <- list(x = c(-1, 1, 1, -1), y = c(-1, -1, 1, 1))
  inner <- inset_polygon_vertices(outer, 0)

  # All points should be at the centroid
  expect_equal(inner$x, rep(mean(outer$x), length(outer$x)))
  expect_equal(inner$y, rep(mean(outer$y), length(outer$y)))
})

test_that("inset_polygon_vertices with ratio 1 equals original", {
  outer <- list(x = c(-1, 1, 1, -1), y = c(-1, -1, 1, 1))
  inner <- inset_polygon_vertices(outer, 1)

  expect_equal(inner$x, outer$x)
  expect_equal(inner$y, outer$y)
})

test_that("inset_polygon_vertices preserves shape", {
  # Triangle
  outer <- list(x = c(0, 1, 0.5), y = c(0, 0, 1))
  inner <- inset_polygon_vertices(outer, 0.5)

  # Should still have 3 vertices
  expect_equal(length(inner$x), 3)
})

test_that("inset_polygon_vertices handles non-centered polygon", {
  outer <- list(x = c(4, 6, 6, 4), y = c(4, 4, 6, 6))  # Square centered at (5,5)
  inner <- inset_polygon_vertices(outer, 0.5)

  # Inner centroid should match outer centroid
  expect_equal(mean(inner$x), mean(outer$x))
  expect_equal(mean(inner$y), mean(outer$y))
})

# ============================================
# GET DONUT BASE VERTICES
# ============================================

test_that("get_donut_base_vertices returns vertices for circle", {
  verts <- get_donut_base_vertices("circle", 0, 0, 1)

  expect_true(is.list(verts))
  expect_equal(length(verts$x), 100)  # n = 100 for donut circle
})

test_that("get_donut_base_vertices returns vertices for square", {
  verts <- get_donut_base_vertices("square", 0, 0, 1)

  expect_equal(length(verts$x), 4)
})

test_that("get_donut_base_vertices returns vertices for rectangle", {
  verts <- get_donut_base_vertices("rectangle", 0, 0, 1)

  expect_equal(length(verts$x), 4)
  # Rectangle has r * 0.7 height
  expect_equal(max(abs(verts$y)), 0.7, tolerance = 0.001)
})

test_that("get_donut_base_vertices returns vertices for triangle", {
  verts <- get_donut_base_vertices("triangle", 0, 0, 1)

  expect_equal(length(verts$x), 3)
})

test_that("get_donut_base_vertices returns vertices for diamond", {
  verts <- get_donut_base_vertices("diamond", 0, 0, 1)

  expect_equal(length(verts$x), 4)
})

test_that("get_donut_base_vertices returns vertices for pentagon", {
  verts <- get_donut_base_vertices("pentagon", 0, 0, 1)

  expect_equal(length(verts$x), 5)
})

test_that("get_donut_base_vertices returns vertices for hexagon", {
  verts <- get_donut_base_vertices("hexagon", 0, 0, 1)

  expect_equal(length(verts$x), 6)
})

test_that("get_donut_base_vertices defaults to circle for unknown shape", {
  verts <- get_donut_base_vertices("unknown_shape", 0, 0, 1)

  # Should fall back to circle with n = 100
  expect_equal(length(verts$x), 100)
})

test_that("get_donut_base_vertices respects center and radius", {
  verts <- get_donut_base_vertices("hexagon", 5, -3, 2)

  expect_equal(mean(verts$x), 5, tolerance = 0.001)
  expect_equal(mean(verts$y), -3, tolerance = 0.001)
})

# ============================================
# GEAR VERTICES
# ============================================

test_that("gear_vertices generates correct structure with defaults", {
  verts <- gear_vertices(0, 0, 1)

  expect_true(is.list(verts))
  # 8 teeth * 8 points per tooth = 64 vertices
  expect_equal(length(verts$x), 64)
  expect_equal(length(verts$y), 64)
})

test_that("gear_vertices respects n_teeth parameter", {
  for (n_teeth in c(4, 6, 8, 10, 12)) {
    verts <- gear_vertices(0, 0, 1, n_teeth = n_teeth)
    expected_pts <- n_teeth * 8
    expect_equal(length(verts$x), expected_pts,
                 info = paste("Failed for n_teeth =", n_teeth))
  }
})

test_that("gear_vertices is centered at given coordinates", {
  verts <- gear_vertices(3, -2, 1)

  expect_equal(mean(verts$x), 3, tolerance = 0.1)
  expect_equal(mean(verts$y), -2, tolerance = 0.1)
})

test_that("gear_vertices respects outer radius", {
  r <- 2
  verts <- gear_vertices(0, 0, r)

  # Max distance should be approximately inner_r + tooth_height = 0.65*r + 0.25*r = 0.9*r
  distances <- sqrt(verts$x^2 + verts$y^2)
  expect_true(max(distances) <= r * 0.91)
  expect_true(max(distances) >= r * 0.8)
})

test_that("gear_vertices has varying radii for teeth", {
  verts <- gear_vertices(0, 0, 1, n_teeth = 8)

  distances <- sqrt(verts$x^2 + verts$y^2)
  # Should have both inner and outer radii
  expect_true(length(unique(round(distances, 2))) > 1)
})

# ============================================
# CLOUD VERTICES
# ============================================

test_that("cloud_vertices generates correct structure with defaults", {
  verts <- cloud_vertices(0, 0, 1)

  expect_true(is.list(verts))
  expect_equal(length(verts$x), 100)  # default n = 100
  expect_equal(length(verts$y), 100)
})

test_that("cloud_vertices respects n parameter", {
  verts <- cloud_vertices(0, 0, 1, n = 50)

  expect_equal(length(verts$x), 50)
})

test_that("cloud_vertices is approximately centered at given coordinates", {
  verts <- cloud_vertices(2, -1, 1)

  # Cloud has some offset, but should be close
  expect_equal(mean(verts$x), 2, tolerance = 0.2)
  # y has offset of r * 0.1
  expect_equal(mean(verts$y), -1 + 0.1, tolerance = 0.2)
})

test_that("cloud_vertices has bumpy profile", {
  verts <- cloud_vertices(0, 0, 1, n = 100)

  distances <- sqrt(verts$x^2 + (verts$y - 0.1)^2)
  # Cloud should have varying radii due to sin modulation
  expect_true(sd(distances) > 0.01)
})

# ============================================
# BRAIN VERTICES
# ============================================

test_that("brain_vertices generates correct structure with defaults", {
  verts <- brain_vertices(0, 0, 1)

  expect_true(is.list(verts))
  expect_equal(length(verts$x), 80)  # default n = 80
  expect_equal(length(verts$y), 80)
})

test_that("brain_vertices respects n parameter", {
  verts <- brain_vertices(0, 0, 1, n = 50)

  expect_equal(length(verts$x), 50)
})

test_that("brain_vertices is centered at given coordinates", {
  verts <- brain_vertices(3, -2, 1)

  expect_equal(mean(verts$x), 3, tolerance = 0.1)
  expect_equal(mean(verts$y), -2, tolerance = 0.1)
})

test_that("brain_vertices has wrinkled profile", {
  verts <- brain_vertices(0, 0, 1, n = 100)

  # Calculate effective radii (accounting for y scaling of 0.85)
  distances <- sqrt(verts$x^2 + (verts$y / 0.85)^2)
  # Brain should have varying radii due to sin modulation
  expect_true(sd(distances) > 0.01)
})

test_that("brain_vertices respects radius scale", {
  verts_small <- brain_vertices(0, 0, 0.5)
  verts_large <- brain_vertices(0, 0, 2)

  extent_small <- max(abs(c(verts_small$x, verts_small$y)))
  extent_large <- max(abs(c(verts_large$x, verts_large$y)))

  expect_true(extent_large > extent_small)
})

# ============================================
# GET SHAPE VERTICES - Main Dispatch Function
# ============================================

test_that("get_shape_vertices returns vertices for all standard shapes", {
  shapes <- c("circle", "square", "rectangle", "triangle", "diamond",
              "pentagon", "hexagon", "star", "heart", "ellipse", "cross",
              "gear", "cloud", "brain")

  for (shape in shapes) {
    verts <- get_shape_vertices(shape, 0, 0, 1)
    expect_true(is.list(verts), info = paste("Failed for shape:", shape))
    expect_true("x" %in% names(verts), info = paste("Missing x for shape:", shape))
    expect_true("y" %in% names(verts), info = paste("Missing y for shape:", shape))
    expect_true(length(verts$x) > 0, info = paste("Empty x for shape:", shape))
    expect_equal(length(verts$x), length(verts$y),
                 info = paste("x/y length mismatch for shape:", shape))
  }
})

test_that("get_shape_vertices defaults to circle for unknown shape", {
  verts <- get_shape_vertices("unknown_shape_xyz", 0, 0, 1)

  # Should fall back to circle with default n = 50
  expect_equal(length(verts$x), 50)
})

test_that("get_shape_vertices handles r2 parameter for rectangle", {
  verts <- get_shape_vertices("rectangle", 0, 0, 2, r2 = 1)

  # Width should be 2*2=4, height should be 2*1=2
  expect_equal(diff(range(verts$x)), 4)
  expect_equal(diff(range(verts$y)), 2)
})

test_that("get_shape_vertices handles r2 parameter for ellipse", {
  verts <- get_shape_vertices("ellipse", 0, 0, 3, r2 = 1)

  # X extent should be 2*3=6, Y extent should be 2*1=2
  expect_equal(diff(range(verts$x)), 6, tolerance = 0.1)
  expect_equal(diff(range(verts$y)), 2, tolerance = 0.1)
})

test_that("get_shape_vertices uses r for r2 when r2 is NULL", {
  verts <- get_shape_vertices("ellipse", 0, 0, 2, r2 = NULL)

  # Should become a circle (equal extents)
  expect_equal(diff(range(verts$x)), diff(range(verts$y)), tolerance = 0.1)
})

test_that("get_shape_vertices passes ... to star_vertices", {
  verts <- get_shape_vertices("star", 0, 0, 1, n_points = 6, inner_ratio = 0.5)

  # 6 points * 2 = 12 vertices
  expect_equal(length(verts$x), 12)
})

test_that("get_shape_vertices passes ... to cross_vertices", {
  verts <- get_shape_vertices("cross", 0, 0, 1, thickness = 0.5)

  expect_equal(length(verts$x), 12)
})

test_that("get_shape_vertices passes ... to gear_vertices", {
  verts <- get_shape_vertices("gear", 0, 0, 1, n_teeth = 6)

  # 6 teeth * 8 points per tooth = 48 vertices
  expect_equal(length(verts$x), 48)
})

# ============================================
# GRAPHICAL TESTS - Rendering polygon shapes
# ============================================

test_that("circle_vertices can be rendered with polygon()", {
  with_temp_png({
    plot(0, 0, xlim = c(-2, 2), ylim = c(-2, 2), type = "n", asp = 1)
    verts <- circle_vertices(0, 0, 1)
    polygon(verts$x, verts$y, col = "lightblue", border = "navy")
  })
  expect_true(TRUE)
})

test_that("star_vertices can be rendered with polygon()", {
  with_temp_png({
    plot(0, 0, xlim = c(-2, 2), ylim = c(-2, 2), type = "n", asp = 1)
    verts <- star_vertices(0, 0, 1, n_points = 5)
    polygon(verts$x, verts$y, col = "gold", border = "orange")
  })
  expect_true(TRUE)
})

test_that("heart_vertices can be rendered with polygon()", {
  with_temp_png({
    plot(0, 0, xlim = c(-2, 2), ylim = c(-2, 2), type = "n", asp = 1)
    verts <- heart_vertices(0, 0, 1)
    polygon(verts$x, verts$y, col = "red", border = "darkred")
  })
  expect_true(TRUE)
})

test_that("gear_vertices can be rendered with polygon()", {
  with_temp_png({
    plot(0, 0, xlim = c(-2, 2), ylim = c(-2, 2), type = "n", asp = 1)
    verts <- gear_vertices(0, 0, 1, n_teeth = 8)
    polygon(verts$x, verts$y, col = "gray50", border = "black")
  })
  expect_true(TRUE)
})

test_that("cloud_vertices can be rendered with polygon()", {
  with_temp_png({
    plot(0, 0, xlim = c(-2, 2), ylim = c(-2, 2), type = "n", asp = 1)
    verts <- cloud_vertices(0, 0, 1)
    polygon(verts$x, verts$y, col = "white", border = "gray")
  })
  expect_true(TRUE)
})

test_that("brain_vertices can be rendered with polygon()", {
  with_temp_png({
    plot(0, 0, xlim = c(-2, 2), ylim = c(-2, 2), type = "n", asp = 1)
    verts <- brain_vertices(0, 0, 1)
    polygon(verts$x, verts$y, col = "pink", border = "maroon")
  })
  expect_true(TRUE)
})

test_that("inset_polygon_vertices can create donut ring", {
  with_temp_png({
    plot(0, 0, xlim = c(-2, 2), ylim = c(-2, 2), type = "n", asp = 1)
    outer <- hexagon_vertices(0, 0, 1)
    inner <- inset_polygon_vertices(outer, 0.6)

    # Draw outer polygon
    polygon(outer$x, outer$y, col = "steelblue", border = "navy")
    # Draw inner polygon (covers center)
    polygon(inner$x, inner$y, col = "white", border = "navy")
  })
  expect_true(TRUE)
})

test_that("multiple shapes can be rendered together", {
  with_temp_png({
    plot(0, 0, xlim = c(-6, 6), ylim = c(-4, 4), type = "n", asp = 1)

    # Row 1: Basic shapes
    shapes <- c("circle", "square", "triangle", "diamond")
    xs <- c(-4.5, -1.5, 1.5, 4.5)

    for (i in seq_along(shapes)) {
      verts <- get_shape_vertices(shapes[i], xs[i], 2, 1)
      polygon(verts$x, verts$y, col = rainbow(4)[i], border = "black")
    }

    # Row 2: Polygon shapes
    shapes2 <- c("pentagon", "hexagon", "star", "cross")
    for (i in seq_along(shapes2)) {
      verts <- get_shape_vertices(shapes2[i], xs[i], -2, 1)
      polygon(verts$x, verts$y, col = rainbow(4, start = 0.5)[i], border = "black")
    }
  })
  expect_true(TRUE)
})

# ============================================
# EDGE CASES AND NUMERIC PRECISION
# ============================================

test_that("polygon vertices handle very small radius", {
  for (shape in c("circle", "square", "triangle", "hexagon")) {
    verts <- get_shape_vertices(shape, 0, 0, 0.001)
    expect_true(all(is.finite(verts$x)), info = paste("Non-finite x for shape:", shape))
    expect_true(all(is.finite(verts$y)), info = paste("Non-finite y for shape:", shape))
  }
})

test_that("polygon vertices handle very large radius", {
  for (shape in c("circle", "square", "triangle", "hexagon")) {
    verts <- get_shape_vertices(shape, 0, 0, 1000)
    expect_true(all(is.finite(verts$x)), info = paste("Non-finite x for shape:", shape))
    expect_true(all(is.finite(verts$y)), info = paste("Non-finite y for shape:", shape))
  }
})

test_that("polygon vertices handle zero radius", {
  verts <- circle_vertices(0, 0, 0, n = 50)

  # All points should be at origin
  expect_true(all(verts$x == 0))
  expect_true(all(verts$y == 0))
})

test_that("polygon vertices handle large coordinates", {
  verts <- circle_vertices(1e6, -1e6, 1)

  expect_equal(mean(verts$x), 1e6, tolerance = 0.01)
  expect_equal(mean(verts$y), -1e6, tolerance = 0.01)
})

test_that("star_vertices handles extreme inner_ratio values", {
  # Very small inner ratio
  verts <- star_vertices(0, 0, 1, inner_ratio = 0.01)
  expect_equal(length(verts$x), 10)

  # Inner ratio close to 1
  verts <- star_vertices(0, 0, 1, inner_ratio = 0.99)
  expect_equal(length(verts$x), 10)
})

test_that("cross_vertices handles extreme thickness values", {
  # Very thin cross
  verts <- cross_vertices(0, 0, 1, thickness = 0.01)
  expect_equal(length(verts$x), 12)

  # Very thick cross (arms touch)
  verts <- cross_vertices(0, 0, 1, thickness = 1)
  expect_equal(length(verts$x), 12)
})

Try the cograph package in your browser

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

cograph documentation built on April 1, 2026, 1:07 a.m.