pyxem / pyxem

An open-source Python library for multi-dimensional diffraction microscopy.
https://pyxem.readthedocs.io
GNU General Public License v3.0
147 stars 85 forks source link

Orientation Mapping Plotting Improvements! #1022

Open CSSFrancis opened 6 months ago

CSSFrancis commented 6 months ago

Is your feature request related to a problem? Please describe. I think that the orientation mapping code could use some really nice plotting improvements. The new markers in HyperSpy really allow us to do some fun (and slightly crazy) things because of how responsive they now are.

The easiest example is something like plotting the fit match lazily. Using the new class defined in #1018 it is easy to make some function like:

coordinates = [sl.data[:,:2] for c in sim.coordinates for sl in c] # flatten the makers for passing to the map function. 
def to_marker(data, coordinates):
    index = data[0 , 0] # gets only the best fit but should do all fits!
    coords = coordinates[int(index)]
    coords[:,1]= coords[:,1]*data[0,-1] # mirror y

    rotation = data[0, 2]
    rotation = np.deg2rad(rotation)
    rot_matrix = np.array([[np.cos(rotation), -np.sin(rotation)], [np.sin(rotation), np.cos(rotation)]])
    newxy = coords @ rot_matrix.T
    return newxy[:,::-1]

A bit more wild of an example would be to embed something like this into a plot.

image

You could do something like:

from orix.quaternion.rotation import Rotation
from orix.vector.vector3d import Vector3d
from orix.projections import StereographicProjection
s = StereographicProjection(pole=-1)
rot_reg_test = sim.rotations[0]*Vector3d.zvector()
x, y = s.vector2xy(rot_reg_test)
offsets = np.vstack((x, y)).T

mins, maxes = offsets.min(axis=0), offsets.max(axis=0)
norm_points = (offsets -((maxes+mins)/2))/ (maxes-mins)*.2
norm_points = norm_points+.85

p = hs.plot.markers.Squares(
    offsets=[[.85,.85]],
    widths=(.25,),
    units="width",
    offset_transform="axes",
    facecolor="white",
    edgecolor="black",

    )
p2 = hs.plot.markers.Points(
    offsets=norm_points,sizes=(5,),offset_transform="axes",  )

#orients =  dp.map(determine_orientation) # returns the best fit orientaitons)
dp.plot(navigator = orient.isig[0]) # just plotting the template number as the navigation
dp.add_marker(p) # add a square for an inset
dp.add_marker(p2) # add the sterographic projection to the inset
#p3 = hs.plot.markers.Points.from_signal(orients,sizes=(5,),offset_transform="axes", facecolor="r" ) # add updating best fit.
dp.add_marker(p3) 
Screenshot 2024-02-17 at 12 21 08 PM

Describe the solution you'd like @viljarjf I wonder if this is something that you would be comfortable trying to do? I can provide more guidance on how I would accomplish this but for the most part it shouldn't be terribly difficult/ I think it would be useful.

@ericpre It might also be a good idea to add something like this as an example in hyperspy. It's a bit ridiculous of a use of markers but we might want to think about making things like MarkerCombinations to handle some case where you want to combine two markers. At least in matplotlib an example of this is with the stream plot function

viljarjf commented 6 months ago

I started working on adding point and text markers: https://github.com/viljarjf/pyxem/commit/819b477a529a51aa62d2ccd6597bf72090999c26 Something like MarkerCombinations would be nice for annotated reflections, my current implementation just re-uses the coordinates for point and text markers.

One issue I ran into is the fact that the width of the text markers, (and as such their positions, since they are centered) is calculated before latex is compiled. This means that negative miller indices make the annotations completely miss the reflection, as $\bar{1}$ is much wider than the compiled $\bar{1}$.

CSSFrancis commented 6 months ago

@viljarjf Have you tried setting usetex =True? Basically the TextsColletion will go through and predict the bounding box and then center based on that. I think I tried to test this when I wrote it but honestly it's been a bit so I'm not sure.

https://github.com/hyperspy/hyperspy/blob/325f611a4323ab6e78793d735679a16f2ccd31dc/hyperspy/external/matplotlib/collections.py#L335-L347

ericpre commented 6 months ago

One issue I ran into is the fact that the width of the text markers, (and as such their positions, since they are centered) is calculated before latex is compiled. This means that negative miller indices make the annotations completely miss the reflection, as $\bar{1}$ is much wider than the compiled 1¯.

@viljarjf: can you provide a minimum example?

viljarjf commented 6 months ago

@ericpre Certainly!

from hyperspy import api as hs
import numpy as np

data = np.arange(100).reshape((2, 2, 5, 5))
signal = hs.signals.Signal2D(data)

signal.plot()

offset = [[3, 3], ]
raw_text = "$\\bar{1}$"

point_marker = hs.plot.markers.Points(offset)
text_marker = hs.plot.markers.Texts(offset, texts=[raw_text,], color="red")

signal.add_marker([point_marker, text_marker])

bilde

As @CSSFrancis pointed out, I just needed usetex=True. It took much longer to display, but the position is correct: bilde

ericpre commented 6 months ago

Thank you @viljarjf for the clear example! I opened https://github.com/hyperspy/hyperspy/issues/3315 to track the issue/not forget about it.