knotcode.sa
Generated by gen_html_sa_files from ICSI. Contact gomes@icsi.berkeley.edu for details
-- Copyright (C) 1983 1996 Kouji KODAMA
-- 2002-11-17
-- VERTEXK: vector operations
-- 1999-4-1
-- KNOT: Graph(Theta curve) support
--1996-9-28
--Linux version.
--1994-10-20 12:32:12 KDM
--smoothDraw bug fix
--1994-10-15 19:56:26 KDM
--DrawKnot: smooth version
--1993-10-31 16:44:30 KDM
--tpic bug fix
--1993-06-28 04:27:38 KDM
--LaTeX Tpic special: DrawKnotPic.
--DrawKnotPic: LaTeX Tpic-special.
--1983 Basic
--K.Kodama
class K
class K is
shared Knot:KNOT;
end;
class KNOT_TEST
class KNOT_TEST is
---------- test data ----------
trivial: KNOT is
knot:KNOT;
knot:=#; knot.NoCompo0;
knot.trivial0;
knot.CodeIn(100.int,-100,1);
knot.CodeIn(100.int,-200,2);
knot.CodeIn(200.int,-200,3);
knot.CodeIn(200.int,-100,4);
knot.CodeIn(100.int,-100,5);
return knot;
end;
trifoil: KNOT is
knot:KNOT;
knot:=#; knot.NoCompo0;
knot.trivial0;
knot.CodeIn(119.int, -105,1);
knot.CodeIn(243.int, -102,2);
knot.CodeIn(219.int, -146,VERTEXC::cross_over,3);
knot.CodeIn(181.int, -214,VERTEXC::cross_under,4);
knot.CodeIn(137.int, -293,5);
knot.CodeIn(78.int ,-151,6);
knot.CodeIn(144.int, -149,VERTEXC::cross_over,7);
knot.CodeIn(219.int, -146,VERTEXC::cross_under,8);
knot.CodeIn(271.int, -144,9);
knot.CodeIn(216.int, -274,10);
knot.CodeIn(181.int, -214,VERTEXC::cross_over,11);
knot.CodeIn(144.int, -149,VERTEXC::cross_under,12);
knot.CodeIn(119.int, -105,13);
-- CHECK_KNOT_ALG::checkKnotD(knot);
return knot;
end;
end;
class VERTEXK
class VERTEXK is
attr x,y,work:INT;
attr sep:VERTEXC;
create(nx,ny,nw:INT,c:VERTEXC):SAME is
res::=new; res.x:=nx; res.y:=ny; res.work:=nw; res.sep:=c; return res;
end;
create(nx,ny:INT, nc:VERTEXC):SAME is return create(nx,ny,0.int,nc); end;
create(nx,ny:INT):SAME is return create(nx,ny,0.int,VERTEXC::normal); end;
create(nx,ny:FLT):SAME is return create(nx.round.int,ny.round.int,0.int,VERTEXC::normal); end;
create(nc:VERTEXC):SAME is return create(0.int,0.int,0.int,nc); end;
create:SAME is return create(0.int,0.int,0.int,VERTEXC::normal); end;
clone:SAME is return create(x,y,work,sep.clone); end;
-- As points/vectors
sameCoord(other:SAME):BOOL is return (x=other.x)and(y=other.y); end;
plus(other:SAME):SAME is return create(x+other.x, y+other.y); end;
plus(other:SAME) is x:=x+other.x; y:=y+other.y; end;
plus(dx,dy:INT):SAME is return create(x+dx, y+dy); end;
plus(dx,dy:INT) is x:=x+dx; y:=y+dy; end;
minus(other:SAME):SAME is return create(x-other.x, y-other.y); end;
minus(dx,dy:INT):SAME is return create(x-dx, y-dy); end;
times(t:INT):SAME is return create(x*t, y*t); end;
times(t:CARD):SAME is return create(x*t.int, y*t.int); end;
times(t:FLT):SAME is return create(x.flt*t, y.flt*t); end;
times(t:FLT) is x:=(x.flt*t).round.int; y:=(y.flt*t).round.int; end;
div(t:FLT):SAME is return create(x.flt/t, y.flt/t); end;
div(t:INT):SAME is return create(x/t, y/t); end;
inner(other:SAME):INT is
-- inner product self . other
return x*other.x+y*other.y;
end;
inner(o1,o2:SAME):INT is
-- inner product (o1-self).(o2-self)
return (o1-self).inner(o2-self);
end;
times(other:SAME):INT is
-- inner product
return inner(other);
end;
outer(other:SAME):INT is
--outer product self x other
return x*other.y-y*other.x;
end;
outer(o1, o2:SAME):INT is
--outer product (o1-self)x(o2-self)
return (o1-self).outer(o2-self);
end;
max(mx,my:INT):SAME is return create(x.max(mx),y.max(my)); end;
max(m:SAME):SAME is return create(x.max(m.x),y.max(m.y)); end;
min(mx,my:INT):SAME is return create(x.min(mx),y.min(my)); end;
min(m:SAME):SAME is return create(x.min(m.x),y.min(m.y)); end;
abs1:INT is return x.abs+y.abs; end;
distance1(other:SAME):INT is return (x-other.x).abs+(y-other.y).abs; end;
abs2:INT is return x*x+y*y; end;
abs:FLT is return (x*x+y*y).flt.sqrt; end;
rotation(p0,p1,p2:SAME):INT is
-- orientation of points p1,p2,p3 around self.
v0::=self-p0; v1::=self-p1; v2::=self-p2;
return VEC_ALG::rotation(v0.x, v0.y, v1.x, v1.y, v2.x, v2.y);
end;
rotation(v1,v2:SAME):INT is
-- orientation of 3 vectors self,v2,v3 around origin O.
return VEC_ALG::rotation(x, y, v1.x, v1.y, v2.x, v2.y);
end;
transform(p,q,r,s:INT):SAME is
-- linear transformation
return create(p*x+q*y, r*x+s*y);
end;
transform(p,q,r,s:FLT):SAME is
-- linear transformation
fx::=x.flt; fy::=y.flt; return create(p*fx+q*fy, r*fx+s*fy);
end;
transform(p,q,r,s:INT) is
-- linear transformation
wx::=p*x+q*y; wy::=r*x+s*y; x:=wx; y:=wy;
end;
transform(p,q,r,s:FLT) is
-- linear transformation
fx::=x.flt; fy::=y.flt; x:=(p*fx+q*fy).round.int; y:=(r*fx+s*fy).round.int;
end;
transform(p,s:INT):SAME is
-- linear transformation with diagonal element (p,s).
return create(x*p,y*s);
end;
transform(other:SAME):SAME is
-- linear transformation with diagonal element from other.
return create(x*other.x, y*other.y);
end;
rotate(t:FLT):SAME is
s::=t.sin; c::=t.cos; return transform(c,-s, s, c);
end;
trace:INT is
return x+y;
end;
end; -- VERTEXK
class KNOT
class KNOT is
include KCODE{VERTEXK} create->kcode_create, clone->kcode_clone;
attr updateCross:BOOL;
-- true if crossing data is changed and needed CrossSet.
create:SAME is
res::=kcode_create; res.updateCross:=false; return res;
end;
clone:SAME is
res::=kcode_clone; res.updateCross:=updateCross;
return res;
end;
SetTCode(out tcode:TCODE):BOOL is
return SET_TCODE_ALG::SetTCode(self,out tcode);
end;
NoCompo0 is
-- set Knot be empty
updateCross:=false; k:=#(1); k[0]:=#(VERTEXC::code_e);
end;
NoCompo0:SAME is
-- set Knot be empty
res:SAME:=#; res.k:=#(1); res.k[0]:=#(VERTEXC::code_e); return res;
end;
is_nocompo:BOOL is
return k.size<3;
end;
trivial0 is
updateCross:=false; k:=#(3);
k[0]:=#(VERTEXC::knot_s); k[1]:=#(VERTEXC::knot_e); k[2]:=#(VERTEXC::code_e);
end;
trivial0:SAME is
res:SAME:=#; res.k:=#(3);
res.k[0]:=#(VERTEXC::knot_s); res.k[1]:=#(VERTEXC::knot_e); res.k[2]:=#(VERTEXC::code_e);
return res;
end;
SLength is
i:INT:=0.int;
if void(self) then raise "KNOT:SLength: knot is void.\n"; end;
loop
if (i+1)>k.size.int then
raise "KNOT::SLength: Knot has no end_code.\n";
elsif [i].sep=VERTEXC::code_e then
if (i+1)=k.size.int then return;
else k:=k.resize(i.card+1);
#OUT+"KNOT::SLength: k.size is too high.\n";
return;
end;
end;
i:=i+1;
end;
end;
---------------------check/search pairings-----------------
Match(i1,i2:INT):BOOL is
--TRUE if coord (x1,y1)=(x2,y2)
return has_ind(i1) and has_ind(i2) and [i1].sameCoord([i2]);
end;
Match2(i1,i2:INT):BOOL is
--Match with band adjustment
if ~(has_ind(i1) and has_ind(i2)) then return false; end;
if VERTEXC::band_s=[i1].sep then i1:=i1+1;
elsif VERTEXC::band_e=[i1].sep then i1:=i1-1;
end;
if VERTEXC::band_s=[i2].sep then i2:=i2+1;
elsif VERTEXC::band_e=[i2].sep then i2:=i2-1;
end;
return Match(i1,i2);
end;
MatchPt(i,from:INT):INT is
-- return j where Knot[i].x=Knot[j].x, Knot[i].y=Knot[j].y, i#j.
-- Return -1 if not exist.
loop j::=from.upto!(k.size.int-2);
if ([i].sameCoord([j]))and(i/=j)and
(~ VERTEXC::separator.in([j].sep)) then return j; end;
end;
return -1;
end;
MatchPtS(i,from:INT):INT is
-- return MatchPt if exist . return (i) if not exist
j::=MatchPt(i,from); if j.is_neg then j:=i; end; return j;
end;
cmpOf(i:INT):INT is
-- return -1 if no companion
if ~has_ind(i) then return -1; end;
j:INT;
if VERTEXC::ks.in([i].sep) then j:=endOfString(i,1.int);
if (j.is_non_neg)and Match(i+1,j-1) then return j; end;
elsif VERTEXC::ke.in([i].sep) then j:=endOfString(i,-1);
if (j.is_non_neg)and Match(i-1,j+1) then return j; end;
elsif VERTEXC::ts.in([i].sep) then j:=0.int;
loop
j:=MatchPt(i+1,j+1);
if (j.is_neg) then return -1; end;
if VERTEXC::band.in([j].sep) then return j; end;
end;
elsif VERTEXC::te.in([i].sep) then j:=0.int;
loop
j:=MatchPt(i-1,j+1);
if (j.is_neg) then return -1; end;
if (VERTEXC::band.in([j].sep)) then return j; end;
end;
elsif VERTEXC::band.in([i].sep) then j:=0.int;
loop
j:=MatchPt(i,j+1);
if (j.is_neg) then return -1; end;
if (VERTEXC::ts.in([j-1].sep)) then return j; end;
if (VERTEXC::te.in([j+1].sep)) then return j; end;
end;
elsif VERTEXC::crossing.in([i].sep) then j:=0.int;
loop
j:=MatchPt(i,j+1);
if (j.is_neg) then return -1; end;
if VERTEXC::crossing.in([j].sep) then return j; end;
end;
elsif VERTEXC::ks.in([i-1].sep) then j:=cmpOf(i-1);
if j.is_non_neg then return j-1; end;
elsif VERTEXC::ke.in([i+1].sep) then j:=cmpOf(i+1);
if j.is_non_neg then return j+1; end;
elsif VERTEXC::ts.in([i-1].sep) then j:=cmpOf(i-1);
if j.is_non_neg then return j; end;
elsif VERTEXC::te.in([i+1].sep) then j:=cmpOf(i+1);
if j.is_non_neg then return j; end;
end;
return -1;
end;
nextPt(i:INT, dir:INT):INT is
-- return next point. Normally i+dir but ks,ke,ts,te.
-- dir:=1,-1. if next pt is not exist return -1.
i0:INT:=i; i:=i+dir;
if VERTEXC::ts.in([i].sep) then i:=-1;
elsif VERTEXC::te.in([i].sep) then i:=-1;
elsif VERTEXC::ks.in([i].sep) then i:= endOfString(i,1.int)-1;
if Match(i0,i) then i:=i-1;else i:=-1; end;
elsif VERTEXC::ke.in([i].sep) then i:= endOfString(i,-1)+1;
if Match(i0,i) then i:=i+1;else i:=-1; end;
else
loop while!(VERTEXC::crossing.in([i].sep)); i:=i+dir; end;
end;
return i;
end;
--------- find & check --------
countVertex(i:INT):INT is
--count vertices on the string
c:INT:=0.int; i:=endOfString(i,-1)+1;
loop while!(~VERTEXC::separator.in([i].sep));
if VERTEXC::normal.in([i].sep) then c:=c+1; end;
i:=i+1;
end;
return c;
end;
const near:INT:=16;
const far:INT:=10000;
distV(i,j:INT):INT is
-- return distance ( Knot[i], Knot[j])
if VERTEXC::separator.in([i].sep+[j].sep) then return far; end;
return [i].distance1([j]);
end;
tooShort(i:INT):BOOL is
-- true if the string is too short to shrink
j0::=endOfString(i, -1); j1::=endOfString(i, 1.int); nv::=countVertex(j0);
if VERTEXC::ks.in([j0].sep) and Match(j0+1,j1-1) and (nv<=4.int) then return true;
elsif VERTEXC::ks.in([j0].sep)and(nv<=2.int) then return true;
elsif VERTEXC::ts.in([j0].sep)and(nv<=2.int) then return true;
else return false;
end;
end;
band_attach_sgn(bi, ti0,ti1:INT):INT is
-- bi points 'band', ti points 'ts' or 'te'
if has_ind(bi).not or has_ind(ti0).not or has_ind(ti1).not then return 0.int; end;
if VERTEXC::band.in([bi].sep).not then return 0.int; end;
if VERTEXC::separator.in([ti1].sep) then return 0.int; end;
if VERTEXC::normal.in([ti0].sep).not then return 0.int; end;
return [bi].rotation([bi+1], [bi-1], [ti1]);
end;
band_attach_sgn(bi, ti:INT):INT is
-- bi points 'band', ti points 'ts' or 'te'
ti0,ti1:INT;
if VERTEXC::ts.in([ti].sep) then ti0:=ti+1; ti1:=ti0+1;
elsif VERTEXC::te.in([ti].sep) then ti0:=ti-1; ti1:=ti0-1;
elsif VERTEXC::ts.in([ti-1].sep) then ti0:=ti; ti1:=ti0+1;
elsif VERTEXC::te.in([ti+1].sep) then ti0:=ti; ti1:=ti0-1;
else return 0;
end;
return band_attach_sgn(bi,ti0,ti1);
end;
band_attach_sgn(i:INT):INT is
if VERTEXC::band.in([i].sep) then
return band_attach_sgn(i,MatchPt(i,bandStart));
elsif VERTEXC::ts.in([i].sep) then
return band_attach_sgn(MatchPt(i+1,0.int),i+1,i+2);
elsif VERTEXC::te.in([i].sep) then
return band_attach_sgn(MatchPt(i-1,0.int),i-1,i-2);
elsif VERTEXC::ts.in([i-1].sep) then
return band_attach_sgn(MatchPt(i,0.int),i,i+1);
elsif VERTEXC::te.in([i+1].sep) then
return band_attach_sgn(MatchPt(i,0.int),i,i-1);
else return 0.int;
end;
end;
is_coherent(b:INT):BOOL is
-- check if band connectiong b is coherent.
-- if the band is not proper then return __true__ .
j0:INT;
if VERTEXC::band.in([b].sep) then
j0:=endOfString(MatchPt(b,bandStart),-1);
else j0:=endOfString(b,-1);
end;
if VERTEXC::ts.in([j0].sep) then -- check coherent and identify
return (band_attach_sgn(j0)*band_attach_sgn(endOfString(j0,1.int))).is_non_neg;
else return true;
end;
end;
---------------search near pts-----------------
const vertex, edge, other;
miniVE(gx,gy:INT, stp,enp:INT, sw:INT, out d:INT):INT is
--search nearest vertex/edge on Knot[].
--sw=0: vertex,
--sw=1:edge
--sw=2: other( dummy )
--returns (number of the vertex) and set distance (d).
dr,dw:INT; -- distance
i,j,js:INT;
i:=0.int; dr:=far; j:=stp.max(0.int); v:VERTEXK:=#(gx+gx,-(gy+gy));
enp:=enp.min(k.size.int-2);
loop
js:=j+sw;
if (js>enp) then break!; end;
if (~(VERTEXC::separator.in([j].sep+[js].sep)))
and (~((sw=0.int)and(VERTEXC::crossing.in([j].sep)))) then
dw:=v.distance1([j]+[js]);
if dw<dr then i:=j; dr:=dw; end;
end;
j:=j+1;
end;
d := dr/2;
return i;
end;
miniCrossing(gx, gy:INT, stp, enp:INT, out d:INT):INT is
dw:INT; -- distance
i::=0.int; d:=far; j::=stp.max(0.int); enp:=enp.min(k.size.int-2);
v:VERTEXK:=#(gx,-gy);
loop
if (j>enp) then break!; end;
if VERTEXC::crossing.in([j].sep) then
dw:=v.distance1([j]);
if dw<d then i:=j; d:=dw; end;
end;
j:=j+1;
end;
return i;
end;
miniPt(gx,gy:INT, out pt:INT, out d:INT, out sw:INT) is
-- find near point of (mouseX,mouseY)
ptw,dw:INT;
pt:=miniVE(gx,gy,0.int,length.int,0.int,out d); sw:=0.int;
ptw:=miniVE(gx,gy,0.int,length.int,1.int,out dw);
if dw<d then pt:=ptw;d:=dw;sw:=1.int; end;
end;
nearStumpVertex(gx,gy: INT, out pt:INT, out d:INT) is
-- find near stump Vertex of knot
p1,p2:INT;
pt:=miniVE(gx,gy,0.int,length.int,vertex.int,out d);
if pt.is_non_pos then d:=far; end;
if d<near then
if VERTEXC::ks.in([pt-1].sep) or VERTEXC::ke.in([pt+1].sep)
then -- end-point
p1:=endOfString(pt,-1)+1;
p2:=endOfString(pt,1.int)-1;
if [p1].sameCoord([p2]) then d:=far; end;
else d:=far;
end;
end;
end;
LookMouseV(stp, enp:INT, inout i0:INT, inout x:INT, inout y:INT,inout left:BOOL, inout right:BOOL) is
i,sw, d:INT;
#OUT+"LookMouseV is uncertified.\n";
-- MouseK.State(left, right, x, y);*)
i:=miniVE(x, y, stp, enp, vertex.int, out d);
sw:=vertex.int;
DRAW_ALG::UpdateVE(self,inout i0, i,inout sw,vertex.int);
end;
LookMouseVE(MDistance:INT,stp, enp:INT, inout i0 :INT, inout veSw:INT,inout x, y:INT, inout left, right:BOOL) is
-- mark if near than MDistance
-- veSw 0:vertex, 1:edge
-- state of mouse
i, ie, sw1, d, de:INT;
#OUT+"LookMouseVE is uncertified.\n";
-- MouseK.State(left, right, x, y);
i :=miniVE(x, y, stp, enp,vertex.int,out d ); sw1:=vertex.int;
ie:=miniVE(x, y, stp, enp,edge.int,out de);
if de<d then i:=ie; sw1:=edge.int; d:=de; end;
if MDistance<d then sw1:=other.int; end;
DRAW_ALG::UpdateVE(self, inout i0, i, inout veSw, sw1);
end;
nearVertexV(i1,i2:INT, out pt, out d:INT) is
-- find the nearest point from i1, except i1,i2
dw,j:INT;
if (~has_ind(i1))or(VERTEXC::separator.in([i1].sep)) then return; end;
pt:=-1; d:=far;
if (~has_ind(i2))or(VERTEXC::separator.in([i2].sep)) then i2:=i1; end;
loop j:=k.ind!.int;
if VERTEXC::normal.in([j].sep)and(j/=i1)and(j/=i2) then
dw:=[i1].distance1([j]);
if dw<d then d:=dw; pt:=j; end;
end;
end;
end;
-------------- add/delete ------------------
CodeIn(x,y:INT, i:CARD) is
CodeIn(#VERTEXK(x,y),i)
end;
CodeIn(x,y:INT, i:INT) pre i.is_neg.not is
CodeIn(x,y,i.card);
end;
CodeIn(x,y:INT, c:VERTEXC, i:CARD) is
CodeIn(#VERTEXK(x,y,c),i);
end;
CodeIn(x,y:INT, c:VERTEXC, i:INT) pre i.is_neg.not is
CodeIn(x,y,i.card);
end;
append(x,y:CARD) is
CodeIn(#VERTEXK(x.int,y.int),k.size-1);
end;
append(x,y:INT) is
CodeIn(#VERTEXK(x,y),k.size-1);
end;
DelV(i:INT) is
-- Del 1 code1. if band then del 1code at ts/te, too.
-- Knot[i] is not ks,ke,ts,te
j, j0, j1:INT;
ClCrossEdge(inout i, 1.int); ClCrossEdge(inout i, -1);
if tooShort(i) then delStr(inout i);
else
if VERTEXC::band.in([i].sep) then
j:=cmpOf(i);
if j.is_non_neg then
if countVertex(j)<=2.int then delStr(inout j);
else
if VERTEXC::ts.in([j-1].sep) then ClCrossEdge(inout j, 1.int);
else ClCrossEdge(inout j,-1);
end;
CodeDel(j.card);
end;
end;
end;
CodeDel(i.card);
end;
-- ClCrossErr(i); chkStr(i);
end;
delStr(inout i:INT) is
--del code of string ks..ke/ts..te cantaining i with codeDel.
j::=endOfString(i,1.int); i:=endOfString(i,-1);
loop while!(j>=i); CodeDel(j.card); j:=j-1; end;
end;
BandDel(i:INT) is
-- not check crossing.
k1, k2, b1, b2:INT;
if VERTEXC::band.is_subset([i].sep) then b1:=cmpOf(i);
else b1:=i;
end;
b1:=endOfString(b1,-1);
if (b1.is_neg)or (~(VERTEXC::ts.is_subset([b1].sep))) then return; end;
b2:=endOfString(b1,1.int);
k1:=cmpOf(b1); if k1.is_non_neg then [k1].sep:=VERTEXC::normal; end;
k2:=cmpOf(b2); if k2.is_non_neg then [k2].sep:=VERTEXC::normal; end;
delStr(inout b1); -- move down
end;
ClCross is
-- delete all crossings
i:INT:=0.int;
loop
if VERTEXC::code_e= [i].sep then break!;
elsif VERTEXC::crossing.in( [i].sep) then CodeDel(i.card);
else i:=i+1;
end;
end;
end;
ClCrossErr(inout vp:INT) is
i:INT:=0;
loop
if VERTEXC::code_e=[i].sep then break!;
elsif VERTEXC::crossing.in([i].sep)and(cmpOf(i).is_neg) then
if i<=vp then vp:=vp-1; end;
CodeDel(i.card);
else i:=i+1;
end;
end;
end;
ClCrossEdge(inout edge:INT, dir:INT) is
if ~has_ind(edge) then return; end;
if ~ VERTEXC::normal.in( [edge].sep) then return; end;
i::=edge+dir;
if dir=1.int then
if VERTEXC::te.in([i].sep) then dir:=-1; i:=edge+dir;
elsif VERTEXC::ke.in([i].sep) then i:=endOfString(i,-1)+1;
if Match(i,edge) then i:=i+1; else return; end;
end;
elsif dir=-1 then
if VERTEXC::ts.in([i].sep) then dir:=1; i:=edge+dir;
elsif VERTEXC::ks.in([i].sep) then i:=endOfString(i,1.int)-1;
if Match(edge,i) then i:=i-1; else return; end;
end;
end;
loop while!(VERTEXC::crossing.in([i].sep));
if i<edge then edge:=edge-1; end;
CodeDel(i.card);
if dir=-1 then i:=i-1; end;
end;
end;
attach(i,j:INT) is
-- Knot[i] <-- Knot[j]
[i].x:=[j].x; [i].y:=[j].y;
end;
StringSplice(i,j:INT) is
i:=endOfString(i,-1); j:=endOfString(j,-1);
CodeSplice(i,endOfString(i,1.int),j,endOfString(j,1.int));
if j<i then i:=j; end;
i:=endOfString(i,1.int);
CodeDel(i.card); CodeDel(i.card);
if Match(i,i-1) then CodeDel(i.card); end;
end;
PointInEdge(i:INT) pre (VERTEXC::normal.in([i].sep) or VERTEXC::normal.in([i+1].sep) or VERTEXC::crossing.in([i].sep)) is
CodeIn(([i]+[i+1])/2.int, i.card+1);
end;
make_coherent is
-- if knot has bands, try orientation reversing on knots
-- to make coherent along band connections.
if ~has_band then return; end;
bs::=bandStart;
c,c0,c1:INT; -- component number
-- set component number
cTbl:KNOT:=clone;
loop i:INT:=0.int.upto!(bs);
if VERTEXC::ks.in([i].sep) then c:=1.int.up!; end;
cTbl[i].work:=c;
end;
-- identify components along coherent band
i0,i1,j0,j1,sgn0,sgn1:INT;
loop i::=0.int.upto!(bs);
if VERTEXC::band.in([i].sep) then
i0:=i; j0:=MatchPt(i0,bs);
if VERTEXC::ts.in([j0-1].sep) then j1:=endOfString(j0,1.int)-1;
elsif VERTEXC::te.in([j0+1].sep) then j1:=endOfString(j0,-1)+1;
end;
i1:=MatchPt(j1,0.int);
-- check coherent and identify
if (i0<i1)
and(cTbl[i0].work/=cTbl[i1].work)
and (band_attach_sgn(i0,j0)*band_attach_sgn(i1,j1)).is_pos
then
c0:=cTbl[i0].work; c1:=cTbl[i1].work;
-- identify c1 with c0
loop j::=0.int.upto!(bs);
if cTbl[j].work=c1 then cTbl[j].work:=c0; end;
end;
end;
end;
end;
-- reverse and identify components along incoherent band
loop i::=0.int.upto!(bs);
if VERTEXC::band.in([i].sep) then
i0:=i; j0:=MatchPt(i0,bs);
if VERTEXC::ts.in([j0-1].sep) then j1:=endOfString(j0,1.int)-1;
elsif VERTEXC::te.in([j0+1].sep) then j1:=endOfString(j0,-1)+1;
end;
i1:=MatchPt(j1,0.int);
if (i0<i1)and(cTbl[i0].work/=cTbl[i1].work) then
-- ori. reverse _c1_ components
c0:=cTbl[i0].work; c1:=cTbl[i1].work;
loop j::=0.int.upto!(bs);
if (VERTEXC::ks.in([j].sep))and(c1=cTbl[j].work) then
revK(j);
end;
end;
-- identify c1 with c0
loop j::=0.int.upto!(bs);
if cTbl[j].work=c1 then cTbl[j].work:=c0; end;
end;
end;
end;
end;
end;
Shift(ofsX,ofsY:INT) is
loop i::=k.ind!;
if (VERTEXC::normal.in([i].sep))or
(VERTEXC::crossing.in([i].sep)) then
[i].plus(ofsX,ofsY);
end;
end;
end;
width(out minX,out maxX,out minY,out maxY:INT) is
maxX:=INT::minint; minX:=INT::maxint; maxY:=maxX; minY:=minX;
loop i::=k.ind!;
if (VERTEXC::normal.in([i].sep))or
(VERTEXC::crossing.in([i].sep)) then
maxX:=maxX.max([i].x); minX:=minX.min([i].x);
maxY:=maxY.max([i].y); minY:=minY.min([i].y);
end;
end;
end;
shiftToInside is
if is_nocompo then return; end;
maxX,minX,maxY,minY,ofsX,ofsY:INT;
width(out minX,out maxX,out minY,out maxY);
ofsX:=(KNOTXW::GraphWidth-(maxX+minX))/2;
if (minX+ofsX)<10.int then ofsX:=10.int-minX; end;
ofsY:=(-KNOTXW::GraphWidth-(maxY+minY))/2;
if (maxY+ofsY)>(-10) then ofsY:=-10-maxY; end;
if (ofsX.is_non_zero) or (ofsY.is_non_zero) then self.Shift(ofsX,ofsY); end;
end;
transform(p,q,r,s:INT) is
-- linear transformation of Knot
loop [k.ind!].transform(p,q,r,s); end;
shiftToInside;
end;
transform(mag:FLT) is
-- linear transformation of Knot
loop [k.ind!]*mag; end;
shiftToInside;
end;
mirrorX is
-- mirror x --> -x
transform(-1,0.int,0.int,1.int);
end;
mirrorY is
-- mirror y --> -y
transform(1.int,0.int,0.int,-1);
end;
mirrorZ is
-- change all crossings
exg::=VERTEXC::cross_exg;
loop
i::=k.ind!;
if VERTEXC::crossing.in(k[i].sep) then
k[i].sep:=k[i].sep/exg;
end;
end;
end;
CrossSet(stp,enp:INT) is
k:=CROSS_SET::CrossSet(self,stp,enp).k;
end;
CrossSet:BOOL is
knot::=clone;
r::=CROSS_SET::CrossSetM(inout knot,0.int,length.int,true,false);
if r then k:=knot.k; end;
return r;
end;
--------------------------- vector ----------------------
distanceL2(ki:INT, x, y:INT, rot:INT):INT is
-- distance( edge(ki--(ki+1), point(x,y) )^2
-- rot; 1:only left side. -1: right side. 0:each side.
l2, inn, outer, d2:INT;
-- if (ki<0)or(length<=ki)
-- or (~normal.in(Knot[ki].sep)) or(separator.in(Knot[ki+1].sep)) then
-- return -1;
-- end;
ki1::=ki; loop ki1:=ki1+1; until!(VERTEXC::normal.in([ki1].sep)); end;
p:VERTEXK:=#(x,y); u::=[ki1]-[ki]; v::=p-[ki];
outer:=u.outer(v); l2:=u.abs2;
if (outer.is_pos)=(rot.is_pos) then
outer:=outer.abs; inn:=u.inner(v);
if inn.is_non_pos then d2:=v.abs2; -- d(e,p)=d(ki,p)
elsif inn>=l2 then v:=v-u; d2:=v.abs2; -- d(ki+1, p)
elsif outer>=l2 then d2:=l2;
else d2:= outer.flt.square.div(l2.flt).round.int;
end;
else
d2:= l2;
end;
if (l2<d2) then d2:=l2; end;
return d2;
end;
------------------- check -----------------------------------------
is_theta:BOOL is
-- true if theta curve(1-component and 1-band).
if ~is_proper then
#OUT+"Not a theta curve.(not proper)\n"; return false;
end;
if number_compo>1.int then
#OUT+"Not a theta curve.(too many component)\n"; return false;
end;
if ~has_band then
#OUT+"Not a theta curve.(need 1 band)\n"; return false;
end;
b1:INT:=bandStart; b2:INT:=endOfString(b1,1.int);
if (b2+1)/=length.int then
#OUT+"Not a theta curve.(too many band)\n"; return false;
end;
return true;
end;
is_proper:BOOL is
-- knot code is proper for braid, ndata, invariants etc.
knot:KNOT:=clone;
if ~checkCode(true) then
#OUT+"Bad code in knot data\n"; return false;
end;
if (length<=1) then
#OUT+"Knot has no component.\n"; return false;
end;
if ~is_Closed then
#OUT+"Diagram is not closed.\n"; return false;
end;
if ~CROSS_SET::CrossSetM(inout knot,0.int,length.int,false,true) then
#OUT+"Knot is not regular.\n"; return false;
end;
return true;
end;
checkCode(message:BOOL):BOOL is
i:INT;
cod0,cod,cod1:VERTEXC;
res:BOOL:=true;
if void(self) then bad(message,0,0.int);return false; end;
if size<=1 then return true; end;
cod:=VERTEXC::graph_e; cod0:=cod; cod1:=k[0].sep;
i:=0;
loop
if (i>length.int)and(i.is_pos) then bad(message,12,i); res:=false;break!; end;
cod0:=cod;cod:=cod1;cod1:=[i+1].sep;
-- #OUT+"cod0="+cod0.int+" cod="+cod.int+" cod1="+cod1.int+"\n";
if VERTEXC::normal.in(cod) and VERTEXC::normal.in(cod0)
and Match(i,i-1) then
bad(message,10,i); res:=false;
end;
if VERTEXC::normal=cod then ;
elsif VERTEXC::band_attach=cod then ;
elsif VERTEXC::crossing.in(cod) then
if (cod-VERTEXC::cross_all).is_empty.not then
bad(message,20,i); res:=false;
elsif VERTEXC::under.in(cod)=VERTEXC::over.in(cod) then
bad(message,21,i); res:=false;
elsif VERTEXC::positive.in(cod) and VERTEXC::negative.in(cod) then
bad(message,22,i); res:=false;
end;
elsif VERTEXC::graph_s=cod then -- #OUT+"'ts";
if VERTEXC::normal /= cod1 then
bad(message,35,i); res:=false;
end;
if VERTEXC::graph_e/=cod0 then
bad(message,36,i); res:=false;
end;
if (VERTEXC::graph_e/=[i+2].sep) then
bad(message,37,i); res:=false;
end;
elsif VERTEXC::graph_e=cod then
if (VERTEXC::normal/=cod0) then
bad(message,47,i); res:=false;
end;
if (VERTEXC::graph_s=cod1) then
elsif (VERTEXC::knot_s=cod1) then
elsif (VERTEXC::band_s=cod1) then
elsif (VERTEXC::code_e=cod1) then
else bad(message,48,i); res:=false;
end;
elsif VERTEXC::knot_s=cod then
if (VERTEXC::normal/=cod1) then bad(message,51,i); res:=false; end;
if (VERTEXC::graph_e= cod0) then
elsif (VERTEXC::knot_e= cod0) then
else bad(message,52,i); res:=false;
end;
elsif VERTEXC::knot_e=cod then
if (VERTEXC::normal/=cod0) then bad(message,63,i); res:=false; end;
if (VERTEXC::knot_s=cod1) then
elsif (VERTEXC::band_s=cod1) then
elsif (VERTEXC::code_e=cod1) then
else bad(message,64,i); res:=false;
end;
elsif VERTEXC::band_s=cod then -- #OUT+"ts";
if (VERTEXC::normal/=cod1) then
bad(message,75,i); res:=false;
end;
if (VERTEXC::graph_e=cod0) then
elsif (VERTEXC::knot_e=cod0) then
elsif (VERTEXC::band_e=cod0) then
else bad(message,76,i); res:=false;
end;
elsif VERTEXC::band_e=cod then
if (VERTEXC::normal/=cod0) then
bad(message,87,i); res:=false;
end;
if (VERTEXC::band_s=cod1) then
elsif (VERTEXC::code_e=cod1) then
else bad(message,88,i); res:=false;
end;
else bad(message,100,i); res:=false;
end;
if VERTEXC::code_e=cod1 then
if VERTEXC::graph_e=cod then
elsif VERTEXC::knot_e=cod then
elsif VERTEXC::band_e=cod then
else bad(message,99,i); res:=false;
end;
break!;
end;
i:=i+1;
end;
-- if ~res then checkKnotD(Knot); end;
return res;
end;
checkCode:BOOL is
-- no check on crossings
return checkCode(true);
end;
printD is
-- print for debug
cod:VERTEXC;
if void(self) then #OUT+"Knot is void\n"; return; end;
#OUT+"Knot.k.size="+size.str+"\n";
#OUT+"index:(x,y),cod\n";
loop i::=k.ind!;
--#OUT+i.fmt("<####>")
-- +Knot[i].x.fmt("<####>")+Knot[i].y.fmt("<####>")
-- +Knot[i].sep.int.fmt("<hex#####> ");
if i>=k.size then break!; end;
#OUT+i.str+":("+[i].x.str+","+[i].y.str+"),"+[i].sep.card.hex_str+" ";
sep::=[i].sep;
if VERTEXC::ks.in(sep) then #OUT+"[";
elsif VERTEXC::ke.in(sep) then #OUT+"]";
elsif VERTEXC::ts.in(sep) then #OUT+"(";
elsif VERTEXC::te.in(sep) then #OUT+")";
elsif VERTEXC::gs.in(sep) then #OUT+"<";
elsif VERTEXC::ge.in(sep) then #OUT+">";
elsif VERTEXC::endc.in(sep) then #OUT+"/";
elsif VERTEXC::band.in(sep) then #OUT+"b";
elsif VERTEXC::crossing.in(sep) then
if VERTEXC::under.in(sep) then #OUT+"u";
elsif VERTEXC::over.in(sep) then #OUT+"o";
end;
if VERTEXC::positive.in(sep) then #OUT+"+";
elsif VERTEXC::negative.in(sep) then #OUT+"-";
end;
end;
#OUT+"\n";
end;
end;
is_Closed(i:INT):BOOL is
-- true if the string containing "i" is closed.
-- check if (1)string of ks..ke is closed
-- and (2)string of ts..te is attached to knot.
if ~has_ind(i) then return false; end;
stp::=endOfString(i,-1); if ~has_ind(stp) then return false; end;
stpm::=cmpOf(stp); if ~has_ind(stpm) then return false; end;
stpmm::=cmpOf(stpm); if ~has_ind(stpmm) then return false; end;
enp::=endOfString(i,1.int); if ~has_ind(enp) then return false; end;
enpm::=cmpOf(enp); if ~has_ind(enpm) then return false; end;
enpmm::=cmpOf(enpm); if ~has_ind(enpmm) then return false; end;
if VERTEXC::ks.in([stp].sep) then
if enp<stp+5 then return false; -- too short
end;
elsif VERTEXC::ts.in([stp].sep) then
if enp<stp+3 then return false; -- too short
end;
-- ts
if ~VERTEXC::band.in([stpm].sep) then return false;
elsif (stp+1)/=stpmm then return false;
end;
-- te
if ~VERTEXC::band.in([enpm].sep) then return false;
elsif (enp-1)/=enpmm then return false;
end;
else return false;
end;
return true;
end;
is_Closed:BOOL is
if VERTEXC::code_e /= [length].sep then return false; end;
i:INT:=0.int;
loop
if VERTEXC::code_e=[i].sep then return i=length.int; end;
if length.int<=i then return false; end;
if is_Closed(i) then i:=endOfString(i,1.int)+1;
else return false;
end;
end;
return false;
end;
concat_at_tail(i:INT) is
-- concatinate strings if (look like) connected in diagram.
j:INT; -- tail of a component
i1,j1:INT; -- head/tail of a component to concatinate
loop i:=endOfString(i,-1); j:=endOfString(i,1.int);
if [i+1].sameCoord([j-1]) then break!; end; -- when loop.
i1:=MatchPt(j-1,j); if i1<j then break!; end; -- not match
i1:=endOfString(i1,-1); j1:=endOfString(i1,1.int);
if [i1+1].sameCoord([j1-1]) then break!; -- when loop.
elsif [j-1].sameCoord([i1+1]) then StringSplice(i,i1);
elsif [j-1].sameCoord([j1-1]) then revK(i1); StringSplice(i,i1);
else break!;
end;
end;
end;
concat is
-- concatinate strings if (look like) connected in diagram.
i::=0.int; -- head of a component
loop -- unify at tail point
concat_at_tail(i);
i:=endOfString(i,1.int)+1; if [i].sep=VERTEXC::code_e then break!; end;
end;
i:=0.int;
loop -- unify at head point
revK(i); concat_at_tail(i); revK(i);
i:=endOfString(i,1.int)+1; if [i].sep=VERTEXC::code_e then break!; end;
end;
end;
edge_clean is
-- If edges v1-v2-v3 is straight then remove v2 as v1-v3.
i:CARD:=1;
loop
if i>=size then break!; end;
if VERTEXC::normal.in([i].sep) and
[i].outer([i-1],[i+1]).is_zero and
(VERTEXC::normal.in([i-1].sep) or VERTEXC::crossing.in([i-1].sep)) and
(VERTEXC::normal.in([i+1].sep) or VERTEXC::crossing.in([i+1].sep))
then
CodeDel(i);
else
i:=i+1;
end;
end;
end;
endOfBridge(i:INT, dir:INT):INT is
if dir.is_neg and
(VERTEXC::ks.in([i].sep) or VERTEXC::ts.in([i].sep) or VERTEXC::gs.in([i].sep)
or VERTEXC::under.in([i].sep)
)then return i; end;
if dir.is_pos and
(VERTEXC::ke.in([i].sep) or VERTEXC::te.in([i].sep) or VERTEXC::ge.in([i].sep)
or VERTEXC::endc.in([i].sep)
)then return i; end;
j:INT:=i;
loop
j:=j+dir;
if VERTEXC::separator.in([j].sep) or VERTEXC::under.in([j].sep) then break!; end;
end;
return j;
end;
nextBridge(i:INT):INT is
j:INT;
if VERTEXC::ke.in([i].sep) then return i+1;
elsif VERTEXC::te.in([i].sep) then return i+1;
elsif VERTEXC::endc.in([i].sep) then return i;
end;
j:=endOfBridge(i,1.int);
if VERTEXC::ke.in([j].sep) then return j+1;
elsif VERTEXC::te.in([j].sep) then return j+1;
end;
return j;
end;
end; -- class KNOT
class CROSS_SET
class CROSS_SET is
shared stp,enp,i1, i2, i1e, i2e, bStart:INT;
shared p10,p11,p20,p21,u,v,p: VERTEXK;
shared max,min:VERTEXK;
shared d, t, s:INT;
shared Succeed:BOOL;
setU1(Knot:KNOT) is
p10:=Knot[i1]; p11:=Knot[i1+1];
min:=#(p10.x.min(p11.x),p10.y.min(p11.y));
max:=#(p10.x.max(p11.x),p10.y.max(p11.y));
u:=p11-p10;
end;
CrossCheck0(Knot:KNOT):BOOL is
if (VERTEXC::separator.in(Knot[i1].sep))or
(VERTEXC::separator.in(Knot[i1+1].sep)) then i2:=i2e; return false;
elsif (VERTEXC::separator.in(Knot[i2].sep))or
(VERTEXC::separator.in(Knot[i2+1].sep)) then return false;
end;
return true;
end;
CrossCheck2(Knot:KNOT):BOOL is
p20:=Knot[i2]; p21:=Knot[i2+1];
if (max.x<=p20.x)and(max.x<=p21.x) then return false; end;
if (p20.x<=min.x)and(p21.x<=min.x) then return false; end;
if (max.y<=p20.y)and(max.y<=p21.y) then return false; end;
if (p20.y<=min.y)and(p21.y<=min.y) then return false; end;
v:=p21-p20;
d:=v.outer(u);
return d.is_non_zero;
end;
CrossCheck3(Knot:KNOT):BOOL is
neg:BOOL:=(d.is_neg);
t:=u.outer(p20-p10);
if neg then d:=-d; t:=-t; end;
if (t.is_non_pos) or (d<=t) then return false; end;
s:=v.outer(p20-p10);
if neg then s:=-s; end;
return (s.is_pos and (s<d));
end;
errorm(messageP:BOOL) is
Succeed:=false;
if messageP then
#OUT+"Check if the projection is regular.\n";
end;
end;
CrossCheck4(Knot:KNOT,messageP:BOOL):BOOL is
p:=u*(s.flt/d.flt)+p10;
if p.sameCoord(p20) then errorm(messageP); return false;
elsif p.sameCoord(p21) then errorm(messageP); return false;
elsif p.sameCoord(p10) then errorm(messageP); return false;
elsif p.sameCoord(p11) then errorm(messageP); return false;
end;
return true;
end;
i1Over(inout Knot:KNOT) is
Knot[i2].sep:=VERTEXC::cross_under;
Knot[i1+1].sep:=VERTEXC::cross_over;
end;
i2Over(inout Knot:KNOT) is
Knot[i2].sep:=VERTEXC::cross_over;
Knot[i1+1].sep:=VERTEXC::cross_under;
end;
CrossIn(inout Knot:KNOT) is
add:INT;
i2e:=i2e+2;
i1e:=i1e+2;
add:=0;
if i1<stp then add:=add+1; end;
if i2<stp then add:=add+1; end;
stp:=stp+add;
add:=0;
if i1<enp then add:=add+1; end;
if i2<enp then add:=add+1; end;
enp:=enp+add;
add:=0;
if i1<bStart then add:=add+1; end;
if i2<bStart then add:=add+1; end;
bStart:=bStart+add;
Knot.CodeIn(p.clone, i2.card+1);
i2:=i2+1;
Knot.CodeIn(p.clone, i1.card+1);
i2:=i2+1;
if i1<stp then i2Over(inout Knot);
elsif enp<i2 then i1Over(inout Knot);
else -- stp<=i1<i2<enp. (Cannot be i2=enp.)
if (bStart<i1)/=(bStart<i2) then i2Over(inout Knot);
else i1Over(inout Knot);
end;
end;
setU1(Knot);
end;
CrossSetM(inout Knot:KNOT, stp_org, enp_org:INT, messageP:BOOL, only_check:BOOL):BOOL
pre Knot.has_ind(stp_org) and Knot.has_ind(enp_org) is
-- find and set crossings from stp to enp.
-- I assume that stp < enp.
if ~Knot.checkCode then return false; end;
stp:=stp_org; enp:=enp_org;
Knot0::=Knot.clone;
Succeed:=true;
bStart::=Knot.bandStart;
i1e:=Knot.length.int-4; i2e:=i1e+2;
i1:=1.int;
loop while! ((i1<i1e)and(i1<stp) );
setU1(Knot); i2:=i1+2; if i2<stp then i2:=stp; end;
loop while! ((i2<i2e)and(i2<enp) );
if CrossCheck0(Knot)and CrossCheck2(Knot)and
CrossCheck3(Knot)and CrossCheck4(Knot,messageP)
then CrossIn(inout Knot);
end;
i2:=i2+1;
end;
i1:=i1+1;
end;
i1:=stp;
loop while! ((i1<i1e)and(i1<enp) );
setU1(Knot); i2:=i1+2;
loop while! (i2<i2e );
if CrossCheck0(Knot)and CrossCheck2(Knot)and
CrossCheck3(Knot)and CrossCheck4(Knot,messageP)
then CrossIn(inout Knot);
end;
i2:=i2+1;
end;
i1:=i1+1;
end;
if only_check then Knot:=Knot0; end;
return Succeed;
end;
CrossSet(Knot_org:KNOT, stp_org, enp_org:INT):KNOT is
-- set crossings on a regular projection and make a diagram.
k:KNOT:=Knot_org.clone;
r::=CrossSetM(inout k, stp_org, enp_org, false,false);
return k;
end;
end; -- class CROSS_SET