-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Actually absolute sizemode for cones #3613
Comments
@etpinard is there a feature related to this? |
This is extremely annoying. I made a stack exchange post where this issue is evident. The docstring for sizeref in
But this "internal factor" is completely mysterious and I have been trying to track down where it is calculated within the code but have so far been unsuccessful. |
Here's how we do that -> https://github.com/gl-vis/gl-cone3d/blob/90c67d0933e8a01ea77c429aacf03644d0f607ba/cone.js#L193-L199 |
@etpinard Thanks for your reply. This hidden factor is definitely responsible for the varying cone size in the above linked stackexchange plot but I am not able to see how. I have two cones rotating about each other on a circle at constant angular velocity thus Otherwise, I would vote to remove the this hidden scaling factor since all it appears to do is cause grief and pain. |
@TakodaS can you share an example of one of your use cases to help us understand the challenges you're facing? The reason we're computing a scaling factor internally is to make "most" cone traces look good by default. By "good", we mean: (1) make all the cones visible, (2) avoid overlaps, (3) not slow things down too much (i.e. keep algo O(n)). Point taken, there's probably a better algorithm out there; it would be nice if you could help us find it. Thank you. |
@etpinard From the linked stackexchange post:
I am using plotly.py to animate 2 cones circling around each other. As you can see, the size of the plotted glyphs varies dramatically as the cones circle each other. I don't understand why this happens but it must be a property of the hidden internal scale factor since if I plot only 1 cone the glyph size remains constant (thus suggesting it's a function of displacement between cones).
I think the algorithm is fine but it needs to be accessible to the user. May i suggest adding an option to the attribute sizemode that deactivates this internal scaling? Perhaps call it explicit? I can't think of a better name right now. |
@etpinard One thing that would really help is if I knew how plotly interfaces with gl-vis. As far as i can see they are completely separate packages so I still have no idea where in the plotly.js source this hidden factor is calculated and I can't find where plotly calls gl-vis. If I knew where gl-cone3d comes into all of this I could try and fix it myself. EDIT: Ignore this, right in traces/cone/convert.js there it is. |
@etpinard I can also give you my use case. I'm visualising force fields where the magnitudes are varying wildly, but the direction of the force is very relevant regardless of the magnitude. What I would like to do in the end is to set all cone lengths to be constant and have manual control of the colors as well. Since I know exactly on what mesh I will calculate the force I know exactly what scaling is appropriate. It's then very annoying to reverse-engineer some automatic scaling just to apply my already known one. Related issue on the colors: #2723 |
I have recreated the problem using plotly.js without the python interface
Then plot
As you can see the size of the cones fluctuates greatly in the same way as with the plotly.py script. EDIT: @etpinard I can confirm that replacing the scale factor behaviour with
with the two major changes that 1) the autoscaling of the glyphs can be accessed and turned on or off and 2) sizeref can now take a list which specifies the relative size of each glyph. I think this will work very well and I will try to code this up and create a pull request but I am not an experienced JS programmer. |
@CarlAndersson I have uploaded a fix for Plotly.py that disables the autoscaling so that the cone size is constant and specified by |
@TakodaS Hello, I am having the same trouble with the internal scaling factor. My issue arises from having a point cloud of the uniformly sampled contour of a sphere. I then calculate the normal for each point and after some processing have several clusters of points that belong together. Those clusters shall be outputted as cones in different colours. I do this via an array of 'data' elements. One data element per cluster. As plotly generates a new view for each data element, points may be close together and probably also due to the irregularity of the grid, the size of the cones in some clusters is calculated so small that they are no more visible. I think it is highly probable that the internal scaling is the issue. Unfortunately pulling a repo outside of the official release is an option I do not wish to take. Is there a way to calculate that internal scaling factor inside my own script? If not, where can I find the lines of code in Plotly.py that are calculating this factor? Thank you! |
@msntag Take a look at Edit: why not just clone my repo and try whatever file you are running as a test? I think that will tell you whether internal scaling is a problem or not. |
@etpinard The automatic cone scaling delivers great default results. For some special use cases, however, a more fine-grained control over the scaling of glyph size and color would be extremely helpful. For instance, to prevent changes in scaling between frames in animations, as reported, or to allow fixed sizes with only color scaling. If override features cannot be easily implemented in Meanwhile, I found a quick solution for a small dataset by adding each single vector with a separate cone trace and controlling the scaling through To see that effect in an animation, have a look at the modification of the example by @TakodaS below: import numpy as np
import pandas as pd
import plotly.graph_objects as go
import plotly.offline as pl
t = np.linspace(0,2*np.pi,100)
dd = dict(
x = np.around(np.vstack((np.cos(t), np.cos(t+np.pi))),decimals=6),
y = np.around(np.vstack((np.sin(t), np.sin(t+np.pi))),decimals=6),
z = np.around(np.vstack((np.ones(len(t)),np.ones(len(t)))),decimals=6),
v = np.around(np.vstack((np.cos(t), np.cos(t+np.pi) * 1.5)),decimals=6),
u = np.around(-np.vstack((np.sin(t), np.sin(t+np.pi) * 1.5)),decimals=6),
w = np.around(np.vstack((np.zeros(len(t)),np.zeros(len(t)))),decimals=6)
)
df = ( pd.concat([pd.DataFrame({k:pd.Series(v[n,:],index=t) for k,v in dd.items() }) for n in [0,1]],
axis=1,keys=[0,1])
.rename_axis(index='t')
)
figlayout = go.Layout(
scene=dict(aspectratio=dict(x=1,y=1,z=0.25),
xaxis=dict(range=[-2,2], tickmode="linear"),
yaxis=dict(range=[-2,2], tickmode="linear"),
zaxis=dict(range=[0,5])))
# Fig3.
# current behaviour: more than one vector per trace activates automatic glyph scaling, scale varies between frames
fig3 = go.Figure(data = [go.Cone(anchor="cm",showscale=True,sizemode="scaled",sizeref=1,cmin=0,cmax=2,
**df.iloc[[0]].stack(level=0).to_dict(orient='list'))],
layout = figlayout)
fig3.frames = [ go.Frame(data = [{**{'type':'cone'}, **(df.iloc[[i]].stack(level=0).to_dict(orient='list'))}],
layout = go.Layout(title='Scaling depends on glyph positions')) for i in range(len(df)) ]
# as Fig3, but using a separate trace for each vector, bypasses automatic scaling algorythm
figx = go.Figure([go.Cone(anchor="cm",showscale=True,sizemode="scaled",sizeref=1,cmin=0,cmax=2,
**df.iloc[[0]].loc[:,n].to_dict(orient='list')) for n in [0,1]],
layout=figlayout)
fig4 = go.Figure(figx)
fig4.frames = [ go.Frame(data = [{**{'type':'cone'}, **(df.iloc[[i]].loc[:,n].to_dict(orient='list'))} for n in [0,1]],
layout = go.Layout(title='Scaling <b>independent</b> of glyph positions')) for i in range(len(df)) ]
# Same, but cone glyph-size normalized (fixed)
def unitvector_cone(ds,mag_min=0.01,sizeref=1):
uv = ( ds['u'][0]**2 + ds['v'][0]**2 + ds['w'][0]**2 )**.5
sf = np.divide(np.ones_like(uv), np.array(uv), out=np.zeros_like(uv), where=uv!=0)
sf = np.max([np.mean(sf),mag_min])*sizeref
return ({**{'type':'cone', 'sizeref':sf}, **ds})
fig5 = go.Figure(figx)
fig5.frames = [ go.Frame(data = [ unitvector_cone(df.iloc[[i]].loc[:,n].to_dict(orient='list'),
sizeref=fig5.data[0]['sizeref']) for n in [0,1]],
layout = go.Layout(title='<b>Fixed</b> glyph size, color scaling')) for i in range(len(df)) ]
pl.plot(fig3,filename='temp-fig3.html')
pl.plot(fig4,filename='temp-fig4.html')
pl.plot(fig5,filename='temp-fig5.html') I have not tested this on a large dataset. |
I came across the same issue. What I want to visualise is the direction in which a camera points along a trajectory. The cone plot seems prefect for this, but all I need is that all cones are constant size. However, this magic factor prevents makes it impossible. An option to disable the factor would be greatly appreciated. |
I also want to bump this issue. I'm trying to animate an array of wind measurements, and the cone plot would be great for showing 'wind socks'. However, the automatic scaling makes it impossible for the size of the cones to have consistent meaning (or even just be constant and act as a directional indicator). |
I also want to push this issue. I am using this to plot forces on particles and the automatic scaling makes magnitude comparison impossible. |
This issue is currently not on our roadmap in the coming months but we would happily accept a pull request if someone wants to implement it, or we would accept sponsorship to accelerate its development. |
This issue has been tagged with A community PR for this feature would certainly be welcome, but our experience is deeper features like this are difficult to complete without the Plotly maintainers leading the effort. What Sponsorship includes:
Please include the link to this issue when contacting us to discuss. |
@archmoj and @etpinard, Just wanted to point our another issue with the current implementation that I came across. The current algorithm will cause a blank plot if two consecutive cones have the same location. Since the current implementation checks the distance between two consecutive points, if there are two consecutive cones with the same position (even with different orientation), For others who wanted to get same scaling between plots, I was able to use the above issue to get same scaling. Basically, I appended my data in each plot with two additional cones that are supper close (x, y, and z) with larger but still small velocity (u, v, and w). This would cause a very small but fixed |
I'm also impacted by this. Any update on the issue ? |
I would also like to bump this issue. I am using plotly to visualize the trajectory of a vehicle in 3D. However, the design choices behind auto scaling and coloring of cones has been a constant pain for this use case. The The scaling of the cones changes as depending on the length of the trajectory, which adds more cones and causes the hidden factor to shrink the cone size automatically. I have been able to work around it by following @z--m-n 's approach of adding cones one at a time, but it is much slower, especially in longer trajectories with many cones. If we had the ability to specify the color and size of the cones manually with arguments, it would be much more convenient and faster. I would encourage the devs to reconsider adding this sort of default behavior without the ability to change them, as it unneccessarily narrows the use-cases for what is touted as a general purpose plotting library. Please consider adding a way to override these defaults. Thank you. |
Same issue here. I'm just wanting to draw some vectors with arrow heads that are all the same size. Would look much better if we could have a uniform sized cone. Hopefully, we can get an accessible setting for this in the near future. import numpy as np
import plotly.graph_objs as go
import plotly.offline as pl
def arrow3d(pt1, pt2, width=6):
x1,y1,z1 = tuple(pt1)
x2,y2,z2 = tuple(pt2)
vector = go.Scatter3d(
x = [x1,x2],
y = [y1,y2],
z = [z1,z2],
marker = dict(size = 0),
line = dict( color = "green",
width = width),
showlegend=False,
hoverinfo="none",
)
cone = go.Cone(
x=[x2],
y=[y2],
z=[z2],
u=[0.3*(x2-x1)],
v=[0.3*(y2-y1)],
w=[0.3*(z2-z1)],
anchor="tip",
hoverinfo="none",
colorscale=[[0, "red"], [1, "red"]],
showscale=False,
# sizemode="scaled",
# sizeref=0.5,
# cmin=1,
# cmax=1
)
return vector,cone
vertices = np.array(
[[ 0. , 0. , 0. ],
[-1.26797223, 0.0507989 , 1.79182315],
[ 2.12619615, 0.8189466 , 1.06064188],
[ 1.5860678 , 0.09509102, -1.32869136],
[ 0.61054188, 0.07584755, -3.09816098],
[-0.44132841, -0.01991065, -5.12563753],
[ 2.11979485, 0.21578296, -5.22348785]]
)
traces = []
pairs = [[vertices[i], vertices[i+1]] for i in range(len(vertices)-1)]
for pt1,pt2 in pairs:
vector,cone = arrow3d(pt1,pt2)
traces = traces + [vector,cone]
layout = go.Layout()
fig = go.Figure(data=traces, layout=layout)
pl.plot(fig,filename="arrow_path.html",auto_open=False,image='png',image_height=800,image_width=1500) |
Hello, I also would like to add to this topic, as this feature also inhibits me to quantitatively display my data. |
|
Yeah this cone scaling magic is black (as in not good). If you have "randomly" spaced points it becomes a pure chaos. And the docs are mind-boggeling too.
The irony is, that (1) cones invisible (2) cones overlapping and (3) slow plot with large dataset, are the reasons I don't/cant use cone plots. Back to the drawing board for a simpler and easier behavior. |
It would be nice if the cones simply could have the size of their magnitude, such that if you have 3 points and two cones going between them in order, then the tip of the first cone touches the center of the base of the second cone, which currently won't happen. As you'd get in a standard 3D space. But given the description of the sponsorship suggested, I assume this would be a big change. The docs should definitely say that the length of the cone is adjusted in a way not representing its 3D length so that people don't expect it to. |
A very hacky and generally not recommended way of manually fixing the size of cones is as follows:
This works because the vector scale calculation @etpinard alluded to uses the minimum of distances between consecutive points and vector lengths, all of which are now constant. |
I was really hoping to find a way to set the sizes of my cones by passing arrays of length same as number of cones, something a la matlab's i was able to get my direction vectors all unit length no problem, but i don't quite see the hint on adding a duplicate datapoint. i remain in search of a solution. |
I also encountered problems with this. I want to plot some reactions of a structure with vectors that are comprised of single cones for forces, double-cones for moments. I can't stack the cones to save my life... |
Hey Folks, Ironically, I found myself here after trying to optimise my code. Originally, I was doing one "cone plot" per cone. This was extremely laggy, but all the cones remained of unit size because the "mystery metric" could never be calculated. So if you are desperate for a solution that works reliably, you can do that. For my unique situation, I have 6 different sets I want to plot on the same figure. See the figure here: As you can see, there scale varies wildy. What I have ended up doing is setting a scaling factor, where I look at the original result, and then guess a "correction factor" for each colour. # Red, Orange, Yellow, Blue, Cyan, Green
scaling_factors = [0, 2, 4, 2, 5, 4]
# For Scaling Issue
# https://github.com/plotly/plotly.js/issues/3613#issuecomment-1750709712
for i, color in enumerate(ACA_COLORS.values()):
triangles = [
triangle for triangle in normals_data if triangle.color == color
]
fig.add_trace(
go.Cone(
x=[triangle.centroid[0] - com[0] for triangle in triangles],
y=[triangle.centroid[1] - com[1] for triangle in triangles],
z=[triangle.centroid[2] - com[2] for triangle in triangles],
u=[triangle.normal[0] for triangle in triangles],
v=[triangle.normal[1] for triangle in triangles],
w=[triangle.normal[2] for triangle in triangles],
colorscale=[
[0, color],
[1, color],
],
showscale=False,
sizemode="absolute",
sizeref=7 - scaling_factors[i],
anchor="tail",
)
) I understand for many usecases this is not a good fix. I also tried #3613 (comment) by appending a duplicate triangle with triangle.centroid changed slightly, but that did nothing. |
I opened #6938. |
If you continue to depend on that approach, even after the wonderful new features proposed by @archmoj, consider updating existing traces, instead of re-creating scenes with new traces, for each time step. Particularly for a fixed amount of cones, updating location and orientation of established traces is much faster than creating new ones. At least, fast enough for generating animations (example: https://doi.org/10.5446/50229) |
Is a workaround still necessary? I'm confused. |
Dear Dr. @gvwilson Hi, Cone size if we could control it correctly helps us in our plots. As, you see on this issue, many of users have problem on controlling the size of this valuable element on Plotly. That would be great if I could know your idea or if you had any comments on it. Thanks |
Thanks @BijanSeif - I'll see what I can find out. |
You could now use |
Dear @archmoj, Firstly, thanks for your contribution. Take a look at the following code: import plotly.graph_objects as go
import random as rnd
fig = go.Figure()
u=[]
v=[]
w=[]
xx=[]
yy=[]
zz=[]
for i in range(20):
u.extend([rnd.randint(0,2),None])
v.extend([rnd.randint(0,2),None])
w.extend([rnd.randint(0,2),None])
xx.extend([rnd.randint(0,20),None])
yy.extend([rnd.randint(0,20),None])
zz.extend([rnd.randint(0,20),None])
sizes=5
fig = fig.add_cone(sizemode='raw',sizeref=sizes,#colorscale=['red','red'],
x=xx, y=yy, z=zz, u=u, v=v, w=w,)
fig.show() In the following picture, the result of the above code has been shown: You can see the size of cones are not equal and depend on the location and maybe direction (velocity) of each cone, size of cones has changed and are not equal. (Plotly Version: 5.22.0) |
Since you are using random numbers as u, v, w vector, the actual size of the vector is different from each other.
Here is a codepen illustrating the raw sizemode and orthographic project. |
Dear @archmoj you are completely right, and it's working for me. I just put the final code here for others if had same issue. Again thanks. import plotly.graph_objects as go
import random as rnd
import math
fig = go.Figure()
u=[]
v=[]
w=[]
xx=[]
yy=[]
zz=[]
for i in range(20):
u0=1
v0=rnd.randint(0,2)
w0=rnd.randint(0,2)
r = math.sqrt(u0*u0+v0*v0+w0*w0)
u0 /= r
v0 /= r
w0 /= r
u.extend([u0,None])
v.extend([v0,None])
w.extend([w0,None])
xx.extend([rnd.randint(0,50),None])
yy.extend([rnd.randint(0,50),None])
zz.extend([rnd.randint(0,50),None])
sizes=5
fig = fig.add_cone(sizemode='raw',sizeref=sizes, colorscale=['red','red'],
x=xx, y=yy, z=zz, u=u, v=v, w=w,)
fig.show() Dear Dr. @gvwilson, my problem by help of @archmoj has solved, and thanks for your attention. |
Issue originally posted at plotly/plotly.py#1443
If I understand the documentation correctly there is no way to manually scale the cones in the plot units. Even when using sizemode='absolute' there is some magic rescaling of the length of the cones.
I would expect a way to use the norm of the (u,v,w) vector directly as the length of the cones, but this is not really possible unless you understand how the scaling factor is computed.
The text was updated successfully, but these errors were encountered: