Algorithm to generate RYB color wheel? - math

I am attempting to generate a color wheel, but in RYB space. I have successfully implemented this in RGB. I start off with Red (255, 0, 0) and convert it to HSL, increment the hue value, then convert back to display on screen. The details are described here: https://serennu.com/colour/rgbtohsl.php
However, my attempts at doing so with RYB have not worked so far.
From what I have read, you get a value in RGB, such as Red (255, 0, 0). Then you ASSUME that it's RYB and use the code below to convert that "RYB" value to RGB. Then, what I have done is gotten that value, converted it to HSL, incremented the Hue, and then displayed it on the screen as before.
def _cubic(t, a, b):
weight = t * t * (3 - 2*t)
return a + weight * (b - a)
def ryb_to_rgb(r, y, b): # Assumption: r, y, b in [0, 1]
# red
x0, x1 = _cubic(b, 1.0, 0.163), _cubic(b, 1.0, 0.0)
x2, x3 = _cubic(b, 1.0, 0.5), _cubic(b, 1.0, 0.2)
y0, y1 = _cubic(y, x0, x1), _cubic(y, x2, x3)
red = _cubic(r, y0, y1)
# green
x0, x1 = _cubic(b, 1.0, 0.373), _cubic(b, 1.0, 0.66)
x2, x3 = _cubic(b, 0., 0.), _cubic(b, 0.5, 0.094)
y0, y1 = _cubic(y, x0, x1), _cubic(y, x2, x3)
green = _cubic(r, y0, y1)
# blue
x0, x1 = _cubic(b, 1.0, 0.6), _cubic(b, 0.0, 0.2)
x2, x3 = _cubic(b, 0.0, 0.5), _cubic(b, 0.0, 0.0)
y0, y1 = _cubic(y, x0, x1), _cubic(y, x2, x3)
blue = _cubic(r, y0, y1)
return (red, green, blue)
I expect to get the RYB color wheel as described here: Calculating the analogous color with python
I have followed the instructions on this post, and still am getting the wrong colors. I have tested this algorithm on a linear path (not a circle) to get http://prntscr.com/o3gcjr
This looks similar to the color wheel on that post I linked to above. However, when starting the wheel from a different color, I get this http://prntscr.com/o3gp76
And in my opinion, that doesn't seem correct.

Related

GraphMakie.jl cuts off text

I would like to visualize some graphs with labeled vertices using GraphMakie.jl.
Sadly, makie doesn't seem to include the labels into its bounding box-calculation and the labels are therefore cut off. I would expect there to be some feature to add some padding to the Axis-object, but I can't find anything like this.
Minimal example:
using Graphs, GraphMakie
fig = Figure()
for i in 1:2
ax = Axis(fig[1,i])
g = wheel_graph(i+1)
graphplot!(ax, g, nlabels=["label" for _ in 1:i+1])
hidedecorations!(ax)
end
display(fig)
Things I tried that didn't work:
adding protrusions: ax.alignmode = Mixed(right = Makie.Protrusion(50))
refreshing limits: autolimits!(ax)
changing the layout gap: colgap!(fig.layout, 50)
manually overriding the size: ax.width = 400
The area inside the rectangle is determined by the plotted range of data. Therefore, to make the labels fit in the rectangle, you have to adjust the range in the "data domain", i.e., using xlims and ylims. To add a margin to these ranges, you have to first know these ranges and hence do the call to the graph layout-function manually. I've implemented a wrapper that does this and adds a margin-property to adjust the margins:
using Graphs, GraphMakie, NetworkLayout, CairoMakie
function graphplotWithMargin!(
ax::Axis,
g::SimpleGraph;
margin = (0.1, 0.1, 0.1, 0.1),
layout = Spring(),
args...)
local pos = layout(g)
graphplot!(ax, g; layout=_->pos, args...)
local x0, x1 = minimum(map(x->x[1],pos)), maximum(map(x->x[1],pos))
CairoMakie.xlims!(ax, x0 - margin[4] * (x1 - x0), x1 + margin[2] * (x1 - x0))
local y0, y1 = minimum(map(x->x[2],pos)), maximum(map(x->x[2],pos))
CairoMakie.ylims!(ax, y0 - margin[1] * (y1 - y0), y1 + margin[3] * (y1 - y0))
end
fig = Figure()
for i in 1:2
ax = Axis(fig[1,i])
g = wheel_graph(i+1)
graphplotWithMargin!(ax, g,
nlabels=["label" for _ in 1:i+1],
margin=(0.05, 0.15, 0.05, 0.05))
hidedecorations!(ax)
end
display(fig)

