<commit_before>###############################################################################
# Copyright (c) 2013, 2014 by <NAME>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
#    list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright notice,
#    this list of conditions and the following disclaimer in the documentation
#    and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
###############################################################################



import json, math
import sage_salvus

from uuid import uuid4
def uuid():
    return str(uuid4())

def json_float(t):
    t = float(t)
    # Neither of nan or inf get JSON'd in a way that works properly, for some reason.  I don't understand why.
    if math.isnan(t) or math.isinf(t):
        return None
    else:
        return t

#######################################################
# Three.js based plotting
#######################################################

noneint = lambda n : n if n is None else int(n)

class ThreeJS(object):
    def __init__(self, renderer=None, width=None, height=None,
                 frame=True, background=None, foreground=None, spin=False, viewer=None, **ignored):
        """
        INPUT:

        - renderer -- None (automatic), 'canvas2d', or 'webgl'
        - width    -- None (automatic) or an integer
        - height   -- None (automatic) or an integer
        - frame    -- bool (default: True); draw a frame that includes every object.
        - background -- None (transparent); otherwise a color such as 'black' or 'white'
        - foreground -- None (automatic = black if transparent; otherwise opposite of background);
           or a color; this is used for drawing the frame and axes labels.
        - spin -- False; if True, spins 3d plot; if a number determines speed (60=one rotation per second)
        - viewer -- synonym for renderer
        """
        if viewer is not None and renderer is None:
            renderer = viewer
        if renderer not in [None, 'webgl', 'canvas', 'canvas2d']:
            raise ValueError("unknown renderer='%s'; it must be None, webgl, or canvas2d"%renderer)
        self._frame    = frame
        self._salvus   = sage_salvus.salvus  # object for this cell
        self._id       = uuid()
        self._selector = "#%s"%self._id
        self._obj      = "$('%s').data('salvus-threejs')"%self._selector
        self._salvus.html("<div id=%s class='salvus-3d-container'></div>"%self._id)
        if not isinstance(spin, bool):
            spin = json_float(spin)

        self._salvus.javascript("$('%s').salvus_threejs(obj)"%self._selector,
                                once = False,
                                obj  = {
                                     'renderer'        : renderer,
                                     'width'           : noneint(width),
                                     'height'          : noneint(height),
                                     'background'      : background,
                                     'foreground'      : foreground,
                                     'spin'            : spin
                                     })
        self._graphics = []
        self._call('init()')

    def _call(self, s, obj=None):
        cmd = 'misc.eval_until_defined({code:"%s", cb:(function(err, __t__) { __t__ != null ? __t__.%s:void 0 })})'%(
                self._obj, s)
        self._salvus.execute_javascript(cmd, obj=obj)

    def bounding_box(self):
        if not self._graphics:
            return -1,1,-1,1,-1,1
        b = self._graphics[0].bounding_box()
        xmin, xmax, ymin, ymax, zmin, zmax = b[0][0], b[1][0], b[0][1], b[1][1], b[0][2], b[1][2]
        for g in self._graphics[1:]:
            b = g.bounding_box()
            xmin, xmax, ymin, ymax, zmin, zmax = (
                  min(xmin,b[0][0]), max(b[1][0],xmax),
                  min(b[0][1],ymin), max(b[1][1],ymax),
                  min(b[0][2],zmin), max(b[1][2],zmax))
        v = xmin, xmax, ymin, ymax, zmin, zmax
        return [json_float(x) for x in v]

    def frame_options(self):
        xmin, xmax, ymin, ymax, zmin, zmax = self.bounding_box()
        return {'xmin':xmin, 'xmax':xmax, 'ymin':ymin, 'ymax':ymax, 'zmin':zmin, 'zmax':zmax,
                'draw' : self._frame}

    def add(self, graphics3d, **kwds):
        kwds = graphics3d._process_viewing_options(kwds)
        self._graphics.append(graphics3d)
        obj = {'obj'       : graphics3d_to_jsonable(graphics3d),
               'wireframe' : jsonable(kwds.get('wireframe')),
               'set_frame' : self.frame_options()}
        self._call('add_3dgraphics_obj(obj)', obj=obj)

    def render_scene(self, force=True):
        self._call('render_scene(obj)', obj={'force':force})

    def add_text(self, pos, text, fontsize=18, fontface='Arial', sprite_alignment='topLeft'):
        self._call('add_text(obj)',
                   obj={'pos':[json_float(pos[0]), json_float(pos[1]), json_float(pos[2])],'text':str(text),
                        'fontsize':int(fontsize),'fontface':str(fontface), 'sprite_alignment':str(sprite_alignment)})

    def animate(self, fps=None, stop=None, mouseover=True):
        self._call('animate(obj)', obj={'fps':noneint(fps), 'stop':stop, 'mouseover':mouseover})

