Julia Marker Width or Weight - julia

Julia 1.5
Plots.jl
Default backend - I haven't changed the backend so it should be GKS, right?
I've got the idea of marker size down, color, and opacity down. I'm trying to change the weight of the individual markers. I'm using the :x symbol. I want to make it appear bold.

AFAIK, there is no built-in default for doing that, but you can make your own marker shape and then modify its thickness yourself, e.g., with
rotate90((x, y)) = (-y, x) # function to rotate a tuple by 90 degrees
function mymarker(t=0.1) # t = relative thickness (default 0.1)
tip0 = (1.0, 1.0)
tips = [tip0, rotate90(tip0), -1 .* tip0, -1 .* rotate90(tip0)]
out = reduce(vcat, [tip .- t .* rotate90(tip), tip .+ t .* rotate90(tip), t .* tip .+ t .* rotate90(tip)] for tip in tips)
push!(out, out[1])
end
and then chose the relative thickness t and do something like,
plot(your_data..., marker = (Shape(mymarker(t)), 30, RGBA(0, 0, 0, 0.2)))
Example in a Pluto notebook:

Related

Scaling a bezier graph to starting and ending points

I have a graph like this:
And I want to be able to convert the position of P1 aka the ball you can drag around to scale with different starting and ending points on my screen.
I esentially want to make it so that the curve dot is around the same position no matter where the starting and ending positions are for the curve
So if I had a different points on my screen it would look the same as the graph
This is what I tried to do but it didn't work
function bezier.scale(startingPosition : Vector2, endingPosition : Vector2)
local screenSize = workspace.CurrentCamera.ViewportSize
local lengthX = (endingPosition.X - startingPosition.X)
local lengthY = (endingPosition.Y - startingPosition.Y)
local screenRelativeX = (screenSize.X - startingPosition.X) + lengthX
local screenRelativeY = (screenSize.Y - startingPosition.Y) + lengthY
local scaleX = (screenRelativeX / graphBackground.Size.X.Offset)
local scaleY = (screenRelativeY / graphBackground.Size.Y.Offset)
local x = (bezierPoint.Position.X.Offset * scaleX)
local y = (bezierPoint.Position.Y.Offset * scaleY)
return Vector2.new(x, y)
end
so your input is 4 2D points ... first 2 points p0,p1 are constant refer to your BEZIER start and end points and the next 2 q0,q1 are start and end point for your animation. So you want affine transform mapping between the two pairs. For that you need rotation and scale and offset...
Scale
is Easy its just ratio between line sizes so:
scale = |q1-q0| / |p1-p0|
Rotation
you can exploit dot product:
ang = acos( dot(p1-p0,q1-q0)/(|p1-p0|*|q1-q0|) )
the sign can be determined by 3D cross product (using z=0) for example:
if (cross(p1-p0,q1-q0).z >=0 ) ang=-ang;
however note that >=0 or <=0 depends on yoru coordinate system and rotation formula so it might be reversed in your case.
offset
simply apply the #1,#2 on p0 lets call the result P0 then the offset is easy:
offset = p0-P0
Putting all toghether
so transforming point p=(x,y) will be:
// #1 apply scale
x' = x*scale
y' = y*scale
// #2 apply rotation
x = x'*cos(ang) + y'*sin(ang)
y =-x'*sin(ang) + y'*cos(ang)
// #3 apply offset
x = x + offset.x
y = y + offset.y
Do not forget to use temp variables x',y' for the rotation! You might also construct 3x3 transform matrix for this instead.
For more info about transform matrices and vector math (dot and cross product included) see:
Understanding 4x4 homogenous transform matrices

what am I doing wrong in this equation?