How to draw a half circle on a plot?

I am trying to draw some half circles on a plot, using the trigonometric functions in R.
So here is what I have :
matPoints <<- as.data.frame(cbind(X=c(-1, -(sqrt(3)/2), -(sqrt(2)/2), -0.5, 0, 0.5, sqrt(2)/2, sqrt(3)/2, 1), Y=c(0, 0.5, sqrt(2)/2, sqrt(3)/2, 1, sqrt(3)/2, sqrt(2)/2, 0.5, 0)))
plot(x = matPoints$X*W, y = matPoints$Y*W)
For the moment, it prints each point on the plot. What I want to do here is to trace a smooth line between points so it gives me a beautiful half circle of center (0, 0) and of scale W.
Any solution?
Do you mean this?
x <- seq(0, pi, length.out = 500)
W <- 3
plot(cos(x) * W, sin(x) * W, type = "l")
Since the general equation of a circle is x^2 + y^2 = r^2, you can mimic it like below as well,
r=3 # radius
x <- seq(-r,r,0.01)
y <- sqrt(r^2 - x^2)
plot(x,y,type="l")
# plot(c(x,x),c(y,-y),type="l") for a full circle.
gives,
Here's another possibility using complex numbers, and polygon to draw a closed shape.
plot(NA, xlim=c(-2,2), ylim=c(-2,2))
polygon(1i^(seq(0,2,l=100)))
Using this method you can easily change the centre, scale, rotation, fill colour etc:
plot(NA, xlim=c(-2,2), ylim=c(-2,2))
polygon(2*(1i^(seq(0,2,l=100)))*1i^.5 + .1-.3i, col="red")
polygon(1i^(seq(0,2,l=100)), col="blue")

How to calculate the area under a frequency polygon in R?

I am an struggeling to find a way of calculating the area under a frequency polygon like this:
x1 <- 1:5
y1 <- c(0.2, 0.14, 0.7, 0.11, 0.1)
plot(x1, y1, type = "l", lwd = 3)
polygon(c(1, x1, 5), c(0, y1, 0), col = "red")
points(x1, y1,cex = 2,pch = 15)
segments(x1, 0, x1, y1)
so basically the area of the red zone from 1 to 5..
any suggestion would be much appreciated!!
Many thanks
If the bottom side of your polygons are always 0 and left and right sides are vertical, then it's nothing but summing areas of a set of trapezoids:
h = x1[2:5]-x1[1:4] # heights
b1 = y1[1:4] #first bases
b2 = y1[2:5] #second bases
ta = h*(b1+b2)/2 # trapezoids
pa = sum # total area
Of course you can generalize indices to make it work with arrays of different lengthes.

How to draw the curves in an energy diagram in R?

