<commit_before>_last_figure = None
import ipywidgets
from IPython.display import display
import IPython
from . import volume
import ipyvolume.embed
import os
import numpy as np
import shutil
from . import utils

def _docsubst(f):
	"""Perform docstring substitutions"""
	f.__doc__ = f.__doc__.format(**_doc_snippets)
	return f

_doc_snippets = {}
_doc_snippets["color"] = "string format, examples for red:'red', '#f00', '#ff0000' or 'rgb(1,0,0)"
_doc_snippets["size"] = "float representing the size of the glyph in percentage of the viewport, where 100 is the full size of the viewport"
_doc_snippets["marker"] = "name of the marker, options are: 'arrow', 'box', 'diamond', 'sphere'"
_doc_snippets["x"] = "1d numpy array with x positions"
_doc_snippets["u"] = "1d numpy array indicating the x direction"

class current:
	figure = None
	container = None
	figures = {}
	containers = {}

def clear():
	"""Remove current figure (and container)"""
	current.container = None
	current.figure = None

def figure(key=None, width=400, height=500, lighting=True, controls=True, debug=False):
	"""Create a new figure (if no key is given) or return the figure associated with key

	:param key: Python object that identifies this figure
	:param width: pixel width of WebGL canvas
	:param height:  .. height ..
	:param lighting: use lighting or not
	:param controls: show controls or not
	:param debug: show debug buttons or not
	:return:
	"""
	if key is not None and key in current.figures:
		current.figure = current.figures[key]
		current.container = current.containers[key]
	else:
		current.figure = volume.VolumeRendererThree(data=None, width=width, height=height)
		current.container = ipywidgets.VBox()
		current.container.children = [current.figure]
		if key is not None:
			current.figures[key] = current.figure
			current.containers[key] = current.container
		if controls:
			stereo = ipywidgets.ToggleButton(value=current.figure.stereo, description='stereo', icon='eye')
			fullscreen = ipywidgets.ToggleButton(value=current.figure.stereo, description='fullscreen',
												 icon='arrows-alt')
			l1 = ipywidgets.jslink((current.figure, 'stereo'), (stereo, 'value'))
			l2 = ipywidgets.jslink((current.figure, 'fullscreen'), (fullscreen, 'value'))
			current.container.children += (ipywidgets.HBox([stereo, fullscreen]),)
		if debug:
			show = ipywidgets.ToggleButtons(options=["Volume", "Back", "Front"])
			current.container.children += (show,)
			ipywidgets.jslink((current.figure, 'show'), (show, 'value'))
	return current.figure

def gcf():
	"""Get current figure, or create a new one"""
	if current.figure is None:
		return figure()
	else:
		return current.figure

def _grow_limit(limits, values):
	finites = np.isfinite(values)
	newvmin = np.min(values[finites])
	newvmax = np.max(values[finites])
	if limits is None:
		return newvmin, newvmax
	else:
		vmin, vmax = limits
		return min(newvmin, vmin), max(newvmax, vmax)

def _grow_limits(x, y, z):
	fig = gcf()
	xlim(*_grow_limit(fig.xlim, x))
	ylim(*_grow_limit(fig.ylim, y))
	zlim(*_grow_limit(fig.zlim, z))

def xlim(xmin, xmax):
	"""Set limits of x axis"""
	fig = gcf()
	fig.xlim = [xmin, xmax]
def ylim(ymin, ymax):
	"""Set limits of y axis"""
	fig = gcf()
	fig.ylim = [ymin, ymax]
def zlim(zmin, zmax):
	"""Set limits of zaxis"""
	fig = gcf()
	fig.zlim = [zmin, zmax]
def xyzlim(vmin, vmax):
	"""Set limits or all axis the same"""
	xlim(vmin, vmax)
	ylim(vmin, vmax)
	zlim(vmin, vmax)

default_color = "red"
default_color_selected = "white"
default_size = 2
default_size_selected = default_size*1.3

