summaryrefslogtreecommitdiff
path: root/Build/source/utils/asymptote/base/trembling.asy
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/utils/asymptote/base/trembling.asy')
-rw-r--r--Build/source/utils/asymptote/base/trembling.asy369
1 files changed, 369 insertions, 0 deletions
diff --git a/Build/source/utils/asymptote/base/trembling.asy b/Build/source/utils/asymptote/base/trembling.asy
new file mode 100644
index 00000000000..6b94317b1c4
--- /dev/null
+++ b/Build/source/utils/asymptote/base/trembling.asy
@@ -0,0 +1,369 @@
+// Copyright (c) 2008, Philippe Ivaldi.
+// trembling.asy: handwriting package for the software Asymptote.
+// Last modified: Mon Jul 7 23:35:36 CEST 2008
+
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or (at
+// your option) any later version.
+
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+// 02110-1301, USA.
+
+// COMMENTARY:
+
+// THANKS:
+
+// BUGS:
+// magnetic points are experimental...
+
+// CODE:
+
+import geometry;
+import stats;
+
+/*<asyxml><variable type="pair[]" signature="magneticPoints"><code></asyxml>*/
+pair[] magneticPoints;
+/*<asyxml></code><documentation>Array of magnetic points.
+ When a point is magnetized, a trembled path will pass though P if the original path passes through P.
+ When trembling is enabled with the routine 'startTrembling' all the drawn points is added to this array.
+ For convenience, one can magnetize an arbitrary number of points with the routine 'magnetize'.<look href="#magnetize(...point[])"><look href="#startTrembling(real,real,real,real,bool">
+ </documentation></variable></asyxml>*/
+real magneticRadius=1; // unit is bp in postscript coordinates.
+real trembleFuzz(){return min(1e-3,magneticRadius/10);}
+// real trembleFuzz(){return min(1e-1,1);}
+
+pair[] single(pair[] P)
+{
+ pair[] op;
+ bool allow;
+ for (int i=0; i < P.length-1; ++i) {
+ allow=true;
+ for (int j=i+1; j < P.length; ++j) {
+ if(abs(P[i]-P[j]) < magneticRadius) {
+ allow=false;
+ break;
+ }
+ }
+ if(allow) op.push(P[i]);
+ }
+ if(P.length > 0) op.push(P[P.length-1]);
+ return op;
+}
+
+/*<asyxml><function type="pair" signature="attract(pair,path,real)"><code></asyxml>*/
+real atime(pair m, path g, real fuzz=trembleFuzz())
+{/*<asyxml></code><documentation>Return the time of the nearest point of 'm' which is on the path g.
+ 'fuzz' is the argument 'fuzz' of 'intersect'.</documentation></function></asyxml>*/
+ if(length(g) == 0) return 0.0;
+ real[] t=intersect(m,g,fuzz);
+ if(t.length > 0) return t[1];
+ real ot;
+ static real eps=sqrt(realEpsilon);
+ real abmax=abs(max(g)-m), abmin=abs(min(g)-m);
+ real initr=abs(m-midpoint(g));
+ real maxR=2*max(abmax,abmin), step=eps, r=initr;
+ real shx=1e-4;
+ transform T=shift(m);
+ path ig;
+ // do {
+ // do {// Find a radius for intersection
+ // r += step;
+ // ig=T*scale(r)*unitcircle;
+ // if(ig == g) {
+ // T *= shift(shx,0);
+ // ig=T*scale(r)*unitcircle;
+ // }
+ // t=intersect(ig,g);
+ // } while(t.length <= 0 && r <= maxR && inside(g,ig) != 1);//
+ // if(t.length <= 0) { // degenerated case
+ // r=initr;
+ // T *= shift(shx,0);
+ // write("warning: atime needs numerical adjustment.");
+ // }
+ // } while(t.length <= 0);
+ if(t.length > 0) ot=t[1];
+ real rm=0, rM=r;
+ while(rM-rm > eps) {
+ r=(rm+rM)/2;
+ t=intersect(T*scale(r)*unitcircle,g,fuzz);
+ if(t.length <= 0) {
+ rm=r;
+ } else {
+ rM=r;
+ ot=t[1];
+ }
+ }
+ return ot;
+}
+
+/*<asyxml><function type="void" signature="magnetize(...pair[])"><code></asyxml>*/
+void magnetize(...pair[] P)
+{/*<asyxml></code><documentation>Magnetize the points P.
+ When a point is magnetized, a trembled path will pass though P if the original path passes through P.</documentation></function></asyxml>*/
+ if(magneticRadius <= 0) return;
+ currentpicture.uptodate=false;
+ currentpicture.nodes.insert(0,
+ new void(frame f, transform t, transform T, pair lb, pair rt)
+ {
+ for (pair PT:P) {
+ magneticPoints.push(t*PT);
+ }
+ });
+}
+
+/*<asyxml><function type="void" signature="magnetize(...triangle[])"><code></asyxml>*/
+void magnetize(...triangle[] t)
+{/*<asyxml></code><documentation>Magnetize the vertices of the triangles t.<look href="#magnetize(...pair[])"/></documentation></function></asyxml>*/
+ for(int i=0; i < t.length; ++i)
+ magnetize(...new point[] {t[i].A, t[i].B, t[i].C});
+}
+
+void trueMagnetize(pair p)
+{
+ if(magneticRadius <= 0) return;
+ magneticPoints.push(p);
+}
+
+/*<asyxml><function type="path" signature="addnode(path,real)"><code></asyxml>*/
+path addnode(path g, real t)
+{/*<asyxml></code><documentation>Add a node to 'g' at point(g,t).</documentation></function></asyxml>*/
+ real l=length(g);
+ real rt=t%1;
+ if (l==0 || (t > l && !cyclic(g)) || rt == 0) return g;
+ if(cyclic(g)) t=t%l;
+ int t0=floor(t);
+ int t1=t0+1;
+ pair z0=point(g,t0), z1=point(g,t1),
+ c0=postcontrol(g,t0), c1=precontrol(g,t1),
+ m0=(1-rt)*z0+rt*c0, m1=(1-rt)*c0+rt*c1,
+ m2=(1-rt)*c1+rt*z1, m3=(1-rt)*m0+rt*m1,
+ m4=(1-rt)*m1+rt*m2;
+ guide og=subpath(g,0,t0);
+ og=og..controls m0 and m3..point(g,t);
+ if(cyclic(g))
+ if(t1 < l) {
+ og=og..controls m4 and m2..subpath(g,t1,l)&cycle;
+ } else og=og..controls m4 and m2..cycle;
+ else og=og..controls m4 and m2..subpath(g,t1,l);
+ return og;
+}
+
+path addnodes(path g, real fuzz=trembleFuzz() ...pair[] P)
+{
+ pair[] P=single(P);
+ if(length(g) == 0 || P.length == 0 || magneticRadius <= 0) return g;
+ path og=g;
+ for(pair tp:P) {
+ real t=atime(tp,og,fuzz);
+ real d=abs(tp-point(og,t));
+ if(d < magneticRadius) og=addnode(og,t);
+ }
+ return og;
+}
+
+/*<asyxml><function type="path" signature="addnodes(path,int)"><code></asyxml>*/
+path addnodes(path g, int n)
+{/*<asyxml></code><documentation>Add 'n' nodes between each node of 'g'.</documentation></function></asyxml>*/
+ real l=length(g);
+ if(n == 0 || l == 0) return g;
+ path og=g;
+ int np=0;
+ for (int i=0; i < l; ++i) {
+ real step=1/(n+1);
+ for (int j=0; j < n; ++j) {
+ og=addnode(og,i*(n+1)+j+step);
+ step=1/(n-j);
+ }
+ }
+ return og;
+}
+
+
+
+/*<asyxml><variable type="real" signature="trembleAngle"><code></asyxml>*/
+real trembleAngle=4, trembleFrequency=0.5, trembleRandom=2;/*<asyxml></code><documentation>Variables used by the routine 'tremble'.</documentation></variable></asyxml>*/
+
+/*<asyxml><function type="path" signature="tremble(path,real,real,real,real)"><code></asyxml>*/
+path tremble(path g,
+ real angle=trembleAngle,
+ real frequency=trembleFrequency,
+ real random=trembleRandom,
+ real fuzz=trembleFuzz())
+{/*<asyxml></code><documentation>Return g as it was handwriting.
+ The postcontrols and precontrols of the nodes of g will be rotated
+ by an angle proportional to 'angle' (in degrees).
+ If frequency < 1, floor(1/frequency) nodes will be added to g to increase the
+ control points.
+ If frequency >= 1, one point for floor(frequency) will be used to deform the path.
+ 'random' controls the randomized coefficient which will be multiplied 'angle'.
+ random is 0 means don't use randomized coefficient;
+ More 'random' is hight more the coefficient is hight and the trembling seems randomized.</documentation></function></asyxml>*/
+ if(length(g) == 0) return g;
+ path tg=g;
+ frequency=abs(frequency);
+ int f=abs(floor(1/frequency)-1);
+ tg=addnodes(tg,f);
+ int frequency=floor(frequency);
+ int tf=(frequency == 0) ? 1 : frequency;
+ int l=length(tg);
+ guide og=point(tg,0);
+ random=abs(random);
+ int rsgn(real x){
+ int d2=floor(100*x)-10*floor(10*x);
+ if(d2 == 0) return 1;
+ return 2%d2 == 0 ? 1 : -1;
+ }
+ real randf()
+ {
+ real or;
+ if(random != 0)
+ if(1%tf != 0) or=0; else {
+ real ur=unitrand();
+ or=rsgn(ur)*angle*(1+ur^(1/random));
+ }
+ else or=rsgn(unitrand())*1.5*angle;
+ return or;
+ }
+
+ real first=randf(), a=first;
+ real gle;
+ for(int i=1; i <= l; ++i)
+ {
+ pair P=point(tg,i);
+ // gle=(i == l && (cyclic(tg))) ? first : a;
+ // pair post=rotate(gle,point(tg,i-1))*postcontrol(tg,i-1);
+ // pair pre=rotate(gle,P)*precontrol(tg,i);
+ a=randf();
+ pair post=rotate(a,point(tg,i-1))*postcontrol(tg,i-1);
+ pair pre=rotate((a+randf())/2,P)*precontrol(tg,i);
+ if(i == l && (cyclic(tg)))
+ og=og..controls post and pre..cycle;
+ else
+ og=og..controls post and pre..P;
+ }
+ return og;
+}
+
+typedef path pathModifier(path);
+pathModifier NoModifier=new path(path g){return g;};
+pathModifier tremble(real angle=trembleAngle,
+ real frequency=trembleFrequency,
+ real random=trembleRandom,
+ real fuzz=trembleFuzz())
+{
+ return new path(path g){return tremble(g,angle,frequency,random,fuzz);};
+}
+
+void orig_draw(frame f, path g, pen p=currentpen)=draw;
+void tremble_draw(frame f, path g, pen p=currentpen){}
+
+void tremble_marknodes(picture pic=currentpicture, frame f, path g) {}
+void tremble_markuniform(bool centered=false, int n, bool rotated=false) {}
+
+int tremble_circlenodesnumber(real r){return 5;}
+int orig_circlenodesnumber(real r)=circlenodesnumber;
+
+int tremble_circlenodesnumber1(real r, real angle1, real angle2){return 4;}
+int orig_circlenodesnumber1(real r, real angle1, real angle2)=circlenodesnumber;
+
+int tremble_ellipsenodesnumber(real a, real b){return 50;}
+int orig_ellipsenodesnumber(real a, real b)=ellipsenodesnumber;
+
+int tremble_ellipsenodesnumber1(real a, real b, real angle1, real angle2, bool dir){return 20;}
+int orig_ellipsenodesnumber1(real a, real b, real angle1, real angle2, bool dir)=ellipsenodesnumber;
+
+int tremble_parabolanodesnumber(parabola p, real angle1, real angle2){return 20;}
+int orig_parabolanodesnumber(parabola p, real angle1, real angle2)=parabolanodesnumber;
+
+int tremble_hyperbolanodesnumber(hyperbola h, real angle1, real angle2){return 20;}
+int orig_hyperbolanodesnumber(hyperbola h, real angle1, real angle2)=hyperbolanodesnumber;
+
+restricted bool tremblingMode=false;
+/*<asyxml><function type="void" signature="startTrembling(real,real,real,real,bool"><code></asyxml>*/
+void startTrembling(real angle=trembleAngle,
+ real frequency=trembleFrequency,
+ real random=trembleRandom,
+ real fuzz=trembleFuzz(),
+ bool magnetizePoints=true)
+{/*<asyxml></code><documentation>Calling this routine all drawn paths will be trembled with the givens parameters.<look href="#tremble(path,real,real,real,real)"/>
+ If 'magnetizePoints' is true, most dotted points are automatically magnetized.<look href="magnetize(...pair[])"/></documentation></function></asyxml>*/
+ if(!tremblingMode) {
+ tremblingMode=true;
+ tremble_draw=new void(frame f, path g, pen p=currentpen)
+ {
+ if(length(g) == 0 && magnetizePoints) {
+ trueMagnetize(point(g,0));
+ }
+ g=addnodes(g,fuzz*abs(max(g)-min(g)) ...magneticPoints);
+ orig_draw(f,tremble(g,angle,frequency,random,fuzz),p);
+ };
+
+ plain.draw=tremble_draw;
+
+ if(magnetizePoints) {
+ marknodes=new void(picture pic=currentpicture, frame f, path g)
+ {
+ for(int i=0; i <= length(g); ++i) {
+ add(pic,f,point(g,i));
+ magnetize(point(g,i));
+ }
+ };
+ dot=dot();
+
+ markuniform=new markroutine(bool centered=false, int n, bool rotated=false)
+ {
+ return new void(picture pic=currentpicture, frame f, path g) {
+ if(n <= 0) return;
+ void add(real x) {
+ real t=reltime(g,x);
+ add(pic,rotated ? rotate(degrees(dir(g,t)))*f : f,point(g,t));
+ magnetize(point(g,t));
+ }
+ if(centered) {
+ real width=1/n;
+ for(int i=0; i < n; ++i) add((i+0.5)*width);
+ } else {
+ if(n == 1) add(0.5);
+ else {
+ real width=1/(n-1);
+ for(int i=0; i < n; ++i)
+ add(i*width);
+ }
+ }
+ };
+ };
+ }
+
+ circlenodesnumber=tremble_circlenodesnumber;
+ circlenodesnumber=tremble_circlenodesnumber1;
+
+ ellipsenodesnumber=tremble_ellipsenodesnumber;
+ ellipsenodesnumber=tremble_ellipsenodesnumber1;
+
+ parabolanodesnumber=tremble_parabolanodesnumber;
+ hyperbolanodesnumber=tremble_hyperbolanodesnumber;
+ }
+}
+
+// void stopTrembling()
+// {
+// if(tremblingMode) {
+// tremblingMode=false;
+// plain.draw=orig_draw;
+// // draw=orig_draw1;
+// circlenodesnumber=orig_circlenodesnumber;
+// circlenodesnumber=orig_circlenodesnumber1;
+// ellipsenodesnumber=orig_ellipsenodesnumber;
+// ellipsenodesnumber=orig_ellipsenodesnumber1;
+// parabolanodesnumber=orig_parabolanodesnumber;
+// hyperbolanodesnumber=orig_hyperbolanodesnumber;
+// }
+// }