I wrote following R script:
#energy diagram
x <- c(0.1, 0.3, 0.5, 0.7, 0.9 ) #chosen randomly, reaction axis
y <- c(-5.057920, -5.057859, -5.057887,-5.057674, -5.057919 ) #energy of the educt, intermediate, transtition states and product
plot(x,y, type="p",
xlim=c(0,1),
ylim=c(-5.058,-5.0575),
xlab="reaction axis",
ylab=expression(paste(E[el] ," / ",10^6," ",kJ/mol)),
xaxt="n" #hide x-axis
)
#h- and v-lines, so i can draw curves by hand
abline(v=seq(0,1,0.1),h=seq(-5.0600,-5.0500,0.00005),col="black",lty=1,lwd=1)
abline(h=c(-5.057920, -5.057859, -5.057887,-5.057674), col="blue", lty=1,lwd=0.7)
Is it possible to draw a curve through the points that would look like a energy diagram. An example of an energy diagram is here:
A lot could be done to streamline / vectorize this code, but for a smallish diagram this works pretty well:
# get that data
x <- c(0.1, 0.3, 0.5, 0.7, 0.9 ) # reaction axis
y <- c(-5.057920, -5.057859, -5.057887,-5.057674, -5.057919 ) # energies
I'm going to make a little Bezier curve to connect each point to the next---this way we can make sure the smooth line passes through the data, not just close to it. I'll give each point a single 'control point' to define the slope. By using the same y-values for a point and it's control point, the slope at the point will be 0. I'll call the offset between the point and the control point delta. We'll start with one point-pair:
library(Hmisc)
delta = 0.15
bezx = c(0.1, 0.1 + delta, 0.3 - delta, 0.3)
bezy = rep(y[1:2], each = 2)
plot(bezx, bezy, type = 'b', col = "gray80")
lines(bezier(bezx, bezy), lwd = 2, col = "firebrick4")
Here I plotted the points and control points in gray, and the smooth line in red so we can see what's going on.
It looks promising, let's turn it into a function that we can apply to each pair of points:
bezf = function(x1, x2, y1, y2, delta = 0.15) {
bezier(x = c(x1, x1 + delta, x2 - delta, x2), y = c(y1, y1, y2, y2))
}
You can play with the delta parameter, I think 0.1 looks pretty good.
plot(x, y, xlab = "Reaction coordinate", ylab = "E", axes = F)
box(bty = "L")
axis(side = 2)
for(i in 1:(length(x) - 1)) {
lines(bezf(x1 = x[i], x2 = x[i + 1], y1 = y[i], y2 = y[i + 1], delta = 0.1))
}
You can of course tweak the plot, add labels, and ablines as in your original. (Use my for loop with the lines command to draw only the smoothed lines.) I left the points on to show that we are passing through them, not just getting close.
I prefer plotting in ggplot2, if you do too you'll need to extract the data into a data.frame:
bezlist = list()
for (i in 1:(length(x) - 1)) {
bezlist[[i]] = bezf(x1 = x[i], x2 = x[i + 1], y1 = y[i], y2 = y[i + 1], delta = 0.1)
}
xx = unlist(lapply(bezlist, FUN = '[', 'y'))
yy = unlist(lapply(bezlist, FUN = '[', 'y'))
bezdat = data.frame(react = xx, E = yy)
library(ggplot2)
ggplot(bezdat, aes(x = react, y = E)) +
geom_line() +
labs(x = "Reaction coordinate")
You could use a spline fit. Define some points along the energy diagram, and then fit to them using a spline function. The more points that you provide, the better that your fit will be. You can check out the smooth.splines function in the stats package for one implementation of the spline fit.

Use segments function to draw lines

I am trying to plot lines between my observed value and fitted value. But somehow I keep on getting error. I am using segments to do this. Below is my script:
mdl<-lm(yld ~ year,data=asm);summary(mdl)
plot(yld ~ year,data=asm,pch=16,xlab="Year",ylab=expression(paste("Raw rice yield (kg/ha)")))
abline(mdl)
x0 <- x1 <- mdl$mdl$year
y0 <- mdl$mdl$yld
y1 <- fitted(mdl)
segments(x0, y0, x1, y1, col="red", lwd=2)
Error in segments(x0, y0, x1, y1, col = "red", lwd = 2) :
invalid first argument
Could anyone tell me what am I doing wrong here? When I run x0 it shows NULL. Same for x1 and y1 (NULL). Is this the problem and how do I correct it
Thanks

Resources