def show_3d_plot_using_threejs(g, **kwds):
    for k in ['spin', 'renderer', 'viewer', 'frame', 'height', 'width', 'background', 'foreground']:
        if k in g._extra_kwds and k not in kwds:
            kwds[k] = g._extra_kwds[k]
    if 'camera_distance' in kwds:
        del kwds['camera_distance'] # deprecated
    t = ThreeJS(**kwds)
    t.add(g, **kwds)
    if kwds.get('spin', False):
        t.animate(mouseover=False)
    return t

import sage.plot.plot3d.index_face_set
import sage.plot.plot3d.shapes
import sage.plot.plot3d.base
import sage.plot.plot3d.shapes2
from sage.structure.element import Element

def jsonable(x):
    if isinstance(x, Element):
        return json_float(x)<commit_msg>3d: fix a bug in 3d plotting that one sees with sage.plot.plot3d.parametric_surface.MobiusStrip(1,.2)<commit_after>###############################################################################
# Copyright (c) 2013, 2014 by <NAME>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
#    list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright notice,
#    this list of conditions and the following disclaimer in the documentation
#    and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
###############################################################################



import json, math
import sage_salvus

from uuid import uuid4
def uuid():
    return str(uuid4())

def json_float(t):
    t = float(t)
    # Neither of nan or inf get JSON'd in a way that works properly, for some reason.  I don't understand why.
    if math.isnan(t) or math.isinf(t):
        return None
    else:
        return t

#######################################################
# Three.js based plotting
#######################################################

noneint = lambda n : n if n is None else int(n)

