Interpolation for degree angles

approx_degrees(
  h1,
  h2,
  h = NULL,
  preset = "custom",
  direction = 1,
  digits = 10,
  verbose = FALSE,
  ...
)

Arguments

h1

numeric vector of degree angles, which should represent the "degree angles from".

h2

numeric vector of degree angles, which should represent the "degree angles to".

h

numeric or NULL, where a numeric vector is a vector of degree angles "from" that should be converted to the corresponding interpolated angle "to", When h is NULL then the object returned is a function to be called to convert a numeric vector "from" to degree angles "to".

digits

integer value indicating the number of digits of precision to use for the input h1 and h2 degree angles, used when confining to 360 degrees with h1 %% 360, and this step sometimes produces slight variations for equivalent values. For example ((12.2 %% 360) == (372.2 %% 360)) is not TRUE without rounding to 13 or fewer digits.

...

additional arguments are ignored.

Details

This function is analogous to stats::approx() except for the special case of degree angles. In this case, degrees are confined to the range [0, 360], and angle are smoothly interpolated around the degrees of a circle.

This function should work properly even when the degree angles in h2 are reversed, or offset. The only implicit requirement is that angles in "from" should be mapped to one and only one angle in "to".

Examples

h_colors <- jamba::getColorRamp(c("white", "firebrick"), n=35, trimRamp=c(1, 0));

h1 <- c(12.2, 27.3, 47.0, 66.5, 85.9, 106.3, 131.7,
   223.1, 263.2, 277.2, 307.7, 345.3, 372.2)
h2 <- seq(from=0, to=360, length.out=13)
h_from <- seq(from=0, to=360, length.out=36)[-36]
h_to <- approx_degrees(h1, h2, h_from)
par("mfrow"=c(2, 2))
display_degrees(h_from, col=h_colors)
display_degrees(h_to, col=h_colors)
plot(h_from, h_to, pch=20, col=h_colors)
par("mfrow"=c(1, 1))


h2 <- c(12.2, 27.3, 47.0, 66.5, 85.9, 106.3, 131.7,
   223.1, 263.2, 277.2, 307.7, 345.3, 372.2)
h1 <- seq(from=0, to=360, length.out=13)
h_from <- seq(from=0, to=360, length.out=36)[-36]
h_to2 <- approx_degrees(h2, h1, h_from)
par("mfrow"=c(2, 2))
display_degrees(h_from, col=h_colors)
display_degrees(h_to2, col=h_colors)
plot(h_from, h_to2, pch=20, col=h_colors)
par("mfrow"=c(1, 1))


h1 <- c(12.2, 27.3, 47.0, 66.5, 85.9, 106.3, 131.7,
   223.1, 263.2, 277.2, 307.7, 345.3, 372.2)
h2 <- rev((seq(from=0, to=360, length.out=13))[c(9:12,1:9)])
h_from <- seq(from=0, to=360, length.out=36)[-36]
h_to <- approx_degrees(h1, h2, h_from)
par("mfrow"=c(2, 2))
display_degrees(h_from, col=h_colors)
display_degrees(h_to, col=h_colors)
plot(h_from, h_to, pch=20, col=h_colors)
par("mfrow"=c(1, 1))


# apply no transform
approx_degrees(h1=0, h2=0, h=c(0, 90, 180, 270))
#> [1]   0  90 180 270

# apply 180 degree transform
approx_degrees(h1=0, h2=180, h=c(0, 90, 180, 270))
#> [1] 180 270   0  90
approx_degrees(h1=180, h2=0, h=c(0, 90, 180, 270))
#> [1] 180 270   0  90

# flip the direction
approx_degrees(h1=c(1, 360), h2=c(359, 0), h=c(0, 90, 180, 270))
#> [1]   0.0000 359.2479 359.4986 359.7493
approx_degrees(h1=c(1, 360), h2=c(359, 0)+90, h=c(0, 90, 180, 270))
#> [1] 90.00000 89.24791 89.49861 89.74930
approx_degrees(h1=c(1, 360)+90, h2=c(359, 0), h=c(0, 90, 180, 270))
#> [1] 359.7493   0.0000 359.2479 359.4986