I am trying to make a function that if a point is outside a certain circle, the point move to the point where the line passing by the center of the circle and the point collide. the code:
def inside_circle(self, pos):
if ((pos[0]-self.pos[0])**2 + (pos[1]-self.pos[1])**2) <= teleport_range**2:
return "inside"#pos
else:
pente = (pos[1]-self.pos[1])/(pos[0]-self.pos[0])
origine = pos[1]-pente*pos[0]
A = pente**2 + 1
B = 2 * -self.pos[0] + (origine+self.pos[1])*pente*2
C = self.pos[0]**2 + (origine+self.pos[1])**2 - teleport_range**2
if pos[0] > self.pos[0]:
X = (-B + math.sqrt(B**2 - 4*A*C))/(2*A)
Y = pente * X + origine
return "outside bot"#(X,Y)
elif pos[0] < self.pos[0]:
X = (-B - math.sqrt(B**2 - 4*A*C))/(2*A)
Y = pente * X + origine
return "outside top"#(X,Y)
self.pos is the center of the circle, pos is where the point I wanna check is, both are tuple
pente is the tilt of the line (its in french sorry)
origine is the Y origin of the line (french also)
teleport_range is the radius, being a constant 300
the actual return I want are commented for testing purposes
When I run it, if it is inside the circle, everythings fine but if it is outside, an error show up because it is trying to square root a negative
X = (-B + math.sqrt(B**2 - 4*A*C))/(2*A)
ValueError: math domain error
the square root in the quadratic equation is only negative when there is no collide point between the line and the circle, however, the line pass by the center of the circle and a point, so there should be two collide point.
I know there can be only one collide point when the line is a constant but I will fix that when I understand why (B**2 - 4*A*C) is negative when it shouldnt
I am not good in math, if someone could help me please, also dont hesitate to tell me if the code could be simplified without loosing clarity
thanks :)
Here is an easier, shorter, clearer way to get your desired point on the circle.
theta = math.atan2(pos[0] - self.pos[0], pos[1] - self.pos[1])
X = self.pos[0] + teleport_range * math.cos(theta)
Y = self.pos[1] + teleport_range * math.sin(theta)
This code first finds the angle of inclination of the ray from the circle's center to the point. It then uses that angle to find a point on the circle with the same angle.
Note that this code even works for points inside the circle: it finds the point on the circle with the same angle from the center. If the point is the circle's center, the desired point is ambiguous but the code returns one particular point.

Bokeh: enable hover tool on image glyphs

