diff options
author | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
commit | e0c6872cf40896c7be36b11dcc744620f10adf1d (patch) | |
tree | 60335e10d2f4354b0674ec22d7b53f0f8abee672 /graphics/asymptote/runtime.in |
Initial commit
Diffstat (limited to 'graphics/asymptote/runtime.in')
-rw-r--r-- | graphics/asymptote/runtime.in | 1212 |
1 files changed, 1212 insertions, 0 deletions
diff --git a/graphics/asymptote/runtime.in b/graphics/asymptote/runtime.in new file mode 100644 index 0000000000..7db30dd589 --- /dev/null +++ b/graphics/asymptote/runtime.in @@ -0,0 +1,1212 @@ +/***** + * runtime.in + * Tom Prince 2005/4/15 + * + * Generate the runtime functions used by the vm::stack machine. + * + *****/ + +/* Autogenerated routines are specified like this (separated by a formfeed): + type asyname:cname(cparams) + { + C code + } + +*/ + +// Use Void f() instead of void f() to force an explicit Stack argument. + + +pen => primPen() +pair => primPair() +triple => primTriple() +path => primPath() +path3 => primPath3() +guide* => primGuide() +cycleToken => primCycleToken() +tensionSpecifier => primTensionSpecifier() +curlSpecifier => primCurlSpecifier() +file* => primFile() +picture* => primPicture() +transform => primTransform() +callable* => voidFunction() +callableBp* => breakpointFunction() +callableReal* => realRealFunction() +callableTransform* => transformFunction() +runnable* => primCode() +boolarray* => booleanArray() +Intarray* => IntArray() +Intarray2* => IntArray2() +realarray* => realArray() +realarray2* => realArray2() +pairarray* => pairArray() +pairarray2* => pairArray2() +triplearray* => tripleArray() +triplearray2* => tripleArray2() +patharray* => pathArray() +patharray2* => pathArray2() +guidearray* => guideArray() +transformarray* => transformArray() +penarray* => penArray() +penarray2* => penArray2() +stringarray* => stringArray() +stringarray2* => stringArray2() + +#include <cfloat> +#include <iostream> +#include <fstream> +#include <time.h> +#include <sys/times.h> +#include <locale.h> + +#include "angle.h" +#include "pair.h" +#include "triple.h" +#include "transform.h" +#include "path.h" +#include "path3.h" +#include "pen.h" +#include "drawpath.h" +#include "guide.h" +#include "picture.h" +#include "fileio.h" +#include "genv.h" +#include "builtin.h" +#include "texfile.h" +#include "pipestream.h" +#include "parser.h" +#include "stack.h" +#include "util.h" +#include "locate.h" +#include "mathop.h" +#include "callable.h" +#include "stm.h" +#include "lexical.h" +#include "process.h" +#include "arrayop.h" + +#if defined(USEGC) && defined(GC_DEBUG) && defined(GC_BACKTRACE) + extern "C" { + void *GC_generate_random_valid_address(void); + void GC_debug_print_heap_obj_proc(void *); + } +#endif + +using namespace vm; +using namespace camp; +using namespace settings; + +#undef OUT +#undef IN + +namespace run { +using camp::pair; +using vm::array; +using vm::frame; +using vm::stack; +using camp::transform; +using absyntax::runnable; + +typedef array boolarray; +typedef array Intarray; +typedef array Intarray2; +typedef array realarray; +typedef array realarray2; +typedef array pairarray; +typedef array pairarray2; +typedef array triplearray; +typedef array triplearray2; +typedef array patharray; +typedef array patharray2; +typedef array guidearray; +typedef array transformarray; +typedef array penarray; +typedef array penarray2; +typedef array stringarray; +typedef array stringarray2; + +typedef callable callableBp; +typedef callable callableReal; +typedef callable callableTransform; +} + +using vm::array; +using types::function; + +#define PRIMITIVE(name,Name,asyName) using types::prim##Name; +#include <primitives.h> +#undef PRIMITIVE + +using types::booleanArray; +using types::IntArray; +using types::IntArray2; +using types::realArray; +using types::realArray2; +using types::pairArray; +using types::pairArray2; +using types::tripleArray; +using types::tripleArray2; +using types::pathArray; +using types::pathArray2; +using types::guideArray; +using types::transformArray; +using types::penArray; +using types::penArray2; +using types::stringArray; +using types::stringArray2; +using types::formal; + +function *realRealFunction() +{ + return new function(primReal(),primReal()); +} + +function *realTripleFunction() +{ + return new function(primReal(),primTriple()); +} + +const size_t camp::ColorComponents[]={0,0,1,3,4,0}; + +namespace vm { +#if COMPACT +const Int DefaultValue=0x7fffffffffffffffLL; +const Int Undefined=0x7ffffffffffffffeLL; + +const Int BoolTruthValue=0xABABABABABABABACLL; +const Int BoolFalseValue=0xABABABABABABABABLL; + +const item Default=DefaultValue; +#else +const item Default=item(default_t()); +#endif +} + +namespace run { + +const char *arrayempty="cannot take min or max of empty array"; +const char *noruntime="no runtime environment for embedded eval"; + +void writestring(stack *s) +{ + callable *suffix=pop<callable *>(s,NULL); + string S=pop<string>(s); + vm::item it=pop(s); + bool defaultfile=isdefault(it); + camp::ofile *f=defaultfile ? &camp::Stdout : vm::get<camp::ofile*>(it); + if(!f->isOpen() || !f->enabled()) return; + if(S != "") f->write(S); + if(f->text()) { + if(suffix) { + s->push(f); + suffix->call(s); + } else if(defaultfile) f->writeline(); + } +} + +string toplocation() { + ostringstream buf; + position& topPos=processData().topPos; + buf << topPos.Line() << "." << topPos.Column(); + return buf.str(); +} + +string emptystring; +pair zero; + +} + +static string defaulttransparency=string("Compatible"); + +void unused(void *) +{ +} + +// Autogenerated routines: + + + +// Initializers + +Int :IntZero() +{ + return 0; +} + +real :realZero() +{ + return 0.0; +} + +bool :boolFalse() +{ + return false; +} + +bool isnan(real x) +{ + return std::isnan(x); +} + +array* :pushNullArray() +{ + return 0; +} + +frame* :pushNullRecord() +{ + return 0; +} + +item :pushNullFunction() +{ + return nullfunc::instance(); +} + + +// Default operations + +// Put the default value token on the stack (in place of an argument when +// making a function call). +item :pushDefault() +{ + return Default; +} + + +// Test if the value on the stack is the default value token. +bool :isDefault(item i) +{ + return isdefault(i); +} + + +// Casts + +guide* :pairToGuide(pair z) +{ + return new pairguide(z); +} + +guide* :pathToGuide(path p) +{ + return new pathguide(p); +} + +path :guideToPath(guide *g) +{ + return g->solve(); +} + + +// Pen operations + +pen :newPen() +{ + return pen(); +} + +bool ==(pen a, pen b) +{ + return a == b; +} + +bool !=(pen a, pen b) +{ + return a != b; +} + +pen +(pen a, pen b) +{ + return a+b; +} + +pen Operator *(real a, pen b) +{ + return a*b; +} + +pen Operator *(pen a, real b) +{ + return b*a; +} + +pair max(pen p) +{ + return p.bounds().Max(); +} + +pair min(pen p) +{ + return p.bounds().Min(); +} + +// Reset the meaning of pen default attributes. +void resetdefaultpen() +{ + processData().defaultpen=camp::pen::initialpen(); +} + +void defaultpen(pen p) +{ + processData().defaultpen=pen(resolvepen,p); +} + +pen defaultpen() +{ + return processData().defaultpen; +} + +bool invisible(pen p) +{ + return p.invisible(); +} + +pen invisible() +{ + return pen(invisiblepen); +} + +pen gray(pen p) +{ + p.togrey(); + return p; +} + +pen rgb(pen p) +{ + p.torgb(); + return p; +} + +pen cmyk(pen p) +{ + p.tocmyk(); + return p; +} + +pen interp(pen a, pen b, real t) +{ + return interpolate(a,b,t); +} + +pen rgb(real r, real g, real b) +{ + return pen(r,g,b); +} + +pen cmyk(real c, real m, real y, real k) +{ + return pen(c,m,y,k); +} + +pen gray(real gray) +{ + return pen(gray); +} + +realarray *colors(pen p) +{ + size_t n=ColorComponents[p.colorspace()]; + array *a=new array(n); + + switch(n) { + case 0: + break; + case 1: + (*a)[0]=p.gray(); + break; + case 3: + (*a)[0]=p.red(); + (*a)[1]=p.green(); + (*a)[2]=p.blue(); + break; + case 4: + (*a)[0]=p.cyan(); + (*a)[1]=p.magenta(); + (*a)[2]=p.yellow(); + (*a)[3]=p.black(); + break; + default: + break; + } + return a; +} + +string hex(pen p) +{ + return p.hex(); +} + +Int byte(real x) +{ + return camp::byte(x); +} + +string colorspace(pen p) +{ + string s=ColorDeviceSuffix[p.colorspace()]; + std::transform(s.begin(),s.end(),s.begin(),tolower); + return s; +} + +pen pattern(string *s) +{ + return pen(setpattern,*s); +} + +string pattern(pen p) +{ + return p.fillpattern(); +} + +pen fillrule(Int n) +{ + return pen(n >= 0 && n < nFill ? (FillRule) n : DEFFILL); +} + +Int fillrule(pen p) +{ + return p.Fillrule(); +} + +pen opacity(real opacity=1.0, string blend=defaulttransparency) +{ + for(Int i=0; i < nBlendMode; ++i) + if(blend == BlendMode[i]) return pen(Transparency(blend,opacity)); + + ostringstream buf; + buf << "Unknown blend mode: " << "'" << blend << "'"; + error(buf); +} + +real opacity(pen p) +{ + return p.opacity(); +} + +string blend(pen p) +{ + return p.blend(); +} + +pen linetype(realarray *pattern, real offset=0, bool scale=true, + bool adjust=true) +{ + size_t size=checkArray(pattern); + array *a=new array(size); + for(size_t i=0; i < size; ++i) + (*a)[i]=::max(vm::read<double>(pattern,i),0.0); + + return pen(LineType(*a,offset,scale,adjust)); +} + +realarray *linetype(pen p=CURRENTPEN) +{ + array a=p.linetype()->pattern; + return copyArray(&a); +} + +real offset(pen p) +{ + return p.linetype()->offset; +} + +bool scale(pen p) +{ + return p.linetype()->scale; +} + +bool adjust(pen p) +{ + return p.linetype()->adjust; +} + +pen adjust(pen p, real arclength, bool cyclic) +{ + return adjustdash(p,arclength,cyclic); +} + +pen linecap(Int n) +{ + return pen(setlinecap,n >= 0 && n < nCap ? n : DEFCAP); +} + +Int linecap(pen p=CURRENTPEN) +{ + return p.cap(); +} + +pen linejoin(Int n) +{ + return pen(setlinejoin,n >= 0 && n < nJoin ? n : DEFJOIN); +} + +Int linejoin(pen p=CURRENTPEN) +{ + return p.join(); +} + +pen miterlimit(real x) +{ + return pen(setmiterlimit,x >= 1.0 ? x : DEFJOIN); +} + +real miterlimit(pen p=CURRENTPEN) +{ + return p.miter(); +} + +pen linewidth(real x) +{ + return pen(setlinewidth,x >= 0.0 ? x : DEFWIDTH); +} + +real linewidth(pen p=CURRENTPEN) +{ + return p.width(); +} + +pen fontcommand(string *s) +{ + return pen(setfont,*s); +} + +string font(pen p=CURRENTPEN) +{ + return p.Font(); +} + +pen fontsize(real size, real lineskip) +{ + return pen(setfontsize,size > 0.0 ? size : 0.0, + lineskip > 0.0 ? lineskip : 0.0); +} + +real fontsize(pen p=CURRENTPEN) +{ + return p.size(); +} + +real lineskip(pen p=CURRENTPEN) +{ + return p.Lineskip(); +} + +pen overwrite(Int n) +{ + return pen(setoverwrite,n >= 0 && n < nOverwrite ? (overwrite_t) n : + DEFWRITE); +} + +Int overwrite(pen p=CURRENTPEN) +{ + return p.Overwrite(); +} + +pen basealign(Int n) +{ + return pen(n >= 0 && n < nBaseLine ? (BaseLine) n : DEFBASE); +} + +Int basealign(pen p=CURRENTPEN) +{ + return p.Baseline(); +} + +transform transform(pen p) +{ + return p.getTransform(); +} + +path nib(pen p) +{ + return p.Path(); +} + +pen makepen(path p) +{ + return pen(p); +} + +pen colorless(pen p) +{ + p.colorless(); + return p; +} + +// Interactive mode + +bool interactive() +{ + return interact::interactive; +} + +bool uptodate() +{ + return interact::uptodate; +} + + +// System commands + +Int system(stringarray *s) +{ + if(safe) error("system() call disabled; override with option -nosafe"); + size_t size=checkArray(s); + if(size == 0) return 0; + mem::vector<string> cmd; + for(size_t i=0; i < size; ++i) + cmd.push_back(read<string>(s,i)); + return System(cmd); +} + +bool view() +{ + return view(); +} + +string asydir() +{ + return systemDir; +} + +string locale(string s=emptystring) +{ + char *L=setlocale(LC_ALL,s.empty() ? NULL : s.c_str()); + return L != NULL ? string(L) : ""; +} + +void abort(string s=emptystring) +{ + if(s.empty()) throw handled_error(); + error(s.c_str()); +} + +void exit() +{ + throw quit(); +} + +void assert(bool b, string s=emptystring) +{ + flush(cout); + if(!b) { + ostringstream buf; + buf << "assert FAILED"; + if(s != "") buf << ": " << s; + error(buf); + } +} + +void sleep(Int seconds) +{ + if(seconds <= 0) return; + sleep(seconds); +} + +void usleep(Int microseconds) +{ + if(microseconds <= 0) return; + usleep((unsigned long) microseconds); +} + +void _eval(string *s, bool embedded, bool interactiveWrite=false) +{ + if(embedded) { + trans::coenv *e=Stack->getEnvironment(); + vm::interactiveStack *is=dynamic_cast<vm::interactiveStack *>(Stack); + if(e && is) + runStringEmbedded(*s, *e, *is); + else + error(noruntime); + } else + runString(*s,interactiveWrite); +} + +void _eval(runnable *s, bool embedded) +{ + absyntax::block *ast=new absyntax::block(s->getPos(), false); + ast->add(s); + + if(embedded) { + trans::coenv *e=Stack->getEnvironment(); + vm::interactiveStack *is=dynamic_cast<vm::interactiveStack *>(Stack); + if(e && is) + runCodeEmbedded(ast, *e, *is); + else + error(noruntime); + } else + runCode(ast); +} + +string xasyKEY() { + processDataStruct *P=&processData(); + xkey_t *xkey=&P->xkey; + xkey_t::iterator p=xkey->find(P->topPos.LineColumn()); + return p != xkey->end() ? p->second+" 1" : toplocation()+" 0"; +} + +void xasyKEY(string *s) { + processData().KEY=*s; +} + +string toplocation() { + return toplocation(); +} + +string location() { + ostringstream buf; + buf << getPos(); + return buf.str(); +} + +// Wrapper for the stack::load() method. +void :loadModule(string *index) +{ + Stack->load(*index); +} + +string cd(string s=emptystring) +{ + if(!s.empty() && !globalwrite()) { + string outname=getSetting<string>("outname"); + string dir=stripDir(outname); + if(dir.empty()) Setting("outname")=getPath()+dirsep+outname; + } + return setPath(s.c_str()); +} + +void list(string *s, bool imports=false) +{ + if(*s == "-") return; + trans::genv ge; + symbol name=symbol::trans(*s); + record *r=ge.getModule(name,*s); + r->e.list(imports ? 0 : r); +} + + +// Guide operations + +guide* :nullGuide() +{ + return new pathguide(path()); +} + +guide* :dotsGuide(guidearray *a) +{ + guidevector v; + size_t size=checkArray(a); + for (size_t i=0; i < size; ++i) + v.push_back(a->read<guide*>(i)); + + return new multiguide(v); +} + +guide* :dashesGuide(guidearray *a) +{ + static camp::curlSpec curly; + static camp::specguide curlout(&curly, camp::OUT); + static camp::specguide curlin(&curly, camp::IN); + + size_t n=checkArray(a); + + // a--b is equivalent to a{curl 1}..{curl 1}b + guidevector v; + if (n > 0) + v.push_back(a->read<guide*>(0)); + + if (n==1) { + v.push_back(&curlout); + v.push_back(&curlin); + } + else + for (size_t i=1; i<n; ++i) { + v.push_back(&curlout); + v.push_back(&curlin); + v.push_back(a->read<guide*>(i)); + } + + return new multiguide(v); +} + +cycleToken :newCycleToken() +{ + return cycleToken(); +} + +guide *operator cast(cycleToken tok) +{ +// Avoid unused variable warning messages. + unused(&tok); + return new cycletokguide(); +} + +guide* operator spec(pair z, Int p) +{ + camp::side d=(camp::side) p; + camp::dirSpec *sp=new camp::dirSpec(z); + + return new specguide(sp,d); +} + +curlSpecifier operator curl(real gamma, Int p) +{ + camp::side s=(camp::side) p; + return curlSpecifier(gamma,s); +} + +real :curlSpecifierValuePart(curlSpecifier spec) +{ + return spec.getValue(); +} + +Int :curlSpecifierSidePart(curlSpecifier spec) +{ + return spec.getSide(); +} + +guide *operator cast(curlSpecifier spec) +{ + return new specguide(spec); +} + +tensionSpecifier operator tension(real tout, real tin, bool atleast) +{ + return tensionSpecifier(tout, tin, atleast); +} + +real :tensionSpecifierOutPart(tensionSpecifier t) +{ + return t.getOut(); +} + +real :tensionSpecifierInPart(tensionSpecifier t) +{ + return t.getIn(); +} + +bool :tensionSpecifierAtleastPart(tensionSpecifier t) +{ + return t.getAtleast(); +} + +guide *operator cast(tensionSpecifier t) +{ + return new tensionguide(t); +} + +guide* operator controls(pair zout, pair zin) +{ + return new controlguide(zout, zin); +} + +Int size(guide *g) +{ + flatguide f; + g->flatten(f,false); + return f.size(); +} + +Int length(guide *g) +{ + flatguide f; + g->flatten(f,false); + return g->cyclic() ? f.size() : f.size()-1; +} + +bool cyclic(guide *g) +{ + flatguide f; + g->flatten(f,false); + return g->cyclic(); +} + +pair point(guide *g, Int t) +{ + flatguide f; + g->flatten(f,false); + return f.Nodes(adjustedIndex(t,f.size(),g->cyclic())).z; +} + +pairarray *dirSpecifier(guide *g, Int t) +{ + flatguide f; + g->flatten(f,false); + Int n=f.size(); + if(!g->cyclic() && (t < 0 || t >= n-1)) return new array(0); + array *c=new array(2); + (*c)[0]=f.Nodes(t).out->dir(); + (*c)[1]=f.Nodes(t+1).in->dir(); + return c; +} + +pairarray *controlSpecifier(guide *g, Int t) +{ + flatguide f; + g->flatten(f,false); + Int n=f.size(); + if(!g->cyclic() && (t < 0 || t >= n-1)) return new array(0); + knot curr=f.Nodes(t); + knot next=f.Nodes(t+1); + if(curr.out->controlled()) { + assert(next.in->controlled()); + array *c=new array(2); + (*c)[0]=curr.out->control(); + (*c)[1]=next.in->control(); + return c; + } else return new array(0); +} + +tensionSpecifier tensionSpecifier(guide *g, Int t) +{ + flatguide f; + g->flatten(f,false); + Int n=f.size(); + if(!g->cyclic() && (t < 0 || t >= n-1)) return tensionSpecifier(1.0,1.0,false); + knot curr=f.Nodes(t); + return tensionSpecifier(curr.tout.val,f.Nodes(t+1).tin.val,curr.tout.atleast); +} + +realarray *curlSpecifier(guide *g, Int t) +{ + flatguide f; + g->flatten(f,false); + Int n=f.size(); + if(!g->cyclic() && (t < 0 || t >= n-1)) return new array(0); + array *c=new array(2); + real c0=f.Nodes(t).out->curl(); + real c1=f.Nodes(t+1).in->curl(); + (*c)[0]=c0 >= 0.0 ? c0 : 1.0; + (*c)[1]=c1 >= 0.0 ? c1 : 1.0; + return c; +} + +guide *reverse(guide *g) +{ + flatguide f; + g->flatten(f,false); + if(f.precyclic()) + return new pathguide(g->solve().reverse()); + + size_t n=f.size(); + bool cyclic=g->cyclic(); + guidevector v; + size_t start=cyclic ? n : n-1; + knot curr=f.Nodes(start); + knot next=curr; + for(size_t i=start; i > 0; --i) { + next=f.Nodes(i-1); + v.push_back(new pairguide(curr.z)); + if(next.out->controlled()) { + assert(curr.in->controlled()); + v.push_back(new controlguide(curr.in->control(),next.out->control())); + } else { + pair d=curr.in->dir(); + if(d != zero) + v.push_back(new specguide(new dirSpec(-d),camp::OUT)); + else { + real C=curr.in->curl(); + if(C >= 0.0) + v.push_back(new specguide(new curlSpec(C),camp::OUT)); + } + real tout=curr.tin.val; + real tin=next.tout.val; + bool atleast=next.tout.atleast; + if(tout != 1.0 || tin != 1.0 || next.tout.atleast) + v.push_back(new tensionguide(tensionSpecifier(tout,tin,atleast))); + d=next.out->dir(); + if(d != zero) + v.push_back(new specguide(new dirSpec(-d),camp::IN)); + else { + real C=next.out->curl(); + if(C >= 0.0) + v.push_back(new specguide(new curlSpec(C),camp::IN)); + } + } + curr=next; + } + if(cyclic) + v.push_back(new cycletokguide()); + else + v.push_back(new pairguide(next.z)); + + return new multiguide(v); +} + + +realarray *_cputime() +{ + static const real ticktime=1.0/sysconf(_SC_CLK_TCK); + struct tms buf; + + ::times(&buf); + array *t=new array(4); + (*t)[0] = ((real) buf.tms_utime)*ticktime; + (*t)[1] = ((real) buf.tms_stime)*ticktime; + (*t)[2] = ((real) buf.tms_cutime)*ticktime; + (*t)[3] = ((real) buf.tms_cstime)*ticktime; + return t; +} + + +// Transforms + +bool ==(transform a, transform b) +{ + return a == b; +} + +bool !=(transform a, transform b) +{ + return a != b; +} + +transform +(transform a, transform b) +{ + return a+b; +} + +transform Operator *(transform a, transform b) +{ + return a*b; +} + +pair Operator *(transform t, pair z) +{ + return t*z; +} + +path Operator *(transform t, path g) +{ + return transformed(t,g); +} + +pen Operator *(transform t, pen p) +{ + return transformed(t,p); +} + +picture * Operator *(transform t, picture *f) +{ + return transformed(t,f); +} + +picture * Operator *(realarray2 *t, picture *f) +{ + return transformed(*t,f); +} + +transform ^(transform t, Int n) +{ + transform T; + if(n < 0) { + n=-n; + t=inverse(t); + } + for(Int i=0; i < n; i++) T=T*t; + return T; +} + +real :transformXPart(transform t) +{ + return t.getx(); +} + +real :transformYPart(transform t) +{ + return t.gety(); +} + +real :transformXXPart(transform t) +{ + return t.getxx(); +} + +real :transformXYPart(transform t) +{ + return t.getxy(); +} + +real :transformYXPart(transform t) +{ + return t.getyx(); +} + +real :transformYYPart(transform t) +{ + return t.getyy(); +} + +transform :real6ToTransform(real x, real y, real xx, real xy, + real yx, real yy) +{ + return transform(x,y,xx,xy,yx,yy); +} + +transform shift(transform t) +{ + return transform(t.getx(),t.gety(),0,0,0,0); +} + +transform shiftless(transform t) +{ + return transform(0,0,t.getxx(),t.getxy(),t.getyx(),t.getyy()); +} + +transform identity:transformIdentity() +{ + return identity; +} + +transform inverse(transform t) +{ + return inverse(t); +} + +transform shift(pair z) +{ + return shift(z); +} + +transform shift(real x, real y) +{ + return shift(pair(x,y)); +} + +transform xscale(real x) +{ + return xscale(x); +} + +transform yscale(real y) +{ + return yscale(y); +} + +transform scale(real x) +{ + return scale(x); +} + +transform scale(real x, real y) +{ + return scale(x,y); +} + +transform slant(real s) +{ + return slant(s); +} + +transform rotate(real angle, pair z=0) +{ + return rotatearound(z,radians(angle)); +} + +transform reflect(pair a, pair b) +{ + return reflectabout(a,b); +} + |