# verify reverse h2 with break across 0-360
seq1 <- seq(from=0, to=330, by=30)
seq2 <- (rev(seq1) + 120) %% 360
seq_out <- seq(from=0, to=350, by=10);
approx_out <- approx_degrees(h1=seq1, h2=seq2, h=seq_out, verbose=TRUE)
#> ##  (11:02:41) 20Sep2023:   approx_degrees(): h1h2_df, input: 
#>     h1  h2 direction h1_diff h2_diff
#> 4    0  90         1    -330     -30
#> 3   30  60         1      30     -30
#> 2   60  30         1      30     -30
#> 1   90   0         1      30     -30
#> 12 120 330         1      30     330
#> 11 150 300         1      30     -30
#> 10 180 270         1      30     -30
#> 9  210 240         1      30     -30
#> 8  240 210         1      30     -30
#> 7  270 180         1      30     -30
#> 6  300 150         1      30     -30
#> 5  330 120         1      30     -30
#> ##  (11:02:41) 20Sep2023:   approx_degrees(): direction:1 
#> ##  (11:02:41) 20Sep2023:   approx_degrees(): which_flips:2,3,4,6,7,8,9,10,11,12 
#> ##  (11:02:41) 20Sep2023:   approx_degrees(): correcting discontinuity in h2 angles crossing above 360 
#> ##  (11:02:41) 20Sep2023:   approx_degrees(): h1h2_df, expanded: 
#>     h1   h2 direction h1_diff h2_diff
#> 4    0   90         1    -330     -30
#> 3   30  420         1      30     -30
#> 2   60  750         1      30     -30
#> 1   90 1080         1      30     -30
#> 12 120 1410         1      30     330
#> 11 150 1740         1      30     -30
#> 10 180 2070         1      30     -30
#> 9  210 2400         1      30     -30
#> 8  240 2730         1      30     -30
#> 7  270 3060         1      30     -30
#> 6  300 3390         1      30     -30
#> 5  330 3720         1      30     -30
#> ##  (11:02:41) 20Sep2023:   h1_min_span:0 
#> ##  (11:02:41) 20Sep2023:   h1_max_span:360 
#> ##  (11:02:41) 20Sep2023:   h1_range_span:360 
#> ##  (11:02:41) 20Sep2023:   h2_min_span:0 
#> ##  (11:02:41) 20Sep2023:   h2_max_span:3960 
#> ##  (11:02:41) 20Sep2023:   h2_range_span:3960 
#> ##  (11:02:41) 20Sep2023:   approx_degrees(): h1h2_df (expanded): 
#>      h1   h2 direction h1_diff h2_diff
#> 4     0   90         1      NA      NA
#> 3    30  420         1      30     330
#> 2    60  750         1      30     330
#> 1    90 1080         1      30     330
#> 12  120 1410         1      30     330
#> 11  150 1740         1      30     330
#> 10  180 2070         1      30     330
#> 9   210 2400         1      30     330
#> 8   240 2730         1      30     330
#> 7   270 3060         1      30     330
#> 6   300 3390         1      30     330
#> 5   330 3720         1      30     330
#> 41  360  450         1      30   -3270
#> 31  390  780         1      30     330
#> 21  420 1110         1      30     330
#> 13  450 1440         1      30     330
#> 121 480 1770         1      30     330
#> 111 510 2100         1      30     330
#> 101 540 2430         1      30     330
#> 91  570 2760         1      30     330
#> 81  600 3090         1      30     330
#> 71  630 3420         1      30     330
#> 61  660 3750         1      30     330
#> 51  690 4080         1      30     330
plot(seq1, seq2, pch=20, col="blue", asp=1, ylim=c(0, 360))
points(seq_out, approx_out, col="red", add=TRUE, cex=2)
#> Warning: "add" is not a graphical parameter