Is it possible to enable hover tool on the image (the glyph created by image(), image_rgba() or image_url()) so that it will display some context data when hovering on points of the image. In the documentation I found only references and examples for the hover tool for glyphs like lines or markers.
Possible workaround solution:
I think it's possible to convert the 2d signal data into a columnar Dataframe format with columns for x,y and value. And use rect glyph instead of image. But this will also require proper handling of color mapping. Particularly, handling the case when the values are real numbers instead of integers that you can pass to some color palette.
Update for bokeh version 0.12.16
Bokeh version 0.12.16 supports HoverTool for image glyphs. See:
bokeh release 0.12.16
for erlier bokeh versions:
Here is the approach I've been using for Hovering over images using bokeh.plotting.image and adding in top of it an invisible (alpha=0) bokeh.plotting.quad that has Hovering capabilities for the data coordinates. And I'm using it for images with approximately 1500 rows and 40000 columns.
# This is used for hover and taptool
imquad = p.quad(top=[y1], bottom=[y0], left=[x0], right=[x1],alpha=0)
A complete example of and image with capabilities of selecting the minimum and maximum values of the colorbar, also selecting the color_mapper is presented here: Utilities for interactive scientific plots using python, bokeh and javascript. Update: Latest bokeh already support matplotlib cmap palettes, but when I created this code, I needed to generate them from matplotlib.cm.get_cmap
In the examples shown there I decided not to show the tooltip on the image with tooltips=None inside the bokeh.models.HoverTool function. Instead I display them in a separate bokeh.models.Div glyph.
Okay, after digging more deeply into docs and examples, I'll probably answer this question by myself.
The hover effect on image (2d signal) data makes no sense in the way how this functionality is designed in Bokeh. If one needs to add some extra information attached to the data point it needs to put the data into the proper data model - the flat one.
tidying the data
Basically, one needs to tidy his data into a tabular format with x,y and value columns (see Tidy Data article by H.Wickham). Now every row represents a data point, and one can naturally add any contextual information as additional columns.
For example, the following code will do the work:
def flatten(matrix: np.ndarray,
extent: Optional[Tuple[float, float, float, float]] = None,
round_digits: Optional[int] = 0) -> pd.DataFrame:
if extent is None:
extent = (0, matrix.shape[1], 0, matrix.shape[0])
x_min, x_max, y_min, y_max = extent
df = pd.DataFrame(data=matrix)\
.stack()\
.reset_index()\
.rename(columns={'level_0': 'y', 'level_1': 'x', 0: 'value'})
df.x = df.x / df.x.max() * (x_max - x_min) + x_min
df.y = df.y / df.y.max() * (y_max - y_min) + y_min
if round_digits is not None:
df = df.round({'x': round_digits, 'y': round_digits})
return df
rect glyph and ColumnDataSource
Then, use rect glyph instead of image with x,y mapped accordingly and the value column color-mapped properly to the color aesthetics of the glyph.
color mapping for values
here you can use a min-max normalization with the following multiplication by the number of colors you want to use and the round
use bokeh builtin palettes to map from computed integer value to a particular color value.
With all being said, here's an example chart function:
def InteractiveImage(img: pd.DataFrame,
x: str,
y: str,
value: str,
width: Optional[int] = None,
height: Optional[int] = None,
color_pallete: Optional[List[str]] = None,
tooltips: Optional[List[Tuple[str]]] = None) -> Figure:
"""
Notes
-----
both x and y should be sampled with a constant rate
Parameters
----------
img
x
Column name to map on x axis coordinates
y
Column name to map on y axis coordinates
value
Column name to map color on
width
Image width
height
Image height
color_pallete
Optional. Color map to use for values
tooltips
Optional.
Returns
-------
bokeh figure
"""
if tooltips is None:
tooltips = [
(value, '#' + value),
(x, '#' + x),
(y, '#' + y)
]
if color_pallete is None:
color_pallete = bokeh.palettes.viridis(50)
x_min, x_max = img[x].min(), img[x].max()
y_min, y_max = img[y].min(), img[y].max()
if width is None:
width = 500 if height is None else int(round((x_max - x_min) / (y_max - y_min) * height))
if height is None:
height = int(round((y_max - y_min) / (x_max - x_min) * width))
img['color'] = (img[value] - img[value].min()) / (img[value].max() - img[value].min()) * (len(color_pallete) - 1)
img['color'] = img['color'].round().map(lambda x: color_pallete[int(x)])
source = ColumnDataSource(data={col: img[col] for col in img.columns})
fig = figure(width=width,
height=height,
x_range=(x_min, x_max),
y_range=(y_min, y_max),
tools='pan,wheel_zoom,box_zoom,reset,hover,save')
def sampling_period(values: pd.Series) -> float:
# #TODO think about more clever way
return next(filter(lambda x: not pd.isnull(x) and 0 < x, values.diff().round(2).unique()))
x_unit = sampling_period(img[x])
y_unit = sampling_period(img[y])
fig.rect(x=x, y=y, width=x_unit, height=y_unit, color='color', line_color='color', source=source)
fig.select_one(HoverTool).tooltips = tooltips
return fig
#### Note: however this comes with a quite high computational price
Building off of Alexander Reshytko's self-answer above, I've implemented a version that's mostly ready to go off the shelf, with some examples. It should be a bit more straightforward to modify to suit your own application, and doesn't rely on Pandas dataframes, which I don't really use or understand. Code and examples at Github: Bokeh - Image with HoverTool

