Skip to content
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

Adding an option for use Asymptote to build svg graphics #1145

Closed
wants to merge 17 commits into from
Closed
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions mathics/builtin/arithmetic.py
Original file line number Diff line number Diff line change
Expand Up @@ -1343,14 +1343,13 @@ class PossibleZeroQ(SympyFunction):
def apply(self, expr, evaluation):
"%(name)s[expr_]"
from sympy.matrices.utilities import _iszero

sympy_expr = expr.to_sympy()
result = _iszero(sympy_expr)
if result is None:
# try expanding the expression
exprexp = Expression("ExpandAll", expr).evaluate(evaluation)
exprexp = exprexp.to_sympy()
result = _iszero(exprexp)
result = _iszero(exprexp)
if result is None:
# Can't get exact answer, so try approximate equal
numeric_val = Expression("N", expr).evaluate(evaluation)
Expand All @@ -1364,7 +1363,6 @@ def apply(self, expr, evaluation):
if Expression("Simplify", expr).evaluate(evaluation) == Integer(0)
else SymbolFalse
)

return from_python(result)


Expand Down
53 changes: 49 additions & 4 deletions mathics/builtin/graphics.py
Original file line number Diff line number Diff line change
Expand Up @@ -3142,7 +3142,7 @@ def get_range(min, max):

return elements, calc_dimensions

def boxes_to_tex(self, leaves, **options):
def boxes_to_tex(self, leaves, forxml=False, **options):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding a parameter like this feels really hacky.

The front end should be involved here. The backend merely needs to provide the underlying building blocks.

Copy link
Contributor Author

@mmatera mmatera Feb 21, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding a parameter like this feels really hacky.
Agree. Maybe I should hide it inside **options

The front end should be involved here. The backend merely needs to provide the underlying building blocks.

OK, but for it, I would need to merge #1140 first...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's wait until #1140 clears then.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea of these modifications is to have a working graphics engine without changing the current core. Most of the code is there, and if the control variable is not set (or set to false), we get the previous (buggy) behavior.
On the other hand, this does not clash against #1140, this is why I put this in a separated PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding a parameter like this feels really hacky.

The front end should be involved here. The backend merely needs to provide the underlying building blocks.
Regarding this, I put forxml as an optional parameter.

elements, calc_dimensions = self._prepare_elements(
leaves, options, max_width=450
)
Expand Down Expand Up @@ -3176,14 +3176,12 @@ def boxes_to_tex(self, leaves, **options):
asy_background = ""

tex = r"""
\begin{asy}
usepackage("amsmath");
size(%scm, %scm);
%s
%s
clip(%s);
%s
\end{asy}
""" % (
asy_number(width / 60),
asy_number(height / 60),
Expand All @@ -3193,9 +3191,56 @@ def boxes_to_tex(self, leaves, **options):
asy_completely_visible,
)

return tex
if forxml:
return (tex, width, height)
else:
return "\n\\begin{asy}\n" + tex + "\n\\end{asy}\n"

def boxes_to_xml(self, leaves, **options):
evaluation = options.get("evaluation", None)
check_asy = False
if evaluation:
check_asy = evaluation.definitions.get_ownvalue("Settings`UseAsyForGraphics2D")
if check_asy:
check_asy = check_asy.replace.is_true()
if check_asy:
import os
from subprocess import DEVNULL, STDOUT, check_call
import tempfile
try:
check_call(['asy', '--version'], stdout=DEVNULL, stderr=DEVNULL)
except:
check_asy = False

if check_asy:
asy, width, height = self.boxes_to_tex(leaves, forxml=True, **options)
fin = os.path.join(tempfile._get_default_tempdir(), next(tempfile._get_candidate_names()))
fout = fin + ".svg"
with open(fin, 'w+') as borrador:
borrador.write(asy)

try:
check_call(['asy', '-f', 'svg', '--svgemulation' ,'-o', fout, fin], stdout=DEVNULL, stderr=DEVNULL)
except:
print("Asy failed to build a svg")
check_asy = False

if check_asy:
with open(fout, 'rt') as ff:
svg = ff.read()

svg = svg[svg.find("<svg "):]
return (
'<img width="%dpx" height="%dpx" src="data:image/svg+xml;base64,%s"/>'
% (
int(width),
int(height),
base64.b64encode(svg.encode("utf8")).decode("utf8"),
)
)
else:
print("Asy not available. Continue with standard")

elements, calc_dimensions = self._prepare_elements(leaves, options, neg_y=True)

xmin, xmax, ymin, ymax, w, h, width, height = calc_dimensions()
Expand Down
10 changes: 9 additions & 1 deletion mathics/builtin/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -406,8 +406,16 @@ class UserName(Predefined):

name = "$UserName"

messages = {
'nologin': "UserName not available in this system.",
}

def evaluate(self, evaluation) -> String:
return String(os.getlogin())
try:
return String(os.getlogin())
except:
evaluation.message('$UserName', 'nologin')
return


class Version(Predefined):
Expand Down
2 changes: 1 addition & 1 deletion mathics/core/expression.py
Original file line number Diff line number Diff line change
Expand Up @@ -2137,7 +2137,7 @@ def __neg__(self) -> 'Rational':

@property
def is_zero(self) -> bool:
return self.numerator().is_zero and not self.denominator().is_zero()
return self.numerator().is_zero and not(self.denominator().is_zero)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be in a separate PR. It fixes a current bug and is not specific to asymptote, right?

Copy link
Contributor Author

@mmatera mmatera Feb 21, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I put this because I got CI errors due to PossibleZeroQ. I removed this change from this PR. Please look at #1164.


class Real(Number):
Expand Down