Closed till-tietz closed 1 week ago
@macartan I think geom_curve uses something like a quadratic bezier curve to draw curves between points. Here is a basic implementation with base R
bezier_curve <- function(x0, y0, x1, y1, curvature, angle) {
## convert angle from degrees to radians
angle_rad <- angle * pi / 180
## calculate control point based on midpoint and skew
xm <- (x0 + x1) / 2
ym <- (y0 + y1) / 2
# skew direction vector
skew_vector_x <- cos(angle_rad)
skew_vector_y <- sin(angle_rad)
# control point
x_ctrl <- xm + curvature * skew_vector_x
y_ctrl <- ym + curvature * skew_vector_y
## calculate bezier curve
t <- seq(0, 1, length.out = 100)
x <- (1 - t)^2 * x0 + 2 * (1 - t) * t * x_ctrl + t^2 * x1
y <- (1 - t)^2 * y0 + 2 * (1 - t) * t * y_ctrl + t^2 * y1
points <- data.frame(x = x, y = y)
return(points)
}
curve <- bezier_curve(x0 = 0, y0 = 0, x1 = 1, y1 = 1, curvature = 1, angle = 90)
# plot
ggplot(curve, aes(x = x, y = y)) +
geom_path() +
theme_bw()
This gives us access to the actual points used to construct the curves so we can:
that looks pretty beautiful
note two other pressing issues with graphs:
point positions are not off here, hiding a set of arrows: make_model("A -> B-> C <- A; A<->C") |> plot()
arrows are sometimes going inside nodes (when the graph area is small); we don;t have the separation correct unfortunately; this is related to this: adjustment <- mean(nodesize sapply(extent, diff) / 300); but I am not sure how to calibrate this correctly; experimenting this worked fairly well but not perfectly for me: adjustment <- nodesize^.75 mean(sapply(extent, diff) / 100)
@macartan merged the changes to the plotting function into
master
(I added a penalty for edge intersections and a gravitation term for layout compactness to the force directed layout algorithm which makes the automated layout for more complicated DAGs when no node positions are passed a bit nicer)last open aesthetic issue here is deciding confounding edge curvature based on graph layout. Currently all confounding edges have positive relatively symmetric curvature when negative or more skewed curvature may be optimal depending on the layout.
I don't think this is super critical to implement but I'll keep tinkering.