@_docsubst
def scatter(x, y, z, color=default_color, size=default_size, size_selected=default_size_selected, color_selected=default_color_selected, marker="diamond", selection=None, **kwargs):
	"""Create a scatter 3d plot with

	:param x: {x}
	:param y:
	:param z:
	:param color: {color}
	:param size: {size}
	:param size_selected: like size, but for selected glyphs
	:param color_selected:  like color, but for selected glyphs
	:param marker: {marker}
	:param selection: array with indices of x,y,z arrays of the selected markers, which can have a different size and color
	:param kwargs:
	:return:
	"""
	fig = gcf()
	_grow_limits(x, y, z)
	scatter = volume.Scatter(x=x, y=y, z=z, color=color, size=size, color_selected=color_selected, size_selected=size_selected, geo=marker, selection=selection, **kwargs)
	fig.scatters = fig.scatters + [scatter]
	return scatter


def quiver(x, y, z, u, v, w, size=default_size*10, size_selected=default_size_selected*10, color=default_color, color_selected=default_color_selected, marker="arrow", **kwargs):
	"""Create a quiver plot, which is like a scatter plot but with arrows pointing in the direction given by u, v and w

	:param x: {x}
	:param y:
	:param z:
	:param u: {u}
	:param v:
	:param w:
	:param size: {size}
	:param size_selected: like size, but for selected glyphs
	:param color: {color}
	:param color_selected: like color, but for selected glyphs
	:param marker: (currently only 'arrow' would make sense)
	:param kwargs:
	:return:
	"""
	fig = gcf()
	_grow_limits(x, y, z)
	scatter = volume.Scatter(x=x, y=y, z=z, vx=u, vy=v, vz=w,
							 color=color, size=size, color_selected=color_selected, size_selected=size_selected,
							 geo=marker, **kwargs)
	fig.scatters = fig.scatters + [scatter]
	return scatter


def show():
	"""Display (like in IPython.display.dispay(...) the current figure"""
	gcf() # make sure we have something..
	display(gcc())

def animate_glyphs(scatter, sequence_length=None, add=True, interval=200):
	"""Animate scatter or quiver by adding a slider and play button.

	:param scatter: scatter or quiver object
	:param sequence_length: If sequence_length is None we try try our best to figure out, in case we do it badly, you can tell us what it should be
	:param add: if True, add the widgets to the container, else return a HBox with the slider and play button
	:param interval: interval in msec between each frame
	:return:
	"""
	if sequence_length is None:
		# get all non-None arrays
		values = [getattr(scatter, name) for name in "x y z vx vy vz".split()]
		values = [k for k in values if k is not None]
		# sort them such that the higest dim is first
		values.sort(key=lambda key: -len(key.shape))
		sequence_length = values[0].shape[0] # assume this defines the sequence length
	fig = gcf()
	fig.animation = interval
	fig.animation_exponent = 1.
	play = ipywidgets.Play(min=0, max=sequence_length, interval=interval, value=0, step=1)
	slider = ipywidgets.IntSlider(min=0, max=play.max)
	ipywidgets.jslink((play, 'value'), (slider, 'value'))
	ipywidgets.jslink((slider, 'value'), (scatter, 'sequence_index'))
	control = ipywidgets.HBox([play, slider])
	if add:
		current.container.children += (control,)
	else:
		return control

def gcc():
	"""Return the current container, that is the widget holding the figure and all the control widgets, buttons etc."""
	gcf() # make sure we have something..
	return current.container

def transfer_function(level=[0.1, 0.5, 0.9], opacity=[0.01, 0.05, 0.1], level_width=0.1, controls=True):
	"""Create a transfer function, see volshow"""
	tf_kwargs = {}
	# level, opacity and widths can be scalars
	try:
		level[0]
	except:
		level = [level]
	try:
		opacity[0]
	except:
		opacity = [opacity] * 3
	try:<commit_msg>fix: one off for Play, might be fixed for ipywidgets<commit_after>_last_figure = None
