diff --git a/mathics/builtin/__init__.py b/mathics/builtin/__init__.py index 594b3d2eba..3d605df166 100755 --- a/mathics/builtin/__init__.py +++ b/mathics/builtin/__init__.py @@ -125,18 +125,3 @@ def contribute(definitions): if not definitions.have_definition(ensure_context(operator)): op = ensure_context(operator) definitions.builtin[op] = Definition(name=op) - - # Special case for Image[]: Image[] is an atom, and so Image[...] - # will not usually evaluate to anything, since there are no rules - # attached to it. we're adding one special rule here, that allows - # to construct Image atoms by using Image[] (using the helper - # builin ImageCreate). - from mathics.core.rules import Rule - from mathics.builtin.image import Image - from mathics.core.parser import parse_builtin_rule - - definition = Definition( - name='System`Image', rules=[ - Rule(parse_builtin_rule('Image[x_]'), - parse_builtin_rule('ImageCreate[x]'), system=True)]) - definitions.builtin['System`Image'] = definition diff --git a/mathics/builtin/base.py b/mathics/builtin/base.py index f56c7afca7..1f9693957b 100644 --- a/mathics/builtin/base.py +++ b/mathics/builtin/base.py @@ -217,6 +217,16 @@ def init(self, *args, **kwargs): pass +class AtomBuiltin(Builtin): + # allows us to define apply functions, rules, messages, etc. for Atoms + # which are by default not in the definitions' contribution pipeline. + # see Image[] for an example of this. + + def get_name(self): + name = super(AtomBuiltin, self).get_name() + return re.sub(r"Atom$", "", name) + + class Operator(Builtin): operator = None precedence = None diff --git a/mathics/builtin/image.py b/mathics/builtin/image.py index 0ce4f500eb..b0ab9612bc 100644 --- a/mathics/builtin/image.py +++ b/mathics/builtin/image.py @@ -12,7 +12,7 @@ from __future__ import division from mathics.builtin.base import ( - Builtin, Test, BoxConstruct, String) + Builtin, AtomBuiltin, Test, BoxConstruct, String) from mathics.core.expression import ( Atom, Expression, Integer, Rational, Real, Symbol, from_python) from mathics.core.evaluation import Evaluation @@ -1114,9 +1114,15 @@ def apply(self, image, evaluation): class BinaryImageQ(Test): - def apply(self, image, evaluation): - 'BinaryImageQ[image_Image]' - return Symbol('True') if image.storage_type() == 'Bit' else Symbol('False') + ''' +
+
'BinaryImageQ[$image]' +
returns True if the pixels of $image are binary bit values, and False otherwise. +
+ ''' + + def test(self, expr): + return isinstance(expr, Image) and expr.storage_type() == 'Bit' # Image core classes @@ -1134,16 +1140,31 @@ def _image_pixels(matrix): return None -class ImageCreate(Builtin): - def apply(self, array, evaluation): - '''ImageCreate[array_]''' - pixels = _image_pixels(array.to_python()) - if pixels is not None: - shape = pixels.shape - is_rgb = (len(shape) == 3 and shape[2] == 3) - return Image(pixels.clip(0, 1), 'RGB' if is_rgb else 'Grayscale') - else: - return Symbol('$Aborted') +class ImageQ(Test): + ''' +
+
'ImageQ[Image[$pixels]]' +
returns True if $pixels has dimensions from which an Image can be constructed, and False otherwise. +
+ + >> ImageQ[Image[{{0, 1}, {1, 0}}]] + = True + + >> ImageQ[Image[{{{0, 0, 0}, {0, 1, 0}}, {{0, 1, 0}, {0, 1, 1}}}]] + = True + + >> ImageQ[Image[{{{0, 0, 0}, {0, 1}}, {{0, 1, 0}, {0, 1, 1}}}]] + = False + + >> ImageQ[Image[{1, 0, 1}]] + = False + + >> ImageQ["abc"] + = False + ''' + + def test(self, expr): + return isinstance(expr, Image) class ImageBox(BoxConstruct): @@ -1152,11 +1173,19 @@ def boxes_to_text(self, leaves, **options): def boxes_to_xml(self, leaves, **options): # see https://tools.ietf.org/html/rfc2397 - img = '' % (leaves[0].get_string_value()) + img = '' % ( + leaves[0].get_string_value(), leaves[1].get_int_value(), leaves[2].get_int_value()) - # see https://github.com/mathjax/MathJax/issues/896 - xml = '%s' % img - return xml + # if we have Mathics JavaScript frontend processing that rewrites MathML tags using + # , we must not embed our tag in here. + uses_mathics_frontend_processing = False + + if not uses_mathics_frontend_processing: + # see https://github.com/mathjax/MathJax/issues/896 + xml = '%s' % img + return xml + else: + return img def boxes_to_tex(self, leaves, **options): return '-Image-' @@ -1198,12 +1227,16 @@ def make_boxes(self, form): width = shape[1] height = shape[0] + scaled_width = width + scaled_height = height # if the image is very small, scale it up using nearest neighbour. min_size = 128 if width < min_size and height < min_size: scale = min_size / max(width, height) - pixels = skimage.transform.resize(pixels, (int(scale * height), int(scale * width)), order=0) + scaled_width = int(scale * width) + scaled_height = int(scale * height) + pixels = skimage.transform.resize(pixels, (scaled_height, scaled_width), order=0) stream = BytesIO() skimage.io.imsave(stream, pixels, 'pil', format_str='png') @@ -1215,7 +1248,7 @@ def make_boxes(self, form): if not six.PY2: encoded = encoded.decode('utf8') - return Expression('ImageBox', String(encoded), Integer(width), Integer(height)) + return Expression('ImageBox', String(encoded), Integer(scaled_width), Integer(scaled_height)) except: return Symbol("$Failed") @@ -1268,3 +1301,15 @@ def storage_type(self): return 'Bit' else: return str(dtype) + + +class ImageAtom(AtomBuiltin): + def apply_create(self, array, evaluation): + 'Image[array_]' + pixels = _image_pixels(array.to_python()) + if pixels is not None: + shape = pixels.shape + is_rgb = (len(shape) == 3 and shape[2] == 3) + return Image(pixels.clip(0, 1), 'RGB' if is_rgb else 'Grayscale') + else: + return Expression('Image', array) diff --git a/mathics/web/media/js/mathics.js b/mathics/web/media/js/mathics.js index 2ea9d0d8e8..3feed392b2 100644 --- a/mathics/web/media/js/mathics.js +++ b/mathics/web/media/js/mathics.js @@ -202,11 +202,11 @@ function translateDOMElement(element, svg) { drawGraphics3D(div, data); dom = div; } - if (nodeName == 'svg' || nodeName == 'graphics3d') { + if (nodeName == 'svg' || nodeName == 'graphics3d' || nodeName.toLowerCase() == 'img') { // create that will contain the graphics object = createMathNode('mspace'); var width, height; - if (nodeName == 'svg') { + if (nodeName == 'svg' || nodeName.toLowerCase() == 'img') { width = dom.getAttribute('width'); height = dom.getAttribute('height'); } else {