Creating X and Y axis for a game

Recently I was thinking about creating my own axis x/y, especially 'x', but in that game in which I want to create it, there are no values below 0, because pointX = 0 is on left screen border.
I want to create function which will smoothly count all values depends on our game resolution X.
For example:
parameters: min value, max value, screenX, cursorPosition
if(cursorPosition == screenWidth/2) then
return 0
end
When cursor position is below screenWidth/2, function will smoothly count value between -0 and min value (min value will be, when cursor position = 0)
and the same when cursor pos is above screenWidth/2, function will smoothly count value between 0 and max value (max value will be when cursor position = our screenX)
Can anyone explain to me, how can I reach an effect like that? :)
Regards
use linear interpolation to change the dynamic range. Let assume your view has xs,ys resolution and point (0,0) is top left corner. so the dynamic range per each axis is:
x = <0,xs-1>
y = <0,ys-1>
and you want to change it to:
x' = <minx,maxx>
y' = <miny,maxy>
So do this:
x' = minx + x*(maxx-minx)/(xs-1)
y' = miny + y*(maxy-miny)/(ys-1)
and if you need to go back for any reason:
x = (x'-minx)*(xs-1)/(maxx-minx)
y = (y'-miny)*(ys-1)/(maxy-miny)
where (minx,miny) is top left corner and (maxx,maxy) is bottom right corner. If you want to change also the sign of any axis then you can as minx<maxx is not required for this so just swap the initial values so minx>maxx.
Not coding in lua but If your values are floating then beware integer rounding while mixing integers and floats together.

Quick Equation to solve Camera rotation based on parent scale

I've got a camera attached to a parent that scales causing the camera to "zoom". I want the camera to tilt more at a lower scale. I need an equation that will tilt the camera between the min and max based on the scale of the parent.
Any help would be greatly appreciated =)
See the diagram below:
Instead of scale, you need distances. Consider the variable verical distance y and the target horizontal distance x which you want to keep fixed. The angle of the camera θ is related by
θ = ATAN(y/x)*(180/π)
Given the end conditions y_1/x = TAN(20°) and y_2/x = TAN(40°) one finds that
y_2 = TAN(40°)/TAN(20°)*y_1 = 2.3054*y_1
x = COS(20°)/SIN(20°)*y_1 = 2.7474*y_1
The initial height y_1 is required to compute the horizontal distance x.
Now since s=0.1 means y(s)=y_1 and s=1.0 means y(s)=y_2 then
y(s) = 10/9*(y_2-y_1)*s+(10*y_1-y_2)/9
= y_1*10*(1-s)/9+y_1*(10*s-1)*TAN(40°)/(9*TAN(20°))
= y_1*(1.450*s+0.855)
TAN(θ) = y(s)/x
TAN(θ) = 10*(1-s)*TAN(20°)/9+(10*s-1)*TAN(40°)/9
Use this:
θ(s) = 180/π*ATAN(0.5279*s+0.3112)
With the following example values
s θ(s)
0.1 20°
0.55 31°
1.0 40°
If I'm reading it right, the Scale varies from 0.1 to 1.0, and you want the Angle to vary from 20 to 40 degrees. Right?
A simple linear formula would look like
CurrentAngle = MinAngle + (CurrentScale - MinScale) * (MaxAngle - MinAngle) / (MaxScale - MinScale)
= 20 + (CurrentScale - 0.1) * (40 - 20) / (1 - 0.1)
= 20 + (CurrentScale - 0.1) * 20 / 0.9
So if you use 0.64 as the CurrentScale, as in your example above, you'd get
= 20 + (0.64 - 0.1) * 20 / 0.9
= 32
Linear is the simplest mathematically, but if your application is animated or needs to change the angle faster on one end or the other of your scale, you may get a more polished result from using a formula with a curve to it (logarithmic, parabolic or exponential, maybe?).

Resources