qmlmathplot 0.4.2__py3-none-any.whl → 0.4.4__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.
- qmlmathplot/model.py +110 -6
- {qmlmathplot-0.4.2.dist-info → qmlmathplot-0.4.4.dist-info}/METADATA +6 -3
- {qmlmathplot-0.4.2.dist-info → qmlmathplot-0.4.4.dist-info}/RECORD +5 -5
- {qmlmathplot-0.4.2.dist-info → qmlmathplot-0.4.4.dist-info}/WHEEL +1 -1
- {qmlmathplot-0.4.2.dist-info → qmlmathplot-0.4.4.dist-info}/entry_points.txt +0 -0
qmlmathplot/model.py
CHANGED
|
@@ -358,6 +358,12 @@ float _seg_dist(vec2 p, vec2 a, vec2 b) {
|
|
|
358
358
|
return length(p - (a + t * ab));
|
|
359
359
|
}
|
|
360
360
|
|
|
361
|
+
// A vertical interval [lo, hi] at this pixel's y, with a one-pixel feather at its edges.
|
|
362
|
+
float _span_cov(float y, float lo, float hi, float sy) {
|
|
363
|
+
return clamp((y - lo) * sy + 1.0, 0.0, 1.0)
|
|
364
|
+
* clamp((hi - y) * sy + 1.0, 0.0, 1.0);
|
|
365
|
+
}
|
|
366
|
+
|
|
361
367
|
// GLSL pow() is undefined for a negative base; odd roots are real there (x**(1/3)).
|
|
362
368
|
float _real_pow(float v, float p) {
|
|
363
369
|
return v < 0.0 ? -pow(-v, p) : pow(v, p);
|
|
@@ -471,6 +477,11 @@ void main() {
|
|
|
471
477
|
}
|
|
472
478
|
}
|
|
473
479
|
|
|
480
|
+
// A pole inside this column is a jump, even when a monotone map such as exp()
|
|
481
|
+
// turns the two one-sided limits into a connected range (exp(tan(x)): 0 and +inf).
|
|
482
|
+
// The interval paths below would bridge that jump with a vertical connector.
|
|
483
|
+
bool pole_col = HAS_SIGN_POLE && POLE(x - 0.5 * dx) * POLE(x + 0.5 * dx) <= 0.0;
|
|
484
|
+
|
|
474
485
|
// ---- fixed-x sub-pixel polyline (GeoGebra's sampled polyline, on the GPU) ----
|
|
475
486
|
// 14 samples over ±2 columns (0.286-column spacing) cover the perpendicular foot even at
|
|
476
487
|
// 89°: a ±1-column window lost 42 % of the width at 84°. Joining the samples gives a
|
|
@@ -488,9 +499,28 @@ void main() {
|
|
|
488
499
|
bool prev_ok = false;
|
|
489
500
|
float prev_d = 0.0;
|
|
490
501
|
bool prev_d_ok = false;
|
|
502
|
+
int pole_crossings = 0;
|
|
503
|
+
#if HAS_SIGN_POLE
|
|
504
|
+
if (abs(camera.z) > 1.0) {
|
|
505
|
+
float pp = POLE(x - 2.0 * dx);
|
|
506
|
+
for (int pi = 0; pi < 32; pi++) {
|
|
507
|
+
float tt = -2.0 + 4.0 * fract((float(pi) + 0.5) * 0.61803398875);
|
|
508
|
+
float px = x + tt * dx;
|
|
509
|
+
float pv = POLE(px);
|
|
510
|
+
if (pp * pv <= 0.0) pole_crossings += 1;
|
|
511
|
+
pp = pv;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
#endif
|
|
515
|
+
float prev_pole = 1.0;
|
|
491
516
|
for (int i = 0; i < NS; i++) {
|
|
492
517
|
float t = -2.0 + (float(i) + 0.5) * (4.0 / float(NS));
|
|
493
518
|
float xi = x + t * dx;
|
|
519
|
+
float pv = POLE(xi);
|
|
520
|
+
if (i > 0 && prev_pole * pv <= 0.0) {
|
|
521
|
+
pole_crossings += 1;
|
|
522
|
+
}
|
|
523
|
+
prev_pole = pv;
|
|
494
524
|
float v = 1e30;
|
|
495
525
|
if (DOMS(xi)) {
|
|
496
526
|
v = F(xi);
|
|
@@ -527,14 +557,61 @@ void main() {
|
|
|
527
557
|
}
|
|
528
558
|
}
|
|
529
559
|
|
|
560
|
+
// A single column path, as GeoGebra's IntervalPath: every resolvable connected column
|
|
561
|
+
// contributes one chord from its left-edge value to its right-edge value. The sampled
|
|
562
|
+
// polyline is only a fallback for columns where the interval cannot be oriented.
|
|
563
|
+
bool use_path = col.kind == IK_CONNECTED && col.lo > -IINF && col.hi < IINF
|
|
564
|
+
&& !pole_col && !HAS_DEPENDENT_QUOTIENT && sturns < 1.5;
|
|
565
|
+
bool inc = true;
|
|
566
|
+
if (use_path) {
|
|
567
|
+
int fi = -1;
|
|
568
|
+
int li = -1;
|
|
569
|
+
for (int i = 0; i < NS; i++) {
|
|
570
|
+
if (ok[i]) {
|
|
571
|
+
if (fi < 0) fi = i;
|
|
572
|
+
li = i;
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
if (fi >= 0 && li > fi && smp[li] < smp[fi]) {
|
|
576
|
+
inc = false;
|
|
577
|
+
}
|
|
578
|
+
float y0 = inc ? col.lo : col.hi;
|
|
579
|
+
float y1 = inc ? col.hi : col.lo;
|
|
580
|
+
pmin = min(pmin, _seg_dist(vec2(x * sx, y * sy),
|
|
581
|
+
vec2((x - 0.5 * dx) * sx, y0 * sy),
|
|
582
|
+
vec2((x + 0.5 * dx) * sx, y1 * sy)));
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// Neighbour joins: the chord above ends at this column's edges; the previous/next
|
|
586
|
+
// columns' chords end at the same boundary but from their own interval. A short
|
|
587
|
+
// boundary join removes the 1-px gap/step at the hand-off.
|
|
588
|
+
if (use_path && (col.hi - col.lo) * sy > 2.0 * R) {
|
|
589
|
+
IVal prevc = FI(x - 1.5 * dx, x - 0.5 * dx);
|
|
590
|
+
IVal nextc = FI(x + 0.5 * dx, x + 1.5 * dx);
|
|
591
|
+
if (prevc.kind == IK_CONNECTED && prevc.lo > -IINF && prevc.hi < IINF) {
|
|
592
|
+
float left = inc ? prevc.hi : prevc.lo;
|
|
593
|
+
float here = inc ? col.lo : col.hi;
|
|
594
|
+
pmin = min(pmin, _seg_dist(vec2(x * sx, y * sy),
|
|
595
|
+
vec2((x - 0.5 * dx) * sx, left * sy),
|
|
596
|
+
vec2((x - 0.5 * dx) * sx, here * sy)));
|
|
597
|
+
}
|
|
598
|
+
if (nextc.kind == IK_CONNECTED && nextc.lo > -IINF && nextc.hi < IINF) {
|
|
599
|
+
float here = inc ? col.hi : col.lo;
|
|
600
|
+
float right = inc ? nextc.lo : nextc.hi;
|
|
601
|
+
pmin = min(pmin, _seg_dist(vec2(x * sx, y * sy),
|
|
602
|
+
vec2((x + 0.5 * dx) * sx, here * sy),
|
|
603
|
+
vec2((x + 0.5 * dx) * sx, right * sy)));
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
530
607
|
// GeoGebra's unbounded-column rule (IntervalPath.drawNormalInfinity): only when the
|
|
531
608
|
// finite bound itself is on screen. The segment uses the column's left edge and middle.
|
|
532
|
-
if (col.kind == IK_CONNECTED && col.lo <= -IINF
|
|
609
|
+
if (!pole_col && col.kind == IK_CONNECTED && col.lo <= -IINF
|
|
533
610
|
&& col.hi >= y_bot && col.hi <= y_top) {
|
|
534
611
|
pmin = min(pmin, _seg_dist(vec2(x * sx, y * sy),
|
|
535
612
|
vec2((x - 0.5 * dx) * sx, col.hi * sy), vec2(x * sx, y_bot * sy)));
|
|
536
613
|
}
|
|
537
|
-
if (col.kind == IK_CONNECTED && col.hi >= IINF
|
|
614
|
+
if (!pole_col && col.kind == IK_CONNECTED && col.hi >= IINF
|
|
538
615
|
&& col.lo >= y_bot && col.lo <= y_top) {
|
|
539
616
|
pmin = min(pmin, _seg_dist(vec2(x * sx, y * sy),
|
|
540
617
|
vec2((x - 0.5 * dx) * sx, col.lo * sy), vec2(x * sx, y_top * sy)));
|
|
@@ -543,11 +620,11 @@ void main() {
|
|
|
543
620
|
// covers it (the visible curve lies inside this x column). GeoGebra's normal-infinity
|
|
544
621
|
// branch declines to draw here; the column is effectively whole (drawWhole), so a
|
|
545
622
|
// vertical segment at the left edge is GGB's own rule for that topology.
|
|
546
|
-
if (col.kind == IK_CONNECTED && col.lo <= -IINF && col.hi > y_top) {
|
|
623
|
+
if (!pole_col && col.kind == IK_CONNECTED && col.lo <= -IINF && col.hi > y_top) {
|
|
547
624
|
pmin = min(pmin, _seg_dist(vec2(x * sx, y * sy),
|
|
548
625
|
vec2((x - 0.5 * dx) * sx, y_bot * sy), vec2((x - 0.5 * dx) * sx, y_top * sy)));
|
|
549
626
|
}
|
|
550
|
-
if (col.kind == IK_CONNECTED && col.hi >= IINF && col.lo < y_bot) {
|
|
627
|
+
if (!pole_col && col.kind == IK_CONNECTED && col.hi >= IINF && col.lo < y_bot) {
|
|
551
628
|
pmin = min(pmin, _seg_dist(vec2(x * sx, y * sy),
|
|
552
629
|
vec2((x - 0.5 * dx) * sx, y_bot * sy), vec2((x - 0.5 * dx) * sx, y_top * sy)));
|
|
553
630
|
}
|
|
@@ -561,11 +638,15 @@ void main() {
|
|
|
561
638
|
pmin = min(pmin, _seg_dist(vec2(x * sx, y * sy),
|
|
562
639
|
vec2((x + 0.5 * dx) * sx, y_top * sy),
|
|
563
640
|
vec2((x + 0.5 * dx) * sx, col.hi * sy)));
|
|
641
|
+
pmin = min(pmin, _seg_dist(vec2(x * sx, y * sy),
|
|
642
|
+
vec2(x * sx, y_top * sy), vec2(x * sx, col.hi * sy)));
|
|
564
643
|
}
|
|
565
644
|
if (col.hi > y_bot) {
|
|
566
645
|
pmin = min(pmin, _seg_dist(vec2(x * sx, y * sy),
|
|
567
646
|
vec2((x - 0.5 * dx) * sx, y_bot * sy),
|
|
568
647
|
vec2((x - 0.5 * dx) * sx, col.lo * sy)));
|
|
648
|
+
pmin = min(pmin, _seg_dist(vec2(x * sx, y * sy),
|
|
649
|
+
vec2(x * sx, y_bot * sy), vec2(x * sx, col.lo * sy)));
|
|
569
650
|
}
|
|
570
651
|
}
|
|
571
652
|
|
|
@@ -573,6 +654,8 @@ void main() {
|
|
|
573
654
|
if (col.kind == IK_WHOLE && FILL_WHOLE) {
|
|
574
655
|
pmin = min(pmin, _seg_dist(vec2(x * sx, y * sy),
|
|
575
656
|
vec2((x - 0.5 * dx) * sx, y_bot * sy), vec2((x - 0.5 * dx) * sx, y_top * sy)));
|
|
657
|
+
pmin = min(pmin, _seg_dist(vec2(x * sx, y * sy),
|
|
658
|
+
vec2(x * sx, y_bot * sy), vec2(x * sx, y_top * sy)));
|
|
576
659
|
}
|
|
577
660
|
|
|
578
661
|
float band_cov = 0.0;
|
|
@@ -612,7 +695,22 @@ void main() {
|
|
|
612
695
|
if (HAS_DEPENDENT_QUOTIENT && !FILL_WHOLE) {
|
|
613
696
|
band_cov = 0.0; // dependency overestimation: the polyline draws the real branches
|
|
614
697
|
}
|
|
615
|
-
float
|
|
698
|
+
float dense = 0.0;
|
|
699
|
+
if (HAS_SIGN_POLE && pole_crossings >= 1 && abs(camera.z) > 1.0
|
|
700
|
+
&& col.kind != IK_EMPTY) {
|
|
701
|
+
// Several poles inside the sampling window: the column is undersampled, so fill its
|
|
702
|
+
// *attained* y interval instead of drawing one branch per sample. Inverted means the
|
|
703
|
+
// gap [lo, hi] is not attained; filling it would paint the asymptote.
|
|
704
|
+
if (col.kind == IK_WHOLE) {
|
|
705
|
+
dense = 1.0;
|
|
706
|
+
} else if (col.kind == IK_INVERTED) {
|
|
707
|
+
dense = max(_span_cov(y, y_bot, col.lo, sy),
|
|
708
|
+
_span_cov(y, col.hi, y_top, sy));
|
|
709
|
+
} else {
|
|
710
|
+
dense = _span_cov(y, max(col.lo, y_bot), min(col.hi, y_top), sy);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
float cov = max(max(band_cov, dense), clamp(R - pmin, 0.0, 1.0));
|
|
616
714
|
if (!DOM(x)) {
|
|
617
715
|
float a = cov * color.a * qt_Opacity;
|
|
618
716
|
fragColor = vec4(color.rgb * a, a);
|
|
@@ -1287,8 +1385,14 @@ void main() {
|
|
|
1287
1385
|
// such as x² + y² = 0 is not left to the projection alone.
|
|
1288
1386
|
// GGB measures the singular cell in *world* derivative units (|Fx| + |Fy| < 0.001);
|
|
1289
1387
|
// the per-pixel gradient of an ordinary circle is ~0.01 and must not qualify.
|
|
1388
|
+
// The distance gate is essential for a scaled expression: (x+y)/(x*y) has |F| < 0.05 and
|
|
1389
|
+
// |Fx|+|Fy| < 0.001 over huge regions around ±50, although the level set is far away. A
|
|
1390
|
+
// singular point must also be *on* the set to the first order: |F| ~ R*|grad F|. The
|
|
1391
|
+
// ratio is scale-invariant, unlike the two absolute thresholds above.
|
|
1290
1392
|
float gw_s = abs(FX(x, y)) + abs(FY(x, y));
|
|
1291
|
-
|
|
1393
|
+
float gnorm_s = max(sqrt(gx * gx + gy * gy), 1e-9);
|
|
1394
|
+
if (dom && (f == f) && (abs(f) < 1e30) && gw_s < 1e-3 && abs(f) < 0.05
|
|
1395
|
+
&& abs(f) < R * gnorm_s) {
|
|
1292
1396
|
march_cov = 1.0;
|
|
1293
1397
|
}
|
|
1294
1398
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: qmlmathplot
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.4
|
|
4
4
|
Summary: Embeddable function plotter for Qt Quick / QtWidgets (implicit per-pixel drawing, MVVM)
|
|
5
5
|
Author: I-AM-A-NOOB
|
|
6
6
|
Author-email: I-AM-A-NOOB <suzc08@outlook.com>
|
|
@@ -281,8 +281,11 @@ corner is in the domain and no pole factor changes sign between them; the edge c
|
|
|
281
281
|
interpolated and the cell's contour segment is stroked (the saddle case is paired using the cell
|
|
282
282
|
centre's sign). The first-order/Newton path still supplies the sub-pixel width; the cell term
|
|
283
283
|
adds the coverage of a thin or tangent branch whose centre projection fails. A **singular cell**
|
|
284
|
-
(`|Fx| + |Fy| < 1e-3` in world units,
|
|
285
|
-
such as `x**2 + y**2 = 0` is not left to the projection alone.
|
|
284
|
+
(`|Fx| + |Fy| < 1e-3` in world units, `|F| < 0.05`, **and** `|F| < R·|grad F|` in pixels) gets
|
|
285
|
+
a small disc, so an isolated point such as `x**2 + y**2 = 0` is not left to the projection alone.
|
|
286
|
+
The last test is the scale-invariant one: it requires the point to be on the set to first order,
|
|
287
|
+
so a scaled expression such as `(x+y)/(x·y)` — whose F and gradient are tiny far from its line —
|
|
288
|
+
no longer fills whole quadrants with the disc.
|
|
286
289
|
|
|
287
290
|
Still not ported: GGB's Bernstein subdivision (**polynomial-only**), its adaptive quad-tree
|
|
288
291
|
refinement and the `ContourAssembler`/`ContourLinker` (they exist to produce a selectable vector
|
|
@@ -9,7 +9,7 @@ qmlmathplot/export.py,sha256=KmWhh87rHOcdWfHo0erRvPgva_xm_RK8Uxf5HCbTv30,9276
|
|
|
9
9
|
qmlmathplot/figure.py,sha256=iuCIuRExdcq36lDj-BA6QSsh2STeBLbgCVbvU7q_c2Y,20777
|
|
10
10
|
qmlmathplot/input.py,sha256=Mb1rL-_Zynl1K-mhKEjjCv_BOA31cHekpL79eMdWUw0,6036
|
|
11
11
|
qmlmathplot/interval.py,sha256=MoWEE6piNPr5cqGNf7nHKXu4RioROJg6qyBvtydhyyQ,21362
|
|
12
|
-
qmlmathplot/model.py,sha256=
|
|
12
|
+
qmlmathplot/model.py,sha256=zX73YzjATEcEtaKMeCd7NJQub3rkP1VCoB2JChFvezI,77179
|
|
13
13
|
qmlmathplot/mpl.py,sha256=4xjKTwzv9liTaRCsKGNG-lmvRtrBHOv8vBJGfkAZ41o,13358
|
|
14
14
|
qmlmathplot/overlay.py,sha256=QY-daroe7Nrs8F5t7XcFR82xfRDstZDHq9JDFfPehYs,13171
|
|
15
15
|
qmlmathplot/plot.py,sha256=MstIKlXFCvnALxFYVZBBr8LWfuU9D-iS33P5e3W66d0,25851
|
|
@@ -53,7 +53,7 @@ qmlmathplot/themes/tableau-colorblind10.mplstyle,sha256=PzUMoOtw0V6l0bPk8ApRAKvc
|
|
|
53
53
|
qmlmathplot/themes.py,sha256=QocZLOgPYXCJW9fYc6i5q6nHEE_S4mQVZXySZ4ISCPw,9225
|
|
54
54
|
qmlmathplot/view.py,sha256=cQVzX7WdFXdAGjNu0LzZMzgFfCmLwfBsUMZFQrsWFy8,1775
|
|
55
55
|
qmlmathplot/widget.py,sha256=7IkjM13KWJ9rqTkhooSMHSE9LjR9PD4OPim4qbKvPcA,9648
|
|
56
|
-
qmlmathplot-0.4.
|
|
57
|
-
qmlmathplot-0.4.
|
|
58
|
-
qmlmathplot-0.4.
|
|
59
|
-
qmlmathplot-0.4.
|
|
56
|
+
qmlmathplot-0.4.4.dist-info/WHEEL,sha256=3NO1oYsF7mANFUWVdyEQLM-zmecczWWEmmF5rEbXrWk,80
|
|
57
|
+
qmlmathplot-0.4.4.dist-info/entry_points.txt,sha256=4z36D_RpPSYLiJDRM8iXnrSe1T4c2alfQJPcJTss3qY,54
|
|
58
|
+
qmlmathplot-0.4.4.dist-info/METADATA,sha256=3S69MPupAyqv4CRkCIDhcMY0T19VnW1sVKFUs9VfrAQ,53532
|
|
59
|
+
qmlmathplot-0.4.4.dist-info/RECORD,,
|
|
File without changes
|