prefig 0.3.9.dev20250728054658__py3-none-any.whl → 0.3.9.dev20250730054824__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
prefig/core/__init__.py CHANGED
@@ -13,6 +13,7 @@ from . import (
13
13
  graph,
14
14
  grid_axes,
15
15
  group,
16
+ image,
16
17
  implicit,
17
18
  label,
18
19
  label_tools,
prefig/core/axes.py CHANGED
@@ -787,8 +787,8 @@ def tick_mark(element, diagram, parent, outline_status):
787
787
  size = un.valid_eval(element.get('size'))
788
788
  if not isinstance(size, np.ndarray):
789
789
  size = (size, size)
790
- else:
791
- size = (3,3)
790
+ else:
791
+ size = (3,3)
792
792
 
793
793
  if tactile:
794
794
  size = (18,0)
@@ -869,8 +869,8 @@ def tick_mark(element, diagram, parent, outline_status):
869
869
 
870
870
  if el_copy.get('alignment', None) is None:
871
871
  el_copy.set('alignment', align)
872
- if el_copy.get('offset', None) is None:
873
- el_copy.set('offset', off)
872
+ if el_copy.get('offset', None) is None:
873
+ el_copy.set('offset', off)
874
874
  el_copy.set("user-coords", "no")
875
875
  el_copy.set("anchor", util.pt2str(p, spacer=","))
876
876
  label.label(el_copy, diagram, parent, outline_status)
prefig/core/graph.py CHANGED
@@ -29,6 +29,10 @@ def graph(element, diagram, parent, outline_status = None):
29
29
  domain = [bbox[0], bbox[2]]
30
30
  else:
31
31
  domain = un.valid_eval(domain)
32
+ if domain[0] == -np.inf:
33
+ domain[0] = bbox[0]
34
+ if domain[1] == np.inf:
35
+ domain[1] = bbox[2]
32
36
 
33
37
  # if there are arrows, we need to pull the domain in by two pixels
34
38
  # so that the arrows don't go outside the domain
prefig/core/image.py ADDED
@@ -0,0 +1,127 @@
1
+ import lxml.etree as ET
2
+ import logging
3
+ import numpy as np
4
+ import base64
5
+ import lxml.etree as ET
6
+ from . import user_namespace as un
7
+ from . import group
8
+ from . import utilities as util
9
+ from . import CTM
10
+
11
+ log = logging.getLogger('prefigure')
12
+
13
+ type_dict = {'jpg':'jpeg',
14
+ 'jpeg':'jpeg',
15
+ 'png':'png',
16
+ 'gif':'gif',
17
+ 'svg':'svg'}
18
+
19
+ # Allows a block of XML to repeat with a changing parameter
20
+
21
+ def image(element, diagram, parent, outline_status):
22
+ if len(element) == 0:
23
+ log.error("An <image> must contain content to replace the image in a tactile build")
24
+ return;
25
+
26
+ if diagram.output_format() == 'tactile':
27
+ element.tag = 'group'
28
+ group.group(element, diagram, parent, outline_status)
29
+ return
30
+
31
+ source = element.get('source', None)
32
+ if source is None:
33
+ log.error("An <image> needs a @source attribute")
34
+ return
35
+ try:
36
+ ll = un.valid_eval(element.get('lower-left', '(0,0)'))
37
+ dims = un.valid_eval(element.get('dimensions', '(1,1)'))
38
+ center = element.get('center', None)
39
+ if center is not None:
40
+ center = un.valid_eval(center)
41
+ ll = center - 0.5 * dims
42
+ else:
43
+ center = ll + 0.5*dims
44
+ rotation = un.valid_eval(element.get('rotate', '0'))
45
+ scale = un.valid_eval(element.get('scale', '1'))
46
+ except:
47
+ log.error("Error parsing placement data in an <image>")
48
+ return
49
+
50
+ file_type = None
51
+ if element.get('filetype', None) is not None:
52
+ file_type = type_dict.get(element.get('filetype'), None)
53
+ if file_type is None:
54
+ suffix = source.split('.')[-1]
55
+ file_type = type_dict.get(suffix, None)
56
+ if file_type is None:
57
+ log.error(f"Cannot determine the type of image in {source}")
58
+ return
59
+
60
+ ll_svg = diagram.transform(ll)
61
+ ur_svg = diagram.transform(ll + dims)
62
+ center_svg = diagram.transform(center)
63
+ width, height = ur_svg - ll_svg
64
+ height = -height
65
+
66
+ if diagram.get_environment() == 'pretext':
67
+ source = 'data/' + source
68
+ else:
69
+ assets_dir = diagram.get_external()
70
+ if assets_dir is not None:
71
+ assets_dir = assets_dir.strip()
72
+ if assets_dir[-1] != '/':
73
+ assets_dir += '/'
74
+ source = assets_dir + source
75
+
76
+ opacity = element.get('opacity', None)
77
+ if opacity is not None:
78
+ opacity = un.valid_eval(opacity)
79
+ if file_type == 'svg':
80
+ svg_tree = ET.parse(source)
81
+ svg_root = svg_tree.getroot()
82
+ svg_width = svg_root.get('width', None)
83
+ svg_height = svg_root.get('height', None)
84
+
85
+ object = ET.SubElement(parent, 'foreignObject')
86
+ object.set('x', util.float2str(center_svg[0]-width/2))
87
+ object.set('y', util.float2str(center_svg[1]-height/2))
88
+ object.set('width', util.float2str(width))
89
+ object.set('height', util.float2str(height))
90
+ svg_root.set('width', '100%')
91
+ svg_root.set('height', '100%')
92
+ if svg_root.get('viewBox', None) is None:
93
+ svg_root.set('viewBox', f"0 0 {svg_width} {svg_height}")
94
+ object.append(svg_root)
95
+ diagram.add_id(object, element.get('id'))
96
+ svg_root.attrib.pop('id', None)
97
+ if opacity is not None:
98
+ object.set('opacity', util.float2str(opacity))
99
+ return
100
+
101
+ image_el = ET.SubElement(parent, 'image')
102
+ image_el.set('x', util.float2str(-width/2))
103
+ image_el.set('y', util.float2str(-height/2))
104
+ image_el.set('width', util.float2str(width))
105
+ image_el.set('height', util.float2str(height))
106
+ if opacity is not None:
107
+ image_el.set('opacity', util.float2str(opacity))
108
+ diagram.add_id(image_el, element.get('id'))
109
+
110
+ with open(source, "rb") as image_file:
111
+ encoded_string = base64.b64encode(image_file.read())
112
+ encoded_string = encoded_string.decode('utf-8')
113
+ ref = f"data:image/{file_type};base64,{encoded_string}"
114
+ image_el.set('href', ref)
115
+
116
+ transform_pieces = [CTM.translatestr(*center_svg)]
117
+
118
+ if isinstance(scale, np.ndarray):
119
+ transform_pieces.append(CTM.scalestr(*scale))
120
+ elif scale != '1':
121
+ transform_pieces.append(f"scale({scale})")
122
+
123
+ if rotation != 0:
124
+ transform_pieces.append(f"rotate({-rotation})")
125
+
126
+ image_el.set('transform', ' '.join(transform_pieces))
127
+
prefig/core/path.py CHANGED
@@ -480,6 +480,31 @@ def decorate(child, diagram, current_point, cmds):
480
480
  cmds += ['L', util.pt2str(ctm.transform((x_pos,0)))]
481
481
  cmds += ['L', util.pt2str(ctm.transform((length, 0)))]
482
482
 
483
+ if decoration_data[0] == 'ragged':
484
+ # offset, step
485
+ try:
486
+ data = [d.split('=') for d in decoration_data[1:]]
487
+ data = {k:v for k, v in data}
488
+ except:
489
+ log.error("Error in wave decoration data")
490
+ return
491
+ try:
492
+ offset = un.valid_eval(data['offset'])
493
+ step = un.valid_eval(data['step'])
494
+ except:
495
+ log.error("Error in retrieving the step and an offset in a ragged decoration")
496
+
497
+ np.random.seed(int(data.get('seed','1')))
498
+ x_pos = 0
499
+ y_pos = 0
500
+ p = ctm.transform((0,0))
501
+ cmds += ['L', util.pt2str(ctm.transform((0,0)))]
502
+ while x_pos < length-step:
503
+ y_pos = 2 * offset * (np.random.random()-0.5)
504
+ x_pos += (0.5*np.random.random()+0.75)*step
505
+ cmds += ['L', util.pt2str(ctm.transform((x_pos,y_pos)))]
506
+ cmds += ['L', util.pt2str(ctm.transform((length, 0)))]
507
+
483
508
  if decoration_data[0] == 'capacitor':
484
509
  try:
485
510
  data = [d.split('=') for d in decoration_data[1:]]
prefig/core/rectangle.py CHANGED
@@ -71,9 +71,11 @@ def rectangle(element, diagram, parent, outline_status):
71
71
  path.set('d', ' '.join(cmds))
72
72
 
73
73
  if diagram.output_format() == 'tactile':
74
- if element.get('stroke') is not None:
74
+ stroke = element.get('stroke')
75
+ fill = element.get('fill')
76
+ if stroke is not None and stroke != 'none':
75
77
  element.set('stroke', 'black')
76
- if element.get('fill') is not None:
78
+ if fill is not None and fill != 'none':
77
79
  element.set('fill', 'lightgray')
78
80
  else:
79
81
  util.set_attr(element, 'stroke', 'none')
prefig/core/tags.py CHANGED
@@ -11,6 +11,7 @@ from . import definition
11
11
  from . import graph
12
12
  from . import grid_axes
13
13
  from . import group
14
+ from . import image
14
15
  from . import implicit
15
16
  from . import label
16
17
  from . import legend
@@ -51,6 +52,7 @@ tag_dict = {
51
52
  'grid-axes': grid_axes.grid_axes,
52
53
  'group': group.group,
53
54
  'histogram': statistics.histogram,
55
+ 'image': image.image,
54
56
  'implicit-curve': implicit.implicit_curve,
55
57
  'label': label.label,
56
58
  'legend': legend.legend,
@@ -319,6 +319,7 @@ GraphicalElements =
319
319
  Grid* &
320
320
  Grid-Axes* &
321
321
  Histogram* &
322
+ Image*&
322
323
  Implicit-Curve* &
323
324
  Label* &
324
325
  Legend* &
@@ -520,6 +521,18 @@ Graph = element graph {
520
521
  CommonAttributes
521
522
  }
522
523
 
524
+ Image = element image {
525
+ attribute at {text}?,
526
+ attribute source{text},
527
+ attribute lower-left {text}?,
528
+ attribute center {text}?,
529
+ attribute dimensions {text}?,
530
+ attribute opacity {text}?,
531
+ attribute filetype {"svg"|"png"|"gif"|"jpeg"|"jpg"}?,
532
+ attribute rotate {text}?,
533
+ attribute scale {text}?
534
+ }
535
+
523
536
  Histogram = element histogram {
524
537
  attribute at {text}?,
525
538
  attribute data {text},
@@ -780,6 +780,9 @@
780
780
  <zeroOrMore>
781
781
  <ref name="Histogram"/>
782
782
  </zeroOrMore>
783
+ <zeroOrMore>
784
+ <ref name="Image"/>
785
+ </zeroOrMore>
783
786
  <zeroOrMore>
784
787
  <ref name="Implicit-Curve"/>
785
788
  </zeroOrMore>
@@ -1430,6 +1433,43 @@
1430
1433
  <ref name="CommonAttributes"/>
1431
1434
  </element>
1432
1435
  </define>
1436
+ <define name="Image">
1437
+ <element name="image">
1438
+ <optional>
1439
+ <attribute name="at"/>
1440
+ </optional>
1441
+ <attribute name="source"/>
1442
+ <optional>
1443
+ <attribute name="lower-left"/>
1444
+ </optional>
1445
+ <optional>
1446
+ <attribute name="center"/>
1447
+ </optional>
1448
+ <optional>
1449
+ <attribute name="dimensions"/>
1450
+ </optional>
1451
+ <optional>
1452
+ <attribute name="opacity"/>
1453
+ </optional>
1454
+ <optional>
1455
+ <attribute name="filetype">
1456
+ <choice>
1457
+ <value>svg</value>
1458
+ <value>png</value>
1459
+ <value>gif</value>
1460
+ <value>jpeg</value>
1461
+ <value>jpg</value>
1462
+ </choice>
1463
+ </attribute>
1464
+ </optional>
1465
+ <optional>
1466
+ <attribute name="rotate"/>
1467
+ </optional>
1468
+ <optional>
1469
+ <attribute name="scale"/>
1470
+ </optional>
1471
+ </element>
1472
+ </define>
1433
1473
  <define name="Histogram">
1434
1474
  <element name="histogram">
1435
1475
  <optional>
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: prefig
3
- Version: 0.3.9.dev20250728054658
3
+ Version: 0.3.9.dev20250730054824
4
4
  Summary: An authoring system for mathematical diagrams
5
5
  Home-page: https://prefigure.org
6
6
  License: GPL-3.0-or-later
@@ -1,11 +1,11 @@
1
1
  prefig/__init__.py,sha256=tAf78JkO5kBvFM23RmchK8TEDYO6v8AXqzOv-kq7TR0,53
2
2
  prefig/cli.py,sha256=-jbXTVb2Vao4uefq3ia69DDh7ZQivIO-H1grzCVPg0I,12495
3
3
  prefig/core/CTM.py,sha256=P32b5rfJD_8QwMNCHRAS6lIB5ellBi1gaSdhxTV4a00,5781
4
- prefig/core/__init__.py,sha256=1EoRwLI_BVkLiO5ulkKNd7MlEuAiLW7wnM_3KO92LCA,717
4
+ prefig/core/__init__.py,sha256=roT0a8-iBrp8WXTbPN5RzMWFiCXfpKrEXa2xE-F-yBo,728
5
5
  prefig/core/annotations.py,sha256=9wStnLhEHFmkIocufHLiBcUZ3NSce7ZESor5kjj-vzg,3998
6
6
  prefig/core/area.py,sha256=U-AxXvaZJWgVccpLlh-aaRfM_QtTa09NR2TqAd8Yk0o,3710
7
7
  prefig/core/arrow.py,sha256=h8lFm1rMdpiZRyQu5kvAiptBmMYr5aKCZuB5JypAGDM,11283
8
- prefig/core/axes.py,sha256=hTvw8uAuTlVaFFfOAv5xlhk0ol0TyZZTktsM_R_4wn0,31677
8
+ prefig/core/axes.py,sha256=-EIWIIfU8VFN2WLlPUQZFIO13PLXWvfja3wUlSbthQk,31677
9
9
  prefig/core/calculus.py,sha256=Q4kSGTTTeQYEEVKgmYWpFP0JLHmCsBchB5OF9qSclQk,498
10
10
  prefig/core/circle.py,sha256=_v1eMVhl4nnD4WXCz-Boa_SAdQg_kKeU3NhP42z2Yzw,15668
11
11
  prefig/core/clip.py,sha256=X3TcFYPuZwjZ9SFBWMc1MYY__6XTJqLafPeOLFNOdIE,806
@@ -14,9 +14,10 @@ prefig/core/coordinates.py,sha256=vEAJ8kL9S1VylxWysqY5qW2WyCMyHj42yFkuAzjfqFs,35
14
14
  prefig/core/definition.py,sha256=7LiaCxQwwEPdcnMpcMgrkZEfAoTc5ng8LO6Nf50Htwg,1033
15
15
  prefig/core/diagram.py,sha256=7Hsa8xOpGmLnln2Chl-ofVl_bG6ZOEJPGV2vE1V8RnI,22599
16
16
  prefig/core/diffeqs.py,sha256=aLXcmTsnfTG6L2-RiNfSaijzwmJF6xJcQpNQdDAWTQo,7127
17
- prefig/core/graph.py,sha256=DSgvfJdk-dsBX0yHwjHhI1nkD-yU9gfKAGjFwAQIoWk,9935
17
+ prefig/core/graph.py,sha256=q81dyJy3CxMWvMNhm74SZ9yjfltatdckmajxegA0z0A,10064
18
18
  prefig/core/grid_axes.py,sha256=v98C5tb9knNViGYGDrWL_ULb7Y78vMSQ6OJO7Xd0DXY,12165
19
19
  prefig/core/group.py,sha256=KByOibuslP9TqSygNfPSaN4zU5oSRYaH2CPnR21ogTg,5825
20
+ prefig/core/image.py,sha256=eQKy0mNvCE6J8EA1S5Y08YF3lrb7kCeGG4wrjBQ_-pI,4373
20
21
  prefig/core/implicit.py,sha256=JyFKpKj6Xi1PF-3BeZpd7t0aNe8jH9Wf_E2rghvt-Ug,6179
21
22
  prefig/core/label.py,sha256=e_gOd2kcdOtcJay1VyRstP66SiDNxK2UK2-obkxQnOc,24746
22
23
  prefig/core/label_tools.py,sha256=jOawiyrKC5iQs0vq7133xNXqrkJbMqDZVHflT-hOruo,9167
@@ -26,17 +27,17 @@ prefig/core/math_utilities.py,sha256=sMm3U9rcYKY8oZZWeJjcBokCDO4CxUkvmaX55auR4Eo
26
27
  prefig/core/network.py,sha256=1izAX2AKvnj2knElrdLeojE_0Q0zO0aQM-EsEwor0Os,27217
27
28
  prefig/core/parametric_curve.py,sha256=ftxdg3gs0M0ME5iPJo3iEX7jT8PHQEMYpx8psr8Cm-w,3046
28
29
  prefig/core/parse.py,sha256=plLmR0KKrUjYx0PmHHo5HZonaaT3tW4qjiDOtzuhSkQ,4542
29
- prefig/core/path.py,sha256=ZWKUNgVU4qeU98Vp4WKpQoy7DehH1UmnyXA4DKRhpGo,19365
30
+ prefig/core/path.py,sha256=8BbWAUzZqe1YD0gO4QtDENaINavAAC1mlU-y3UV-vu0,20312
30
31
  prefig/core/point.py,sha256=a97N1soBcmYn3StZ60HBmaBi3pB1abA7sL0LNJ2nxR0,8341
31
32
  prefig/core/polygon.py,sha256=8NVdWSUVXuNE4GiPQ4MySSmLy8aCNwKUqCcfHukizME,11301
32
33
  prefig/core/read.py,sha256=Q1Nsluwysg3M5wtUmwIirfNo-Rw9-dFJPEeM4pDCpyo,1988
33
- prefig/core/rectangle.py,sha256=T0EMtRdLtmG1uY7Bu8kXmxQ81IGG_102g5fLCg9RuzE,3387
34
+ prefig/core/rectangle.py,sha256=b_ki7yqA4-mke1_0khvKqSKBRToB667mZc-xZhhPIBA,3473
34
35
  prefig/core/repeat.py,sha256=DAO5w4BxUMGU-8ymsv8EwsifI0zPa_cgIRUcWWS8BWU,2755
35
36
  prefig/core/riemann_sum.py,sha256=T2dQgJIY17QuuPDOB8XWIP8HYmg5jOQ-LroFH7tiGAU,3012
36
37
  prefig/core/shape.py,sha256=Qb_-upbSHwEoYrrAnbCoFFFuVIb3JgJLdyP9ckD5evc,8675
37
38
  prefig/core/slope_field.py,sha256=e396uznWNaCUmOIIf70lsxvy9g_SGRFZlpxsIpy4ajE,8640
38
39
  prefig/core/statistics.py,sha256=5qN_-ABN0EFVG12ZP95uSPk82INbWRqI3w2fUdR3wsw,3458
39
- prefig/core/tags.py,sha256=fj86TjSkSpo0cAYmp0OQ83ULX9B8Nz4PRsNyZsR2JMk,4088
40
+ prefig/core/tags.py,sha256=7w6E3960o_ZgZl4xYJLVJ1eLttt0vVlB4IgMZGTMyyE,4134
40
41
  prefig/core/tangent_line.py,sha256=I9wf9VAqhnmmsh8Lzynwi7_Z1DonHXqHf8ym7eO40tk,3400
41
42
  prefig/core/user_namespace.py,sha256=6xlkYd2g9dSiDPH0vmYj93oPzm1BFZdgyMGQ9ipKOgU,7059
42
43
  prefig/core/utilities.py,sha256=R1fBrzj-abXHG4Bh-sS0pZNC8vc9fIt6yJBcuztCVho,2905
@@ -55,13 +56,13 @@ prefig/resources/examples/tangent.xml,sha256=FLZaNz0iFoTLX6gB4pfLZFtu_TWeCug4Dda
55
56
  prefig/resources/fonts/Braille29.ttf,sha256=T4hosDvaUGMVK0Wh0BmBgWeT7sQQmLXyA3gRUR-a0oE,55880
56
57
  prefig/resources/js/mj-sre-page.js,sha256=LHVeI4dCH13bTv56nz3SKNo3JGRXa6joPM3D3wpKV40,9251
57
58
  prefig/resources/js/package.json,sha256=hwKDg76_GzkhX72T7DRiF3FoMLokCv9PSLQvnW5Ovok,116
58
- prefig/resources/schema/pf_schema.rnc,sha256=KAmcFfhLpVRVG9nFzdHcPOMdEj62aUOMs6BXqiXy7jc,19824
59
- prefig/resources/schema/pf_schema.rng,sha256=OfOY7Pgd_G6V07v6kqRVJlvyAbyclWBSX9Q8Br97CKo,53495
59
+ prefig/resources/schema/pf_schema.rnc,sha256=WORhF-dRkNY8aeiNQvZIaPTE5QGCMzbrXqttXCjtOJY,20166
60
+ prefig/resources/schema/pf_schema.rng,sha256=TmZQLgyK2wntXDXOiQXYcVQedlPuPh8IWrtttK0iCQY,54454
60
61
  prefig/resources/template/pf_publication.xml,sha256=eEv8HACv610Apw5DSVNy0reLfELYqHlNy9Oq0GH7C_c,200
61
62
  prefig/scripts/__init__.py,sha256=qJcPi1WRh9UAwu4zIFJbmxWalAMilMYqgi6LxRkF7bI,25
62
63
  prefig/scripts/install_mj.py,sha256=5A6-oc1xbIXka5mkFE70vlp9-Rh_QoxOxGMrQi9Hkio,1219
63
- prefig-0.3.9.dev20250728054658.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
64
- prefig-0.3.9.dev20250728054658.dist-info/METADATA,sha256=kqDcIYXexeaIQR_LzQ9tVV7kntpcoTyqFzjSh28PbdQ,8728
65
- prefig-0.3.9.dev20250728054658.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
66
- prefig-0.3.9.dev20250728054658.dist-info/entry_points.txt,sha256=OP4ZQT71q2b0Zfbie-oM2Z1HlxpkuX7qaxJnzwsBOVQ,42
67
- prefig-0.3.9.dev20250728054658.dist-info/RECORD,,
64
+ prefig-0.3.9.dev20250730054824.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
65
+ prefig-0.3.9.dev20250730054824.dist-info/METADATA,sha256=FZsmOvFwrgQuC2gNmrd283Sq0qRzWP4sWSoQurCHUqg,8728
66
+ prefig-0.3.9.dev20250730054824.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
67
+ prefig-0.3.9.dev20250730054824.dist-info/entry_points.txt,sha256=OP4ZQT71q2b0Zfbie-oM2Z1HlxpkuX7qaxJnzwsBOVQ,42
68
+ prefig-0.3.9.dev20250730054824.dist-info/RECORD,,