class ThreeJS(object):
    def __init__(self, renderer=None, width=None, height=None,
                 frame=True, background=None, foreground=None, spin=False, viewer=None, **ignored):
        """
        INPUT:

        - renderer -- None (automatic), 'canvas2d', or 'webgl'
        - width    -- None (automatic) or an integer
        - height   -- None (automatic) or an integer
        - frame    -- bool (default: True); draw a frame that includes every object.
        - background -- None (transparent); otherwise a color such as 'black' or 'white'
        - foreground -- None (automatic = black if transparent; otherwise opposite of background);
           or a color; this is used for drawing the frame and axes labels.
        - spin -- False; if True, spins 3d plot; if a number determines speed (60=one rotation per second)
        - viewer -- synonym for renderer
        """
        if viewer is not None and renderer is None:
            renderer = viewer
        if renderer not in [None, 'webgl', 'canvas', 'canvas2d']:
            raise ValueError("unknown renderer='%s'; it must be None, webgl, or canvas2d"%renderer)
        self._frame    = frame
        self._salvus   = sage_salvus.salvus  # object for this cell
        self._id       = uuid()
        self._selector = "#%s"%self._id
        self._obj      = "$('%s').data('salvus-threejs')"%self._selector
        self._salvus.html("<div id=%s class='salvus-3d-container'></div>"%self._id)
        if not isinstance(spin, bool):
            spin = json_float(spin)

        self._salvus.javascript("$('%s').salvus_threejs(obj)"%self._selector,
                                once = False,
                                obj  = {
                                     'renderer'        : renderer,
                                     'width'           : noneint(width),
                                     'height'          : noneint(height),
                                     'background'      : background,
                                     'foreground'      : foreground,
                                     'spin'            : spin
                                     })
        self._graphics = []
        self._call('init()')

    def _call(self, s, obj=None):
        cmd = 'misc.eval_until_defined({code:"%s", cb:(function(err, __t__) { __t__ != null ? __t__.%s:void 0 })})'%(
                self._obj, s)
        self._salvus.execute_javascript(cmd, obj=obj)

    def bounding_box(self):
        if not self._graphics:
            return -1,1,-1,1,-1,1
        b = self._graphics[0].bounding_box()
        xmin, xmax, ymin, ymax, zmin, zmax = b[0][0], b[1][0], b[0][1], b[1][1], b[0][2], b[1][2]
        for g in self._graphics[1:]:
            b = g.bounding_box()
            xmin, xmax, ymin, ymax, zmin, zmax = (
                  min(xmin,b[0][0]), max(b[1][0],xmax),
                  min(b[0][1],ymin), max(b[1][1],ymax),
                  min(b[0][2],zmin), max(b[1][2],zmax))
        v = xmin, xmax, ymin, ymax, zmin, zmax
        return [json_float(x) for x in v]

    def frame_options(self):
        xmin, xmax, ymin, ymax, zmin, zmax = self.bounding_box()
        return {'xmin':xmin, 'xmax':xmax, 'ymin':ymin, 'ymax':ymax, 'zmin':zmin, 'zmax':zmax,
                'draw' : self._frame}

    def add(self, graphics3d, **kwds):
        kwds = graphics3d._process_viewing_options(kwds)
        self._graphics.append(graphics3d)
        obj = {'obj'       : graphics3d_to_jsonable(graphics3d),
               'wireframe' : jsonable(kwds.get('wireframe')),
               'set_frame' : self.frame_options()}
        self._call('add_3dgraphics_obj(obj)', obj=obj)

    def render_scene(self, force=True):
        self._call('render_scene(obj)', obj={'force':force})

    def add_text(self, pos, text, fontsize=18, fontface='Arial', sprite_alignment='topLeft'):
        self._call('add_text(obj)',
                   obj={'pos':[json_float(pos[0]), json_float(pos[1]), json_float(pos[2])],'text':str(text),
                        'fontsize':int(fontsize),'fontface':str(fontface), 'sprite_alignment':str(sprite_alignment)})

    def animate(self, fps=None, stop=None, mouseover=True):
        self._call('animate(obj)', obj={'fps':noneint(fps), 'stop':stop, 'mouseover':mouseover})

def show_3d_plot_using_threejs(g, **kwds):
    for k in ['spin', 'renderer', 'viewer', 'frame', 'height', 'width', 'background', 'foreground']:
        extra_kwds = {} if g._extra_kwds is None else g._extra_kwds
        if k in extra_kwds and k not in kwds:
            kwds[k] = g._extra_kwds[k]
    if 'camera_distance' in kwds:
        del kwds['camera_distance'] # deprecated
    t = ThreeJS(**kwds)
    t.add(g, **kwds)
    if kwds.get('spin', False):
        t.animate(mouseover=False)
    return t

import sage.plot.plot3d.index_face_set
import sage.plot.plot3d.shapes
import sage.plot.plot3d.base
import sage.plot.plot3d.shapes2
from sage.structure.element import Element

def jsonable(x):
    if isinstance(x, Element):
        return json_float(x)
{'fraction_non_alphanumeric': 0.15439262854329325, 'fraction_numerical': 0.010058380811704298, 'mean_word_length': 2.6040557667934094, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 16, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5787749', 'n_tokens_mistral': 4352, 'n_tokens_neox': 4123, 'n_words': 1239}