# verify forward h2 with break across 0-360
seq1 <- seq(from=0, to=330, by=30)
seq2 <- (seq1 + 120) %% 360
seq_out <- seq(from=0, to=350, by=10);
approx_out <- approx_degrees(h1=seq1, h2=seq2, h=seq_out, verbose=TRUE)
#> ##  (11:02:41) 20Sep2023:   approx_degrees(): h1h2_df, input: 
#>     h1  h2 direction h1_diff h2_diff
#> 5    0 120         1    -330      30
#> 6   30 150         1      30      30
#> 7   60 180         1      30      30
#> 8   90 210         1      30      30
#> 9  120 240         1      30      30
#> 10 150 270         1      30      30
#> 11 180 300         1      30      30
#> 12 210 330         1      30      30
#> 1  240   0         1      30    -330
#> 2  270  30         1      30      30
#> 3  300  60         1      30      30
#> 4  330  90         1      30      30
#> ##  (11:02:41) 20Sep2023:   approx_degrees(): direction:1 
#> ##  (11:02:41) 20Sep2023:   approx_degrees(): which_flips:9 
#> ##  (11:02:41) 20Sep2023:   approx_degrees(): correcting discontinuity in h2 angles crossing above 360 
#> ##  (11:02:41) 20Sep2023:   approx_degrees(): h1h2_df, expanded: 
#>     h1  h2 direction h1_diff h2_diff
#> 5    0 120         1    -330      30
#> 6   30 150         1      30      30
#> 7   60 180         1      30      30
#> 8   90 210         1      30      30
#> 9  120 240         1      30      30
#> 10 150 270         1      30      30
#> 11 180 300         1      30      30
#> 12 210 330         1      30      30
#> 1  240 360         1      30    -330
#> 2  270 390         1      30      30
#> 3  300 420         1      30      30
#> 4  330 450         1      30      30
#> ##  (11:02:41) 20Sep2023:   h1_min_span:0 
#> ##  (11:02:41) 20Sep2023:   h1_max_span:360 
#> ##  (11:02:41) 20Sep2023:   h1_range_span:360 
#> ##  (11:02:41) 20Sep2023:   h2_min_span:0 
#> ##  (11:02:41) 20Sep2023:   h2_max_span:720 
#> ##  (11:02:41) 20Sep2023:   h2_range_span:720 
#> ##  (11:02:41) 20Sep2023:   approx_degrees(): h1h2_df (expanded): 
#>      h1  h2 direction h1_diff h2_diff
#> 5     0 120         1      NA      NA
#> 6    30 150         1      30      30
#> 7    60 180         1      30      30
#> 8    90 210         1      30      30
#> 9   120 240         1      30      30
#> 10  150 270         1      30      30
#> 11  180 300         1      30      30
#> 12  210 330         1      30      30
#> 1   240 360         1      30      30
#> 2   270 390         1      30      30
#> 3   300 420         1      30      30
#> 4   330 450         1      30      30
#> 51  360 480         1      30      30
#> 61  390 510         1      30      30
#> 71  420 540         1      30      30
#> 81  450 570         1      30      30
#> 91  480 600         1      30      30
#> 101 510 630         1      30      30
#> 111 540 660         1      30      30
#> 121 570 690         1      30      30
#> 13  600 720         1      30      30
#> 21  630 750         1      30      30
#> 31  660 780         1      30      30
#> 41  690 810         1      30      30
plot(seq1, seq2, pch=20, col="blue", asp=1, ylim=c(0, 360))
points(seq_out, approx_out, col="red", add=TRUE, cex=2)
#> Warning: "add" is not a graphical parameter


new_h1h2 <- adjust_hue_warp(preset="dichromat", h2_shift=15, reverse_h2=TRUE)
hseq <- seq(from=0, to=350, by=15);
approx_degrees(h2=new_h1h2$h1, h1=new_h1h2$h2, h=hseq, verbose=FALSE)
#>  [1]  26.60759  81.16162 198.58586 293.17607 336.14153  19.10699  62.07245
#>  [8] 105.03791 148.00337 190.96883 233.93429 274.48661 311.31696 348.14732
#> [15]  24.97768  61.80804 291.89394 348.71212  47.51960 111.61568 175.71176
#> [22] 239.80784 303.90392   8.00000