How can I draw the contour of a segment - r

I am using the R function segments and would like to know how I can draw "around" the segment (the contour), in say black.
plot(0)
segments(.9,.1,.8,.3, lwd=10, lend='square', col='pink')
Here I would get a black rectangle around the pink segment

You can draw it twice, first in black, large (lwd=12), then in pink, smaller (lwd=10).
plot(0)
segments(.9,.1,.8,.3, lwd=12, lend='square', col='black')
segments(.9,.1,.8,.3, lwd=10, lend='square', col='pink')

This is pretty messy, but I threw it together anyway.
draw.rect <- function(x1=0.9,y1=0.1,x2=0.8,y2=0.3,width=0.05){
ang <- atan((y2-y1)/(x2-x1))
xshift <- width*sin(ang)
yshift <- width*cos(ang)
polygon(x=c(x1,x2,x2-xshift,x1-xshift),y=c(y1,y2,y2+yshift,y1+yshift),col="pink")
}
It would allow you to use your same coordinates. You can adjust the size of the rectangle with the width argument. I think #VincentZoonekynd has a great idea with drawing the segment twice. This rough function does not center the rectangle on the coordinates provided, although you could pretty easily adjust it to do so.

Related

Bokeh circle height is incorrect [duplicate]

I am plotting some geometry using bokeh and came across this. I am plotting a rectangle with equal sides (i.e. a square), and in that square, plotting a circle with diameter = width of the square. The circle should tangent to the square at edges, but it is not.
here is the code:
from bokeh.plotting import output_notebook, figure, show
output_notebook()
p = figure(width=500, height=500)
p.rect(0, 0, 300, 300, line_color='black')
p.circle(x=0, y=0, radius=150, line_color='black',
fill_color='grey', radius_units='data')
p.axis.minor_tick_out = 0
show(p)
Which results in this:
Is there anything I am doing wrong or could change to make the circle fit exactly in the square?
Thanks in advance,
Randall
Here's another case - just drawing a circle:
p = figure(width=500, height=500, x_range=(-150, 150), y_range=(-150, 150))
p.circle(x=0, y=0, radius=150, line_color='black',
fill_color='grey', radius_units='data')
show(p)
radius of the circle is 150 in the x direction, but not the y-direction.
I would like to report that as of Bokeh 0.12.7, this issue can now be fixed in a simpler manner.
As described in other posts, the main issue is not that the circle is not a circle, but that the square is not a square. This is due to the fact that actual area on which Bokeh draws the figure (the canvas) is usually not a square by default or even when the width and height are set to the same value. Bokeh by default will attempt to draw a figure by using up all the space on the canvas. This creates a mismatch between the data distance and the pixel distance of the plot.
As of 0.12.7, figures can now accept a match_aspect property which when set to True will will match the aspect of the data space to the pixel space of the plot.
In your example, simply adding the match_aspect = True in your figure
p = figure(width=500, height=500, match_aspect=True,
title="Circle touches all 4 sides of square")
p.rect(0, 0, 300, 300, line_color='black')
p.circle(x=0, y=0, radius=150, line_color='black',
fill_color='grey', radius_units='data')
will now produce
UPDATE: Please note new answer by #DuCorey below. As of Bokeh 0.12.7, aspect control is now available, for situations like this.
The issue is actually that the square is not square, and that is because the pixel aspect ratio and the "data" aspect ratio do not match. i.e., the distance per pixel is different in the x direction than it is in the y direction.
There are a few options:
You can use various properties to control the dimensions of the central plot area (e.g. plot border width and axis tick label orientation) You can also control you data ranges explicitly. In other words, you can make the aspect ratios match, and then the circle and rect will match
You can use absolute pixel units (e.g. size for a circle, and use a large square marker instead of rect) instead of "data" units.
Alternatively, if you want a circle that "deforms" when the aspects do not match, then your best bet is to use an ellipse with an identical width and height, which will work because in this case bokeh has two dimensions to use to measure (instead of the single radius) and can match each to the scale along each dimension independently.
(This is actually the fundamental difference that explains the behaviour: rect has two dimensions to measure independently. circle does not, it only has one, and has to arbitrarily use the x or y dimension to measure distance per pixel)
ok, based on the suggestions, I tried a few things.
Changed the orientation of the y-axis tick labels - still
had issue.
Changed various stand-offs, even moving in the tick
labels inside the plot (with a negative offset). Did not work either.
Changed the x_range and r_range in figure() to be equal tuples. Did not work either
Changes the plot_height (decreased it), and I could eventually, through rial and error, get the circle to fit in the square with a plot_height that was < plot width.
Lots of great practice controlling attributes of the plot. Time will invested.
However, the last change I tried worked the best. It was one of the first suggestions - change the plot border.
Weirdly, setting p.min_border=40, which on 0.12.6 is the default value, and voila, it appears the chart aspect ratio for a chart where plot_width=plot_height is truly 1 on the screen as well.
p = figure(plot_width=500, plot_height=500)
p.rect(0, 0, 300, 300, line_color=None)
p.circle(x=0, y=0, radius=150, line_color=None,
fill_color='lightgrey', radius_units='data')
p.min_border=40
show(p)
Before and after images showing the effect of adding p.min_border=40. Any value over ~33 appeared to be enough force the plot area to have the same screen x and y dimension - so the square was really a square (and the circle fit inside).
The reason for this is that you're creating a circular marker (or circle glyphs) and placing it at position (0, 0), while it seems like you want to create a circle centered at 0.
I think the rect here "happens" to work because it can scale correctly in both dimensions and remain a "rectangle".
Keyword Args:
radius (UnitsSpecPropertyDescriptor) : The radius values for circle markers (in "data space" units, by default). (default None)
radius_dimension (BasicPropertyDescriptor) : What dimension to measure circle radii along. (default 'x')
radius_units (Enum('screen', 'data')) : (default 'data')
I guess my point is here you've taken a shortcut by trying to use a "glyph" as your plot and specifying the units to be the data units.
If you want to create an actual circle you could do the following:
th = np.linspace(0, 2*np.pi)
r = 150
p = figure(width=500, height=500)
p.rect(0, 0, 300, 300, line_color='black')
p.line(r * np.cos(th), r * np.sin(th), line_color='black')
# p.circle(x=0, y=0, radius=150, line_color='black',
# fill_color='grey', radius_units='data')
p.axis.minor_tick_out = 0
show(p)
Notice the above is harder to fill (I didn't bother) because I'm guessing you need to define some closed polygon function while I only defined a line that happens to be a closed polygon, in this case a circle.
Not sure, but the bleu rectangle is not your rectangle.
Replace:
p.rect(0, 0, 300, 300, line_color='black')
By:
p.rect(-150, -150, 150, 150, line_color='black')

Control persp mesh tile border colors

I'm having some trouble creating a perspective plot that looks exactly how I want it to look. In particular, I am trying to get the mesh not to be visible at all. If you look at the image on the left you can see faint lines running between the tiles. I want it looking like the right image with no lines visible:
I specifically want a solution with graphics::persp or other base R function. I am not interested in 3rd party packages like rgl.
I obtained the right by using polygon and specifying a border color to match the col color. If I leave border=NA with polygon I get the same result as with persp. However, it seems persp just takes the first border value and re-uses it, unlike polygon which matches colors to the polygons.
This is the code used to generate the image:
nr <- nc <- 10
mx <- matrix(numeric(nr * nc), nr)
par(mai=numeric(4))
col <- gray((row(mx[-1,-1]) * col(mx[-1,-1])/((nr-1)*(nc-1))))
par(mfrow=c(1,3), mai=c(0, 0, .25, 0), pty='s')
persp(
mx, phi=90, theta=0, border=NA, col=col, r=1e9, zlim=c(0,1),
axes=FALSE, box=FALSE
)
title('Persp border=NA')
persp(
mx, phi=90, theta=0, border=col, col=col, r=1e9, zlim=c(0,1),
axes=FALSE, box=FALSE
)
title('Persp border=col')
plot.new()
mxpoly.x <- rbind(
c(row(mx)[-nr, -nc]), c(row(mx)[-1, -nc]), c(row(mx)[-1, -1]),
c(row(mx)[-nr, -1]), NA
)
mxpoly.y <- rbind(
c(col(mx)[-nr, -nc]), c(col(mx)[-1, -nc]), c(col(mx)[-1, -1]),
c(col(mx)[-nr, -1]), NA
)
title('Polygon')
polygon(
((mxpoly.x - 1) / (max(mxpoly.x,na.rm=TRUE) - 1)),
((mxpoly.y - 1) / (max(mxpoly.y,na.rm=TRUE) - 1)),
col=col, border=col
)
That looks like a result of antialiasing. When each cell is drawn, the background is white, so antialiasing means the border pixels are drawn in a lighter colour.
On a Mac, you can fix this by turning antialiasing off. Your first example gives
by default, but if I open the graphics device using
quartz(antialias = FALSE)
and then run the identical code, I get
Turning off antialiasing can cause jagged edges, so this might not really be an acceptable solution to your real problem if it has diagonal lines.
You might be able to get things to work by drawing the surface twice with antialiasing: the first time will show borders, the second time might still show something, but should show less. However, persp() has no add = TRUE argument, so drawing things the second time is likely to be tricky.
If you're not on a Mac, you'll need to read about the device you're using to find if it allows control of antialiasing.
Edited to add: I tried modifying the C source to the persp function
to draw the surface 2 or 3 times. The boundaries were still slightly
visible when it was drawn twice, but invisible with 3 draws.

Improve the speed of a dilating circle animation in R

I am trying to create an enlarging circle to test the Apparent Looming Threshold of fish. I want to be able to control the radius of the circle and the rate at which the radius increases.
I have the following code:
library(plotrix)
px=1:50
py=1:50
plot(px,py,type="n",xlab="",ylab="",axes = FALSE)#create blank plot
x=25#set x location of circle
y=25#set y location of circle
radius=seq(1,20,by=.5)#set sequence of radii to plot
#plot circle on top of each other to give appearance of growing circle
#at a rate of Sys.sleep(x)
for (i in radius){
draw.circle(x,y,radius = i,col="black")
Sys.sleep(1)#plot one circle per second
}
The code allows me to change the radius of the circle and the rate, however, if the Sys.sleep(x) is set below 1, the for loop takes too long to process and skips circles in the sequence. Is there alternative to a for loop that would speed up the plotting so that I could get the entire animation to run faster than 1 frame per second?
Thank you
I suspect rather plotrix than the for loop which slows down. You could just plot a normal point and let him grow literately with cex; wrap all into a function.
growCycle <- function(x) {
plot(25, 25, xlab="", ylab="", axes=FALSE, asp=1, col=1, pch=20, cex=x)
Sys.sleep(.2) # at least 5 fps
}
Note: Consider a glance into ?plot and ?par for more graphical parameters.
Then use sapply():
radius = seq(1, 20, by=.5)
sapply(radius, growCycle)
Results in an increased speed of 400 %!
Hope this helps.

How do I make planes in RGL thicker?

I will try 3D printing data to make some nice visual illustration for a binary classification example.
Here is my 3D plot:
require(rgl)
#Get example data from mtcars and normalize to range 0:1
fun_norm <- function(k){(k-min(k))/(max(k)-min(k))}
x_norm <- fun_norm(mtcars$drat)
y_norm <- fun_norm(mtcars$mpg)
z_norm <- fun_norm(mtcars$qsec)
#Plot nice big spheres with rgl that I hope will look good after 3D printing
plot3d(x_norm, y_norm, z_norm, type="s", radius = 0.02, aspect = T)
#The sticks are meant to suspend the spheres in the air
plot3d(x_norm, y_norm, z_norm, type="h", lwd = 5, aspect = T, add = T)
#Nice thick gridline that will also be printed
grid3d(c("x","y","z"), lwd = 5)
Next, I wanted to add a z=0 plane, inspired by this blog here describing the r2stl written by Ian Walker. It is supposed to be the foundation of the printed structure that holds everything together.
planes3d(a=0, b=0, c=1, d=0)
However, it has no volume, it is a thin slab with height=0. I want it to form a solid base for the printed structure, which is meant to keep everything together (check out the aforementioned blog for more details, his examples are great). How do I increase the thickness of my z=0 plane to achieve the same effect?
Here is the final step to exporting as STL:
writeSTL("test.stl")
One can view the final product really nicely using the open source Meshlab as recommended by Ian in the blog.
Additional remark: I noticed that the thin plane is also separate from the grids that I added on the -z face of the cube and is floating. This might also cause a problem when printing. How can I merge the grids with the z=0 plane? (I will be sending the STL file to a friend who will print for me, I want to make things as easy for him as possible)
You can't make a plane thicker. You can make a solid shape (extrude3d() is the function to use). It won't adapt itself to the bounding box the way a plane does, so you would need to draw it last.
For example,
example(plot3d)
bbox <- par3d("bbox")
slab <- translate3d(extrude3d(bbox[c(1,2,2,1)], bbox[c(3,3,4,4)], 0.5),
0,0, bbox[5])
shade3d(slab, col = "gray")
produces this output:
This still isn't printable (the points have no support), but it should get you started.
In the matlib package, there's a function regvec3d() that draws a vector space representation of a 2-predictor multiple regression model. The plot method for the result of the function has an argument show.base that draws the base x1-x2 plane, and draws it thicker if show.base >0.
It is a simple hack that just draws a second version of the plane at a small offset. Maybe this will be enough for your application.
if (show.base > 0) planes3d(0, 0, 1, 0, color=col.plane, alpha=0.2)
if (show.base > 1) planes3d(0, 0, 1, -.01, color=col.plane, alpha=0.1)

Join overlapping circles using R

I want to draw circles with semitransparent colour on top of a map (showing prevalence), the colour at overlap areas is too heavy to show the background image, the code am using is too long to put here, but for circles am using:
symbols(data[,c(9, 10)],
circles = 5/(pi * a.rad * cos(atan(b.rad / a.rad * tan((data[,10])*pi/180)))/180000),
fg = NULL,
bg = rgb(0, 1, 0, 0.18),
inches = F,
add = T)
I want to joint circles to plot them with the same "transparent" colour, is there some way to do this using R? Or is there any other alternative using R*?
*Please note: The maps am using are develped using R, and I can't use other program to develop them.
There's no simple way, since you're plotting a series of circles. If you don't demand the fill color be consistent, you could set the alpha value to a very small level. To get a consistent fill color, you'll have to calculate the intersection areas of the circles and plot those areas as well as the nonintersection areas (instead of plotting circles). If you have multiple overlapping circles, you can see that will become a computational nightmare.
My personal recommendation is to plot with the smallest visible alpha value so that the worst-case overlap area doesn't obscure the map. This has the side-effect of clearly indicating the density of overlap areas.

Resources