12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172# 1 "src/meshFC.ml"(* Functions for layout c_layout.
***********************************************************************)openPrintfopenBigarrayopenMesh_commontypemesh=c_layoutttypevec=c_layoutMesh_common.vectypemat=c_layoutMesh_common.mattypeint_mat=c_layoutMesh_common.int_mattypeint_vec=c_layoutMesh_common.int_vecletlayout=c_layout;;letempty_vec=Array1.createintlayout0letempty_mat2=Array2.create(float64)c_layout(0)(2)letempty_mat4=Array2.create(float64)c_layout(0)(4)letempty_int_mat2=Array2.create(int)c_layout(0)(2)letempty_int_mat3=Array2.create(int)c_layout(0)(3)letcheck_pointnamepoint=ifArray2.dim1(point)=0theninvalid_arg(name^": points cannot be empty");ifArray2.dim2(point)<>2theninvalid_arg(name^": dim2 points must be 2")letcheck_point_markernamepoint=function|None->empty_vec|Somem->letn=Array1.dimminif0<n&&n<Array2.dim1(point)theninvalid_arg(name^": point_marker too small");mletcheck_segment_markernamesegment=function|None->empty_vec|Somem->letn=Array1.dimminif0<n&&n<Array2.dim1(segment)theninvalid_arg(name^": segment_marker too small");mletcheck_holename=function|None->empty_mat2|Someh->ifArray2.dim1(h)>0&&Array2.dim2(h)<>2theninvalid_arg(name^": dim2 hole must be 2");hletcheck_regionname=function|None->empty_mat4|Somer->ifArray2.dim1(r)>0&&Array2.dim2(r)<>4theninvalid_arg(name^": dim2 region must be 4");rletpslg~hole~region~point_marker~point~segment_marker~segment=check_point"Mesh.pslg"point;letpoint_marker=check_point_marker"Mesh.pslg"pointpoint_markerinletsegment_marker=check_segment_marker"Mesh.pslg"segmentsegment_markerinlethole=check_hole"Mesh.pslg"holeinletregion=check_region"Mesh.pslg"regionin(objectmethodpoint=pointmethodpoint_marker=point_markermethodsegment=segmentmethodsegment_marker=segment_markermethodhole=holemethodregion=regionend:c_layoutpslg)(* Similar to [make_mesh] but with some elementary checks. *)letcreate~hole~region~point_marker~point~segment_marker~segment~neighbor~edge~edge_marker~triangle=check_point"Mesh.create"point;letpoint_marker=check_point_marker"Mesh.create"pointpoint_markerinletsegment=matchsegmentwith|None->empty_int_mat2|Somes->ifArray2.dim1(s)>0&&Array2.dim2(s)<>2theninvalid_arg"Mesh.create: dim2 segment must be 2";sinletsegment_marker=check_segment_marker"Mesh.create"segmentsegment_markerinlethole=check_hole"Mesh.create"holeinletregion=check_region"Mesh.create"regioninifArray2.dim1(triangle)=0theninvalid_arg"Mesh.create: triangle cannot be empty";ifArray2.dim2(triangle)<3theninvalid_arg"Mesh.create: dim2 triangle must be at least 3";letneighbor=matchneighborwith|None->empty_int_mat3|Somenbh->ifArray2.dim1(nbh)>0then(ifArray2.dim1(nbh)<>Array2.dim1(triangle)theninvalid_arg"Mesh.create: dim1 neighbor <> dim1 triangle";ifArray2.dim2(nbh)<>3theninvalid_arg"Mesh.create: dim2 neighbor <> 3";);nbhinletedge=matchedgewith|None->empty_int_mat2|Somee->ifArray2.dim1(e)>0&&Array2.dim2(e)<>2theninvalid_arg"Mesh.create: dim2 edge <> 2";einletedge_marker=matchedge_markerwith|None->empty_vec|Somee->ifArray1.dime>0&&Array1.dime<>Array2.dim1(edge)theninvalid_arg"Mesh.create: dim1 edge_marker <> dim1 edge";ein(objectmethodpoint=pointmethodpoint_marker=point_markermethodsegment=segmentmethodsegment_marker=segment_markermethodhole=holemethodregion=regionmethodtriangle=trianglemethodneighbor=neighbormethodedge=edgemethodedge_marker=edge_markerend:c_layoutt)(** Return the smaller box (xmin, xmax, ymin, ymax) containing the [mesh]. *)letbounding_box(mesh:mesh)=letxmin=refinfinityandxmax=refneg_infinityandymin=refinfinityandymax=refneg_infinityinletpoint=mesh#pointinfori=0toArray2.dim1(point)-1doletx=point.{i,0}andy=point.{i,1}inifx>!xmaxthenxmax:=x;ifx<!xminthenxmin:=x;ify>!ymaxthenymax:=y;ify<!yminthenymin:=y;done;(!xmin,!xmax,!ymin,!ymax)letlatex_write?edge:(edge_color=fun_->Someblack)(mesh:mesh)fh=letedge=mesh#edgeinletpt=mesh#pointinifArray2.dim1(edge)=0theninvalid_arg"Mesh.latex: mesh#edge must be nonempty";ifArray2.dim2(edge)<>2theninvalid_arg"Mesh.latex: mesh#edge must have 2 rows (fortran)";ifArray2.dim1(pt)=0theninvalid_arg"Mesh.latex: mesh#point must be nonempty";ifArray2.dim2(pt)<>2theninvalid_arg"Mesh.latex: mesh#point must have 2 rows (fortran)";letxmin,xmax,ymin,ymax=bounding_boxmeshinlatex_beginfh(xmax-.xmin)(ymax-.ymin)xminymin;(* Write lines *)fprintffh" %% %i triangles\n"(Array2.dim1(mesh#triangle));fore=0toArray2.dim1(edge)-1domatchedge_colorewith|None->()|Somecolor->leti1=edge.{e,0}andi2=edge.{e,1}inletp1={x=pt.{i1,0};y=pt.{i1,1}}andp2={x=pt.{i2,0};y=pt.{i2,1}}inlinefhcolorp1p2done;(* Write points *)fprintffh" %% %i points\n"(Array2.dim1(pt));fori=0toArray2.dim1(pt)-1dopoint_xyfhi(pt.{i,0})(pt.{i,1});done;latex_endfhletlatex?edgemeshfilename=letfh=open_outfilenameintrylatex_write?edgemeshfh;close_outfhwithe->close_outfh;raiseeletscilab(mesh:mesh)?(longitude=70.)?(azimuth=60.)?(mode=`Triangles)?(box=`Full)?edgecolor(z:vec)fname=lettriangle=mesh#triangleinletpt=mesh#pointinifArray2.dim1(triangle)=0theninvalid_arg"Mesh.scilab: mesh#triangle must be nonempty";ifArray2.dim2(triangle)<3theninvalid_arg"Mesh.scilab: mesh#triangle must have at least \
3 rows (fortran)";ifArray2.dim1(pt)=0theninvalid_arg"Mesh.scilab: mesh#point must be nonempty";ifArray2.dim2(pt)<>2theninvalid_arg"Mesh.scilab: mesh#point must have 2 rows (fortran)";ifArray1.dimz<Array2.dim1(pt)theninvalid_arg"Mesh.scilab: vector too small";letfname=ifFilename.check_suffixfname".sci"thenFilename.chop_extensionfnameelsefnameinletsci=fname^".sci"andxf=fname^"_x.dat"andyf=fname^"_y.dat"andzf=fname^"_z.dat"inletmode=matchmodewith|`Triangles->1|`Triangles_only->0|`No_triangles->-1inletbox=matchboxwith|`None->0|`Behind->2|`Box_only->3|`Full->4inletedgecolor,er,eg,eb=matchedgecolorwith|None->false,0.,0.,0.|Some(`Colorc)->(true,float((clsr16)land0xFF)/.255.,float((clsr8)land0xFF)/.255.,float(cland0xFF)/.255.)|Some(`Greyc)->ifc<=0.||c>1.then(false,0.,0.,0.)else(true,c,c,c)inletfh=open_outsciin(* Put the edge color at the bottom of the colormap so it is usually
hidden. Moreover, put enough color in the map so the edge color
is seldom drawn. *)fprintffh"mode(0);\n\
// Run in Scilab with: exec('%s')\n\
// Written by the OCaml Mesh module (version 0.9.2).\n\
// mesh: %i triangles, %i points.\n\
ocaml = struct('f', scf(), 'e', null, \
'x', fscanfMat('%s'), 'y', fscanfMat('%s'), \
'z', fscanfMat('%s'));\n\
clf();\n\
ocaml.e = gce();\n\
ocaml.e.hiddencolor = -1;\n\
ocaml.f.color_map = jetcolormap(100);\n"sci(Array2.dim1(triangle))(Array2.dim1(pt))(Filename.basenamexf)(Filename.basenameyf)(Filename.basenamezf);ifedgecolor&&mode>=0thenfprintffh"ocaml.f.color_map(1,:) = [%g, %g, %g];\n\
xset('color', 1);\n"eregeb;fprintffh"plot3d1(ocaml.x, ocaml.y, ocaml.z, theta=%g, alpha=%g, \
flag=[%d,2,%d]);\n\
disp('Save: xs2pdf(ocaml.f, ''%s.pdf'')');\n"longitudeazimuthmodebox(Filename.basenamefname);close_outfh;letsave_matfnamecoord=letfh=open_outfnamein(* We traverse several times the triangles but Scilab will not
have to transpose the matrices. *)forpoint=0to2dofort=0toArray2.dim1(triangle)-1dofprintffh"%.16e "(coord(triangle.{t,point}))done;fprintffh"\n";done;close_outfhinsave_matxf(funi->pt.{i,0});save_matyf(funi->pt.{i,1});save_matzf(funi->z.{i})letis_allowed_matlabc=('0'<=c&&c<='9')||('a'<=c&&c<='z')||('A'<=c&&c<='Z')||c='_'letmatlab(mesh:mesh)?(edgecolor=`Color0)?(linestyle="-")?(facealpha=1.)(z:vec)fname=lettr=mesh#triangleinletpt=mesh#pointinifArray2.dim1(tr)=0theninvalid_arg"Mesh.matlab: mesh#triangle must be nonempty";ifArray2.dim2(tr)<3theninvalid_arg"Mesh.matlab: mesh#triangle must have at least \
3 rows (fortran)";ifArray2.dim1(pt)=0theninvalid_arg"Mesh.matlab: mesh#point must be nonempty";ifArray2.dim2(pt)<>2theninvalid_arg"Mesh.matlab: mesh#point must have 2 rows (fortran)";letdir=Filename.dirnamefnameandbase=Filename.basenamefnameinletbase=ifFilename.check_suffixbase".m"thenBytes.unsafe_of_string(String.subbase0(String.lengthbase-2))elseBytes.of_stringbasein(* Matlab filenames can contain only alphanumeric characters and
underscores. Convert all other characters to underscore *)fori=0toBytes.lengthbase-1doifnot(is_allowed_matlab(Bytes.getbasei))thenBytes.setbasei'_'done;letbase=Bytes.unsafe_to_stringbaseinletmat=Filename.concatdir(base^".m")inletsave_xyfhcoord=forp=0toArray2.dim1(pt)-1dofprintffh"%.13g "(pt.{p,coord})done;fprintffh"\n"inletfh=open_outmatinfprintffh"%% Run in Matlab with: run %s\n\
%% Created by the OCaml Mesh module (version 0.9.2).\n\
%% print -painters -dpdf -r600 %s.pdf\n"matbase;fprintffh"mesh_x = [";save_xyfh0;fprintffh"];\nmesh_y = [";save_xyfh1;fprintffh"];\nmesh_z = [";fori=0toArray1.dim(z)-1dofprintffh"%.13f "z.{i}done;fprintffh"];\nmesh_triangles = [";fort=0toArray2.dim1(tr)-1dofprintffh"%i %i %i; "(tr.{t,0})(tr.{t,1})(tr.{t,2})done;letedgecolor=matchedgecolorwith|`None->"'none'"|`Flat->"'flat'"|`Interp->"'interp'"|`Colorc->ifc<0then"'none'"elseletb=float(cland0xFF)/.255.andg=float((clsr8)land0xFF)/.255.andr=float((clsr16)land0xFF)/.255.insprintf"[%g,%g,%g]"rgbinletfacealpha=iffacealpha<0.then0.elseiffacealpha>1.then1.elsefacealphain(* FIXME: protect against strings containing "'". *)fprintffh"];\ntrisurf(mesh_triangles, mesh_x, mesh_y, mesh_z, \
'FaceAlpha', %f, 'EdgeColor', %s, 'LineStyle', '%s');\n"facealphaedgecolorlinestyle;close_outfh;;(* Sort the vertices at node [n0] by increasing (counterclockwise)
angle w.r.t. the base vertex [i0]. [TriangularSurfacePlot] (not
[PlanarGraphPlot] it seems) requires the vertices to be ordered. *)letsort_counterclockwise(pt:mat)n0=function|([]|[_])asadj->adj|n1::tl->letx0=pt.{0,n0}andy0=pt.{1,n0}inletdx1=pt.{0,n1}-.x0anddy1=pt.{1,n1}-.y0in(* Since [atan2] returns an angle in ]-pi, pi], the angle of
(dx1,dy1) will be set to pi so that the order given by the
angles is correct. Also there is no need to norm the vectors
[(dx1,dy1)] and [(dx,dy)] because that will only dilate
[(e1,e2)] which does not change the value of [atan2]. *)letanglen=letdx=pt.{0,n}-.x0anddy=pt.{1,n}-.y0inlete1=-.dx*.dx1-.dy*.dy1ande2=dx*.dy1-.dy*.dx1inatan2e2e1in(* Add angles *)lettl=List.map(funn->(n,anglen))tlinlettl=List.fast_sort(fun(_,a1)(_,a2)->comparea1a2)tlinn1::List.map(fun(n,_)->n)tl;;(* Return an array [adj] such that [adj.(i)] is the list of the
adjacent nodes to [i]. *)letadjacency(mesh:mesh)=letpt=mesh#pointinletn=Array2.dim1(pt)inletadj=Array.make(n+0)[]inletedge=mesh#edgeinfore=0toArray2.dim1(edge)-1doleti1=edge.{e,0}andi2=edge.{e,1}inadj.(i1)<-i2::adj.(i1);adj.(i2)<-i1::adj.(i2);done;(* This is important for TriangularSurfacePlot (that uses the order
for orientation?). *)Array.mapi(funn0adj->sort_counterclockwiseptn0adj)adjletis_allowed_mathematicac=('0'<=c&&c<='9')||('a'<=c&&c<='z')||('A'<=c&&c<='Z')letcount_mathematica_allowedbase=letn=ref0infori=0toString.lengthbase-1doifis_allowed_mathematica(String.unsafe_getbasei)thenincrndone;!n(* Remove all chars that are not alphanumeric. *)letmathematica_safebase=letlen=count_mathematica_allowedbaseiniflen=String.lengthbasethenbaseelse(letbase'=Bytes.createleninletj=ref0infori=0toString.lengthbase-1doletc=String.unsafe_getbaseiinifis_allowed_mathematicacthen(Bytes.setbase'!jc;incrj;)done;Bytes.unsafe_to_stringbase')letmathematica_print_floatfhf=lets=Bytes.unsafe_of_string(sprintf"%.16g"f)intrylete=Bytes.indexs'e'inoutputfhs0e;output_stringfh"*^";outputfhs(e+1)(Bytes.lengths-e-1)withNot_found->outputfhs0(Bytes.lengths)letmathematica(mesh:mesh)(z:vec)fname=letpt=mesh#pointinifArray2.dim1(pt)=0theninvalid_arg"Mesh.mathematica: mesh#point must be nonempty";ifArray2.dim2(pt)<>2theninvalid_arg"Mesh.mathematica: mesh#point must have 2 rows (fortran)";ifArray2.dim1(mesh#edge)=0theninvalid_arg"Mesh.mathematica: mesh#edge must be nonempty";ifArray2.dim2(mesh#edge)<>2theninvalid_arg"Mesh.mathematica: mesh#edge must have 2 rows (fortran)";letbase=Filename.basenamefnameinletpkg,fname=ifFilename.check_suffixbase".m"thenmathematica_safe(String.subbase0(String.lengthbase-2)),fnameelsemathematica_safebase,fname^".m"inletpkg=String.capitalize_asciipkginletfh=open_outfnameinfprintffh"(* Created by the OCaml Mesh module (version 0.9.2)) \
*)\n";fprintffh"%s`xyz = {"pkg;output_stringfh"{";mathematica_print_floatfhpt.{0,0};output_stringfh", ";mathematica_print_floatfhpt.{1,0};output_stringfh", ";mathematica_print_floatfhz.{0};output_stringfh"}";fori=0+1toArray2.dim1(pt)-1dooutput_stringfh", {";mathematica_print_floatfhpt.{0,i};output_stringfh", ";mathematica_print_floatfhpt.{1,i};output_stringfh", ";mathematica_print_floatfhz.{i};output_stringfh"}"done;fprintffh"};\n\n";letadj=adjacencymeshinletoutput_adji=(* mathematica indices start at 1 *)matchadj.(i)with|[]->fprintffh"{%i, {}}"((i)+1)|n::tl->fprintffh"{%i, {%i"((i)+1)((n)+1);List.iter(funn->fprintffh", %i"((n)+1))tl;fprintffh"}}"infprintffh"%s`adj = {"pkg;output_adj0;fori=0+1toArray.lengthadj-1dooutput_stringfh", ";output_adjidone;fprintffh"};\n\n";fprintffh"Needs[\"ComputationalGeometry`\"];\n";fprintffh"TriangularSurfacePlot[%s`xyz, %s`adj, Axes -> True]\n"pkgpkg;close_outfh;;(************************************************************************)(* mesh_level_curvesC.ml included by "make_FC_code.ml" with Mesh = "Mesh". *)(* Generic code to draw level cuves. To be included in a file that
defines the drawing primitives. *)moduleM=Map.Make(structtypet=intletcomparexy=compare(x:int)yend)(* Module to build a structure helping to determine when the segment
joining 2 points are on the boundary. *)moduleEdge=structletmake()=refM.emptyletadd_edgeti1i2=assert(i1<i2);tryletv=M.findi1!tinv:=i2::!vwithNot_found->t:=M.addi1(ref[i2])!t(* Declare the segment joining the points of indexes [i1] and [i2]
as being part of the boundary. It is auusmed that [i1 <> i2]. *)letaddti1i2=ifi1<i2thenadd_edgeti1i2elseadd_edgeti2i1leton_boundaryti1i2=assert(i1<i2);tryletv=M.findi1!tinList.memi2!vwithNot_found->false(* Tells whether the segment (if any) joining the points of indices
[i1] and [i2] is on the boundary (according to the information in
[t]). It is assumed that [i1 <> i2]. *)letonti1i2=ifi1<i2thenon_boundaryti1i2elseon_boundaryti2i1end;;letdefault_level_eql1l2=abs_float(l1-.l2)<=1E-8*.(abs_floatl1+.abs_floatl2)letmidpq={x=0.5*.(p.x+.q.x);y=0.5*.(p.y+.q.y)}(* Intersection of the curve et level [l] and the line passing through
(x1,y1) and (x2,y2). [z1 <> z2] assumed. *)letintercept{x=x1;y=y1}z1{x=x2;y=y2}z2l=letd=z1-.z2anda=l-.z2andb=z1-.lin{x=(a*.x1+.b*.x2)/.d;y=(a*.y1+.b*.y2)/.d}letdraw_levels~boundary(mesh:mesh)(z:vec)?(level_eq=default_level_eq)levelssurf=letedge=mesh#edgeinletmarker=mesh#edge_markerinletpt=mesh#pointinifArray2.dim1(edge)=0theninvalid_arg("Mesh.level_curves: mesh#edge must be nonempty");ifArray2.dim2(edge)<>2theninvalid_arg("Mesh.level_curves: mesh#edge must have 2 rows (fortran)");ifArray1.dimmarker<Array2.dim1(edge)theninvalid_arg("Mesh.level_curves: dim mesh#edge_marker < number edges");ifArray2.dim1(pt)=0theninvalid_arg("Mesh.level_curves: mesh#point must be nonempty");ifArray2.dim2(pt)<>2theninvalid_arg("Mesh.level_curves: mesh#point must have 2 rows (fortran)");letbd=Edge.make()in(* Draw the boundary edges *)fore=0toArray2.dim1(edge)-1doletm=marker.{e}inifm<>0(* not an interior point *)thenbeginleti1=edge.{e,0}andi2=edge.{e,1}inEdge.addbdi1i2;(* collect boundary points *)matchboundarymwith|None->()|Somecolor->letp1={x=pt.{i1,0};y=pt.{i1,1}}andp2={x=pt.{i2,0};y=pt.{i2,1}}inlinesurfcolorp1p2enddone;lettr=mesh#triangleinifArray2.dim1(tr)=0theninvalid_arg("Mesh.level_curves: mesh#triangle must be nonempty");ifArray2.dim2(tr)<3theninvalid_arg("Mesh.level_curves: mesh#triangle must have at least 3 \
rows (fortran) or 3 columns (C)");letmarker=mesh#point_markerinfort=0toArray2.dim1(tr)-1doleti1=tr.{t,0}andi2=tr.{t,1}andi3=tr.{t,2}inletp1={x=pt.{i1,0};y=pt.{i1,1}}andz1=z.{i1}inletp2={x=pt.{i2,0};y=pt.{i2,1}}andz2=z.{i2}inletp3={x=pt.{i3,0};y=pt.{i3,1}}andz3=z.{i3}inList.iter(fun(l,color)->(* Draw the level curve [l] on the triangle [t] except if
that curve is on the boundary. *)iflevel_eqlz1then(iflevel_eqlz2then(iflevel_eqlz3then(* The entire triangle is at the same level. Try to
remove boundary edges. *)ifEdge.onbdi1i2thenifEdge.onbdi1i3||Edge.onbdi2i3thentrianglesurfcolorp1p2p3(* Full triangle ! *)elselinesurfcolorp3(midp1p2)else(* i1-i2 not on boundary *)ifEdge.onbdi1i3thenifEdge.onbdi2i3thentrianglesurfcolorp1p2p3elselinesurfcolorp2(midp1p3)else(* i1-i3 not on boundary *)ifEdge.onbdi2i3thenlinesurfcolorp1(midp2p3)elsetrianglesurfcolorp1p2p3(* Full triangle ! *)else(* l = z1 = z2 <> z3 *)ifnot(Edge.onbdi1i2)thenlinesurfcolorp1p2)else(* l = z1 <> z2 *)iflevel_eqlz3then(* l = z1 = z3 <> z2 *)(ifnot(Edge.onbdi1i3)thenlinesurfcolorp1p3)elseif(z2<l&&l<z3)||(z3<l&&l<z2)thenlinesurfcolorp1(interceptp2z2p3z3l))elseifl<z1then(iflevel_eqlz2theniflevel_eqlz3then(ifnot(Edge.onbdi2i3)thenlinesurfcolorp2p3)elseifl>z3then(* z3 < l = z2 < z1 *)linesurfcolorp2(interceptp1z1p3z3l)else(* corner point, inside the domain. Ususally this
happens because the level line passes through a
triangle corner. *)(ifmarker.{i2}=0thenpointsurfi2p2)elseifl<z2then(iflevel_eqlz3then(ifmarker.{i3}=0thenpointsurfi3p3)elseifl>z3thenlinesurfcolor(interceptp1z1p3z3l)(interceptp2z2p3z3l))else(* z2 < l < z1 *)linesurfcolor(interceptp1z1p2z2l)(iflevel_eqlz3thenp3elseifl<z3theninterceptp2z2p3z3lelse(* l > z3 *)interceptp1z1p3z3l))else(* l > z1 *)((* Symmetric of [l < z1] with all inequalities reversed *)iflevel_eqlz2theniflevel_eqlz3then(ifnot(Edge.onbdi2i3)thenlinesurfcolorp2p3)elseifl<z3then(* z1 < l = z2 < z3 *)linesurfcolorp2(interceptp1z1p3z3l)else(* corner point, inside the domain *)(ifmarker.{i2}=0thenpointsurfi2p2)elseifl>z2then(iflevel_eqlz3then(ifmarker.{i3}=0thenpointsurfi3p3)elseifl<z3thenlinesurfcolor(interceptp1z1p3z3l)(interceptp2z2p3z3l))else(* z1 < l < z2 *)linesurfcolor(interceptp1z1p2z2l)(iflevel_eqlz3thenp3elseifl>z3theninterceptp2z2p3z3lelse(* l < z3 *)interceptp1z1p3z3l)))levelsdone;;typepolygon_fill=|Tri123(* triangle with edge 1 and cut in edges 2, 3 *)|Tri231|Tri312|Quad123(* Quadrilateral with edges 1-2 and 1-3 of the triangle cut *)|Quad231|Quad312|Whole|Empty;;(* base 3: c1 + 1 + 3(c2 + 1) + 9(c3 + 1). The [c1], [c2] and [c3]
are the comparisons of the 3 corners with the desired level. *)letindexc1c2c3=c1+3*c2+9*c3+13letsuper=letd=Array.make27Emptyind.(index(1)(1)(1))<-Whole;d.(index(1)(1)(0))<-Whole;d.(index(1)(1)(-1))<-Quad312;d.(index(1)(0)(1))<-Whole;d.(index(1)(0)(0))<-Whole;d.(index(1)(0)(-1))<-Tri123;d.(index(1)(-1)(1))<-Quad231;d.(index(1)(-1)(0))<-Tri123;d.(index(1)(-1)(-1))<-Tri123;d.(index(0)(1)(1))<-Whole;d.(index(0)(1)(0))<-Whole;d.(index(0)(1)(-1))<-Tri231;d.(index(0)(0)(1))<-Whole;d.(index(0)(0)(0))<-Empty;(* > 0 required *)d.(index(0)(0)(-1))<-Empty;d.(index(0)(-1)(1))<-Tri312;d.(index(0)(-1)(0))<-Empty;d.(index(0)(-1)(-1))<-Empty;d.(index(-1)(1)(1))<-Quad123;d.(index(-1)(1)(0))<-Tri231;d.(index(-1)(1)(-1))<-Tri231;d.(index(-1)(0)(1))<-Tri312;d.(index(-1)(0)(0))<-Empty;d.(index(-1)(0)(-1))<-Empty;d.(index(-1)(-1)(1))<-Tri312;d.(index(-1)(-1)(0))<-Empty;d.(index(-1)(-1)(-1))<-Empty;dletsub=letd=Array.make27Emptyind.(index(1)(1)(1))<-Empty;d.(index(1)(1)(0))<-Empty;d.(index(1)(1)(-1))<-Tri312;d.(index(1)(0)(1))<-Empty;d.(index(1)(0)(0))<-Empty;d.(index(1)(0)(-1))<-Tri312;d.(index(1)(-1)(1))<-Tri231;d.(index(1)(-1)(0))<-Tri231;d.(index(1)(-1)(-1))<-Quad123;d.(index(0)(1)(1))<-Empty;d.(index(0)(1)(0))<-Empty;d.(index(0)(1)(-1))<-Tri312;d.(index(0)(0)(1))<-Empty;d.(index(0)(0)(0))<-Empty;(* < 0 required *)d.(index(0)(0)(-1))<-Whole;d.(index(0)(-1)(1))<-Tri231;d.(index(0)(-1)(0))<-Whole;d.(index(0)(-1)(-1))<-Whole;d.(index(-1)(1)(1))<-Tri123;d.(index(-1)(1)(0))<-Tri123;d.(index(-1)(1)(-1))<-Quad231;d.(index(-1)(0)(1))<-Tri123;d.(index(-1)(0)(0))<-Whole;d.(index(-1)(0)(-1))<-Whole;d.(index(-1)(-1)(1))<-Quad312;d.(index(-1)(-1)(0))<-Whole;d.(index(-1)(-1)(-1))<-Whole;dletdraw_xxx_leveldecisionname?(boundary=(fun_->Someblack))(mesh:mesh)(z:vec)lcolorsurf=letedge=mesh#edgeinletedge_marker=mesh#edge_markerinletpt=mesh#pointinifArray2.dim1(edge)=0theninvalid_arg("Mesh"^name^": mesh#edge must be nonempty");ifArray2.dim2(edge)<>2theninvalid_arg("Mesh"^name^": mesh#edge must have 2 rows (fortran)");ifArray1.dimedge_marker<Array2.dim1(edge)theninvalid_arg("Mesh"^name^": dim mesh#edge_marker < number edges");ifArray2.dim1(pt)=0theninvalid_arg("Mesh"^name^": mesh#point must be nonempty");ifArray2.dim2(pt)<>2theninvalid_arg("Mesh"^name^": mesh#point must have 2 rows (fortran)");lettr=mesh#triangleinifArray2.dim1(tr)=0theninvalid_arg("Mesh"^name^": mesh#triangle must be nonempty");ifArray2.dim2(tr)<3theninvalid_arg("Mesh"^name^": mesh#triangle must have at least 3 \
rows (fortran) or 3 columns (C)");fort=0toArray2.dim1(tr)-1doleti1=tr.{t,0}andi2=tr.{t,1}andi3=tr.{t,2}inletp1={x=pt.{i1,0};y=pt.{i1,1}}andz1=z.{i1}inletp2={x=pt.{i2,0};y=pt.{i2,1}}andz2=z.{i2}inletp3={x=pt.{i3,0};y=pt.{i3,1}}andz3=z.{i3}inmatchdecision.(index(comparez1l)(comparez2l)(comparez3l))with|Tri123->fill_trianglesurfcolorp1(interceptp1z1p2z2l)(interceptp1z1p3z3l)|Tri231->fill_trianglesurfcolorp2(interceptp2z2p3z3l)(interceptp2z2p1z1l)|Tri312->fill_trianglesurfcolorp3(interceptp3z3p1z1l)(interceptp3z3p2z2l)|Quad123->fill_quadrilateralsurfcolor(interceptp1z1p2z2l)(interceptp1z1p3z3l)p3p2|Quad231->fill_quadrilateralsurfcolor(interceptp2z2p3z3l)(interceptp2z2p1z1l)p1p3|Quad312->fill_quadrilateralsurfcolor(interceptp3z3p1z1l)(interceptp3z3p2z2l)p2p1|Whole->fill_trianglesurfcolorp1p2p3|Empty->()done;(* Draw the boundary edges (over the filled area) *)fore=0toArray2.dim1(edge)-1doletm=edge_marker.{e}inifm<>0(* not an interior point *)thenbeginmatchboundarymwith|None->()|Somecolor->leti1=edge.{e,0}andi2=edge.{e,1}inletp1={x=pt.{i1,0};y=pt.{i1,1}}andp2={x=pt.{i2,0};y=pt.{i2,1}}inlinesurfcolorp1p2enddoneletdraw_super_level?boundarymeshzlevelcolorsurf=draw_xxx_levelsuper".super_level"?boundarymeshzlevelcolorsurfletdraw_sub_level?boundarymeshzlevelcolorsurf=draw_xxx_levelsub".sub_level"?boundarymeshzlevelcolorsurf;;(************************************************************************)letlevel_curves?(boundary=(fun_->Someblack))(mesh:mesh)(z:vec)?level_eqlevelsfname=letfh=open_outfnameinletxmin,xmax,ymin,ymax=bounding_boxmeshinlatex_beginfh(xmax-.xmin)(ymax-.ymin)xminymin;draw_levels~boundarymeshz?level_eqlevelsfh;latex_endfh;close_outfhletsuper_level?boundary(mesh:mesh)(z:vec)levelcolorfname=letfh=open_outfnameinletxmin,xmax,ymin,ymax=bounding_boxmeshinlatex_beginfh(xmax-.xmin)(ymax-.ymin)xminymin;draw_super_level?boundarymeshzlevelcolorfh;latex_endfh;close_outfhletsub_level?boundary(mesh:mesh)(z:vec)levelcolorfname=letfh=open_outfnameinletxmin,xmax,ymin,ymax=bounding_boxmeshinlatex_beginfh(xmax-.xmin)(ymax-.ymin)xminymin;draw_sub_level?boundarymeshzlevelcolorfh;latex_endfh;close_outfh(* Determine the number of superdiagonals + 1 main diagonal *)letband_height_P1filter(mesh:mesh)=lettr=mesh#triangleinletkd=ref0inmatchfilterwith|None->fort=0toArray2.dim1(tr)-1doleti1=tr.{t,0}andi2=tr.{t,1}andi3=tr.{t,2}inkd:=max4!kd(abs(i1-i2))(abs(i2-i3))(abs(i3-i1))done;!kd+1|Somecond->fort=0toArray2.dim1(tr)-1doleti1=tr.{t,0}andi2=tr.{t,1}andi3=tr.{t,2}inifcondi1then(ifcondi2thenifcondi3thenkd:=max4!kd(abs(i1-i2))(abs(i2-i3))(abs(i3-i1))else(* exlude i3 *)kd:=max2!kd(abs(i2-i1))else(* exclude i2 *)ifcondi3thenkd:=max2!kd(abs(i3-i1)))else(* exclude i1 *)ifcondi2&&condi3thenkd:=max2!kd(abs(i3-i2))done;!kd+1(* Return the index with the lowest nonnegative [deg] (negative
degrees are ignored). Return [-1] if all degrees are < 0. *)letmin_deg(deg:intarray)=leti=ref(-1)inletdegi=ref(max_int)inforj=0toArray.lengthdeg-1doifdeg.(j)>=0&°.(j)<!degithen(i:=j;degi:=deg.(j))done;!i(* sub
***********************************************************************)(* Iterator with indices adapted to the current layout. *)letreciterifi=function|[]->()|x::tl->fix;iterif(succi)tlletiterifl=iterif0lletfilter_columns_shift(m:int_mat)selectshift=letcols=ref[]inletnselected=ref0in(* length of [cols] *)forc=0toArray2.dim1(m)-1doifselectmcthen(cols:=c::!cols;incrnselected)done;letcols=List.rev!colsinletm'=Array2.create(int)c_layout(!nselected)(Array2.dim2(m))initeri(funipi->forj=0toArray2.dim2(m')-1dom'.{i,j}<-m.{pi,j}-shiftdone)cols;m',!nselected,colsletsub_markers(v:int_vec)ncols=ifArray1.dimv=0thenv(* no markers *)else(letv'=Array1.create(int)c_layout(n)initeri(funipi->v'.{i}<-v.{pi})cols;v')letinternal_sub(mesh:c_layout#t)?poslen=letpos=matchposwith|None->0|Somepos->ifpos<0theninvalid_arg"Mesh.sub: pos < 0";posiniflen<=0theninvalid_arg"Mesh.sub: len <= 0";ifpos+len>Array2.dim1(mesh#point)theninvalid_arg"Mesh.sub: len too large";letshift=posinletmax_point_idx=pos+len-1inletsub_pointi=pos<=i&&i<=max_point_idxin(* Points *)letpoint=Array2.sub_leftmesh#pointposleninletpoint_marker=Array1.submesh#point_markerposlenin(* Segments *)letselect2(m:int_mat)i=sub_pointm.{i,0}&&sub_pointm.{i,1}inletnew_seg,n,cols=filter_columns_shiftmesh#segmentselect2shiftinletnew_seg_marker=sub_markersmesh#segment_markerncolsin(* Triangles *)letselect3(m:int_mat)t=sub_pointm.{t,0}&&sub_pointm.{t,1}&&sub_pointm.{t,2}inletnew_tr,n_tr,cols_tr=filter_columns_shiftmesh#triangleselect3shiftin(* Neighbors corresponding to the selected triangles. *)letnew_neighbor=letold_nbh=mesh#neighborinifArray2.dim1(old_nbh)=0thenold_nbhelse(letnbh=Array2.create(int)c_layout(n_tr)(3)in(* new neighbor *)lettrans=Array1.create(int)c_layout(Array2.dim1(mesh#triangle))in(* old idx → new *)Array1.filltrans(-1);(* default: no corresponding index *)iteri(funipi->trans.{pi}<-i)cols_tr;iteri(funipi->nbh.{i,0}<-trans.{old_nbh.{pi,0}};nbh.{i,1}<-trans.{old_nbh.{pi,1}};nbh.{i,2}<-trans.{old_nbh.{pi,2}};)cols_tr;nbh)in(* Edges *)letnew_edge,n,cols=filter_columns_shiftmesh#edgeselect2shiftinletnew_edge_marker=sub_markersmesh#edge_markerncolsin(make_mesh~point:point~point_marker:point_marker~segment:new_seg~segment_marker:new_seg_marker~hole:mesh#hole(* keep *)~region:mesh#region(* keep *)~triangle:new_tr~neighbor:new_neighbor~edge:new_edge~edge_marker:new_edge_marker,n_tr,cols_tr)letsub(mesh:mesh)?poslen=letm,_,_=internal_submesh?posleninm(* Permutations
***********************************************************************)(** Apply the permutation [perm] to the [mesh]. *)letdo_permute_pointsname(mesh:mesh)(perm:int_vec)(inv_perm:int_vec):mesh=(* Build the new mesh *)letold_pt=mesh#pointinletn=Array2.dim1(old_pt)inifn<>Array1.dimpermtheninvalid_arg(sprintf"%s: dim1 #point = %i <> dim perm = %i"namen(Array1.dimperm));letpt=Array2.create(float64)c_layout(n)(2)inletlast_pt_idx=Array2.dim1(pt)-1infori=0tolast_pt_idxdoletold_i=perm.{i}inpt.{i,0}<-old_pt.{old_i,0};pt.{i,1}<-old_pt.{old_i,1};done;letold_ptm=mesh#point_markerinletptm=Array1.createintlayout(Array1.dimold_ptm)infori=0toArray1.dim(ptm)-1doptm.{i}<-old_ptm.{perm.{i}}done;letold_seg=mesh#segmentinletseg=Array2.create(int)c_layout(Array2.dim1(old_seg))(2)infors=0toArray2.dim1(seg)-1doleti1=old_seg.{s,0}inifi1<0||i1>last_pt_idxthenfailwith(sprintf"%s: mesh#segment.{%i} = %i not in [%i..%i]"namesi10last_pt_idx);seg.{s,0}<-inv_perm.{i1};leti2=old_seg.{s,1}inifi2<0||i2>last_pt_idxthenfailwith(sprintf"%s: mesh#segment.{%i} = %i not in [%i..%i]"namesi20last_pt_idx);seg.{s,1}<-inv_perm.{i2};done;letold_tr=mesh#triangleinlettr=Array2.create(int)c_layout(Array2.dim1(old_tr))(Array2.dim2(old_tr))infort=0toArray2.dim1(tr)-1doforc=0toArray2.dim2(tr)-1dotr.{t,c}<-inv_perm.{old_tr.{t,c}}done;done;letold_edge=mesh#edgeinletedge=Array2.create(int)c_layout(Array2.dim1(old_edge))(2)infore=0toArray2.dim1(edge)-1doedge.{e,0}<-inv_perm.{old_edge.{e,0}};edge.{e,1}<-inv_perm.{old_edge.{e,1}};done;make_mesh~point:pt~point_marker:ptm~segment:seg~segment_marker:mesh#segment_marker~hole:mesh#hole~region:mesh#region~triangle:tr~neighbor:mesh#neighbor~edge:edge~edge_marker:mesh#edge_markerletpermute_points_name="Mesh.permute_points"letpermute_points_unsafemeshperm=letn=Array2.dim1(mesh#point)in(* Inverse perm *)letinv_perm=Array1.createintlayoutninfori=0toArray1.dim(perm)-1doinv_perm.{perm.{i}}<-idone;do_permute_pointspermute_points_namemeshperminv_permletinverse_permname(perm:int_vec)=(* Inverse perm and check that [perm] is indeed a permuation. *)letinv_perm=Array1.createintlayout(Array1.dimperm)inArray1.fillinv_perm(-1);(* never an index *)letlast_el=Array1.dim(perm)-1infori=0tolast_eldoletpi=perm.{i}inifpi<0||pi>last_eltheninvalid_arg(sprintf"%s: perm.{%i} = %i not in [%i..%i]"nameipi0last_el)elseifinv_perm.{pi}<0theninv_perm.{pi}<-ielseinvalid_arg(sprintf"%s: not a permutation (perm.{%i} = %i = \
perm.{%i})"nameinv_perm.{pi}pii)done;inv_permletpermute_points(mesh:mesh)~invperm=letinv_perm=inverse_permpermute_points_nameperminifinvthendo_permute_pointspermute_points_namemeshinv_permpermelsedo_permute_pointspermute_points_namemeshperminv_permletdo_permute_trianglesname(mesh:mesh)(perm:int_vec)=letold_tr=mesh#triangleinletn=Array2.dim1(old_tr)inifn<>Array1.dimpermtheninvalid_arg(sprintf"%s: dim1 #triangle = %i <> dim perm = %i"namen(Array1.dimperm));lettr=Array2.create(int)c_layout(n)(Array2.dim2(old_tr))inletlast_tr_idx=Array2.dim1(tr)-1infori=0tolast_tr_idxdoforj=0toArray2.dim2(tr)-1dotr.{i,j}<-old_tr.{perm.{i},j}donedone;letold_nbh=mesh#neighborinletnbh=ifArray2.dim1(old_nbh)=0thenold_nbhelse(ifArray2.dim2(old_nbh)<>3theninvalid_arg(sprintf"%s: invalid mesh: ROW #neighbor <> 3"name);ifn<>Array2.dim1(old_nbh)theninvalid_arg(sprintf"%s: invalid mesh: COL #neighbor = %i <> \
COL #triangle = %i"name(Array2.dim1(old_nbh))n);letnbh=Array2.create(int)c_layout(n)(3)infori=0tolast_tr_idxdoletold_i=perm.{i}innbh.{i,0}<-old_nbh.{old_i,0};nbh.{i,1}<-old_nbh.{old_i,1};nbh.{i,2}<-old_nbh.{old_i,2};done;nbh)inmake_mesh~point:mesh#point~point_marker:mesh#point_marker~segment:mesh#segment~segment_marker:mesh#segment_marker~hole:mesh#hole~region:mesh#region~triangle:tr~neighbor:nbh~edge:mesh#edge~edge_marker:mesh#edge_markerletpermute_triangles_name="Mesh.permute_triangles"letpermute_triangles(mesh:mesh)~invperm=letinv_perm=inverse_permpermute_triangles_nameperminifinvthendo_permute_trianglespermute_triangles_namemeshinv_permelsedo_permute_trianglespermute_triangles_namemeshperm(* Band
***********************************************************************)(* http://ciprian-zavoianu.blogspot.com/2009/01/project-bandwidth-reduction.html
*)letcuthill_mckee~revperm(mesh:mesh):mesh=letn=Array2.dim1(mesh#point)inletperm=matchpermwith|None->Array1.createintlayoutn|Somep->ifArray1.dimp<>ntheninvalid_arg"Mesh.cuthill_mckee: dim perm <> number of points";pinletdeg=Array.make(n+0)0in(* degree of adjacency of each node *)letnbh=Array.make(n+0)[]in(* list of adjacent nodes *)letedge=mesh#edgeinfore=0toArray2.dim1(edge)-1doleti1=edge.{e,0}andi2=edge.{e,1}innbh.(i1)<-i2::nbh.(i1);deg.(i1)<-deg.(i1)+1;nbh.(i2)<-i1::nbh.(i2);deg.(i2)<-deg.(i2)+1;done;letfree=ref(0)in(* first free position in [perm] *)letq=Queue.create()inletaddnode=perm.{!free}<-node;incrfree;deg.(node)<--1;(* [i] put in the final vec. *)letnbhs=List.filter(funi->deg.(i)>=0)nbh.(node)inletnbhs=List.fast_sort(funi1i2->comparedeg.(i1)deg.(i2))nbhsinList.iter(funi->Queue.addiq)nbhsinletlast_pt=Array1.dim(perm)-1inwhile!free<=last_ptdoadd(min_degdeg);whilenot(Queue.is_emptyq)doletc=Queue.takeqinifdeg.(c)>=0thenaddcdonedone;ifrevthen(lets=if0=0thenn-1elsen+1in(* FIXME: cond known at compil. *)fori=0ton/2-1+0dolett=perm.{i}inperm.{i}<-perm.{s-i};perm.{s-i}<-t;done);permute_points_unsafemeshperm(* A Generalized GPS Algorithm For Reducing The Bandwidth And Profile
Of A Sparse Matrix, Q. Wang, Y. C. Guo, and X. W. Shi
http://www.jpier.org/PIER/pier90/09.09010512.pdf *)letggps(mesh:mesh)perm:mesh=letn=Array2.dim1(mesh#point)inletperm=matchpermwith|None->Array1.createintlayoutn|Somep->ifArray1.dimp<>ntheninvalid_arg"Mesh.ggps: dim perm <> number of points";pinletdeg=Array.make(n+0)0in(* degree of adjacency of each node *)letedge=mesh#edgeinfore=0toArray2.dim1(edge)-1doleti1=edge.{e,0}andi2=edge.{e,1}indeg.(i1)<-deg.(i1)+1;deg.(i2)<-deg.(i2)+1;done;let_v=min_degdegin(* FIXME *)permute_points_unsafemeshperm(* Local Variables: *)(* compile-command: "make -k -C .." *)(* End: *)