import ipywidgets
from IPython.display import display
import IPython
from . import volume
import ipyvolume.embed
import os
import numpy as np
import shutil
from . import utils

def _docsubst(f):
	"""Perform docstring substitutions"""
	f.__doc__ = f.__doc__.format(**_doc_snippets)
	return f

_doc_snippets = {}
_doc_snippets["color"] = "string format, examples for red:'red', '#f00', '#ff0000' or 'rgb(1,0,0)"
_doc_snippets["size"] = "float representing the size of the glyph in percentage of the viewport, where 100 is the full size of the viewport"
_doc_snippets["marker"] = "name of the marker, options are: 'arrow', 'box', 'diamond', 'sphere'"
_doc_snippets["x"] = "1d numpy array with x positions"
_doc_snippets["u"] = "1d numpy array indicating the x direction"

class current:
	figure = None
	container = None
	figures = {}
	containers = {}

def clear():
	"""Remove current figure (and container)"""
	current.container = None
	current.figure = None

def figure(key=None, width=400, height=500, lighting=True, controls=True, debug=False):
	"""Create a new figure (if no key is given) or return the figure associated with key

	:param key: Python object that identifies this figure
	:param width: pixel width of WebGL canvas
	:param height:  .. height ..
	:param lighting: use lighting or not
	:param controls: show controls or not
	:param debug: show debug buttons or not
	:return:
	"""
	if key is not None and key in current.figures:
		current.figure = current.figures[key]
		current.container = current.containers[key]
	else:
		current.figure = volume.VolumeRendererThree(data=None, width=width, height=height)
		current.container = ipywidgets.VBox()
		current.container.children = [current.figure]
		if key is not None:
			current.figures[key] = current.figure
			current.containers[key] = current.container
		if controls:
			stereo = ipywidgets.ToggleButton(value=current.figure.stereo, description='stereo', icon='eye')
			fullscreen = ipywidgets.ToggleButton(value=current.figure.stereo, description='fullscreen',
												 icon='arrows-alt')
			l1 = ipywidgets.jslink((current.figure, 'stereo'), (stereo, 'value'))
			l2 = ipywidgets.jslink((current.figure, 'fullscreen'), (fullscreen, 'value'))
			current.container.children += (ipywidgets.HBox([stereo, fullscreen]),)
		if debug:
			show = ipywidgets.ToggleButtons(options=["Volume", "Back", "Front"])
			current.container.children += (show,)
			ipywidgets.jslink((current.figure, 'show'), (show, 'value'))
	return current.figure

def gcf():
	"""Get current figure, or create a new one"""
	if current.figure is None:
		return figure()
	else:
		return current.figure

def _grow_limit(limits, values):
	finites = np.isfinite(values)
	newvmin = np.min(values[finites])
	newvmax = np.max(values[finites])
	if limits is None:
		return newvmin, newvmax
	else:
		vmin, vmax = limits
		return min(newvmin, vmin), max(newvmax, vmax)

def _grow_limits(x, y, z):
	fig = gcf()
	xlim(*_grow_limit(fig.xlim, x))
	ylim(*_grow_limit(fig.ylim, y))
	zlim(*_grow_limit(fig.zlim, z))

def xlim(xmin, xmax):
	"""Set limits of x axis"""
	fig = gcf()
	fig.xlim = [xmin, xmax]
def ylim(ymin, ymax):
	"""Set limits of y axis"""
	fig = gcf()
	fig.ylim = [ymin, ymax]
def zlim(zmin, zmax):
	"""Set limits of zaxis"""
	fig = gcf()
	fig.zlim = [zmin, zmax]
def xyzlim(vmin, vmax):
	"""Set limits or all axis the same"""
	xlim(vmin, vmax)
	ylim(vmin, vmax)
	zlim(vmin, vmax)

default_color = "red"
default_color_selected = "white"
default_size = 2
default_size_selected = default_size*1.3

@_docsubst
def scatter(x, y, z, color=default_color, size=default_size, size_selected=default_size_selected, color_selected=default_color_selected, marker="diamond", selection=None, **kwargs):
	"""Create a scatter 3d plot with

	:param x: {x}
	:param y:
	:param z:
	:param color: {color}
	:param size: {size}
	:param size_selected: like size, but for selected glyphs
	:param color_selected:  like color, but for selected glyphs
	:param marker: {marker}
	:param selection: array with indices of x,y,z arrays of the selected markers, which can have a different size and color
	:param kwargs:
	:return:
	"""
	fig = gcf()
	_grow_limits(x, y, z)
	scatter = volume.Scatter(x=x, y=y, z=z, color=color, size=size, color_selected=color_selected, size_selected=size_selected, geo=marker, selection=selection, **kwargs)
	fig.scatters = fig.scatters + [scatter]
	return scatter


def quiver(x, y, z, u, v, w, size=default_size*10, size_selected=default_size_selected*10, color=default_color, color_selected=default_color_selected, marker="arrow", **kwargs):
	"""Create a quiver plot, which is like a scatter plot but with arrows pointing in the direction given by u, v and w

	:param x: {x}
	:param y:
	:param z:
	:param u: {u}
	:param v:
	:param w:
	:param size: {size}
	:param size_selected: like size, but for selected glyphs
	:param color: {color}
	:param color_selected: like color, but for selected glyphs
	:param marker: (currently only 'arrow' would make sense)
	:param kwargs:
	:return:
	"""
	fig = gcf()
	_grow_limits(x, y, z)
	scatter = volume.Scatter(x=x, y=y, z=z, vx=u, vy=v, vz=w,
							 color=color, size=size, color_selected=color_selected, size_selected=size_selected,
							 geo=marker, **kwargs)
	fig.scatters = fig.scatters + [scatter]
	return scatter


def show():
	"""Display (like in IPython.display.dispay(...) the current figure"""
	gcf() # make sure we have something..
	display(gcc())

def animate_glyphs(scatter, sequence_length=None, add=True, interval=200):
	"""Animate scatter or quiver by adding a slider and play button.

	:param scatter: scatter or quiver object
	:param sequence_length: If sequence_length is None we try try our best to figure out, in case we do it badly, you can tell us what it should be
	:param add: if True, add the widgets to the container, else return a HBox with the slider and play button
	:param interval: interval in msec between each frame
	:return:
	"""
	if sequence_length is None:
		# get all non-None arrays
		values = [getattr(scatter, name) for name in "x y z vx vy vz".split()]
		values = [k for k in values if k is not None]
		# sort them such that the higest dim is first
		values.sort(key=lambda key: -len(key.shape))
		sequence_length = values[0].shape[0] # assume this defines the sequence length
	fig = gcf()
	fig.animation = interval
	fig.animation_exponent = 1.
	play = ipywidgets.Play(min=0, max=sequence_length, interval=interval, value=0, step=1)
	slider = ipywidgets.IntSlider(min=0, max=play.max-1)
	ipywidgets.jslink((play, 'value'), (slider, 'value'))
	ipywidgets.jslink((slider, 'value'), (scatter, 'sequence_index'))
	control = ipywidgets.HBox([play, slider])
	if add:
		current.container.children += (control,)
	else:
		return control

def gcc():
	"""Return the current container, that is the widget holding the figure and all the control widgets, buttons etc."""
	gcf() # make sure we have something..
	return current.container

def transfer_function(level=[0.1, 0.5, 0.9], opacity=[0.01, 0.05, 0.1], level_width=0.1, controls=True):
	"""Create a transfer function, see volshow"""
	tf_kwargs = {}
	# level, opacity and widths can be scalars
	try:
		level[0]
	except:
		level = [level]
	try:
		opacity[0]
	except:
		opacity = [opacity] * 3
	try:
{'fraction_non_alphanumeric': 0.12702425691834643, 'fraction_numerical': 0.008131192347113085, 'mean_word_length': 4.889738430583501, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '872680', 'n_tokens_mistral': 5285, 'n_tokens_neox': 4959, 'n_words': 1612}