# Source: papier-core.js

    /*
    	This file is a part of "Papier" a paper-crafting tool

    Copyright (C) 2018  Saint Pierre Thomas ( s1pierro@protonmail.fr )

    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 3 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, see <http://www.gnu.org/licenses/>.
    */
    'use strict';

    /** @constructor */

    function Page (pattern)
    {
    	this.patterns = [pattern];
    	this.desc = 'page '+dispatcher.pages.length+1;
    	this.size = pattern.papersizereq.s;
    	this.height = pattern.papersizereq.h;
    	this.width = pattern.papersizereq.w;
    }
    /** @description

    	print page to dest_container;
    	@param {object} dest_container
    	@param {text} guid the guid of the pattern to highlight

     */
    Page.prototype.out = function ( dest_container, pguid )
    {

    		$('#svg7').attr('viewBox', '0 0 '+this.width+' '+this.height);

    	renderplane.innerHTML = "";

    	for ( var ig = 0 ; ig < this.patterns.length ; ig++)
    	{
    		var g = document.createElementNS("http://www.w3.org/2000/svg",'g');
    		g.setAttribute( 'id', this.patterns[ig].guid );
    		g.setAttribute( 'transform', 'translate('+this.patterns[ig].position.x+', '+this.patterns[ig].position.y+')' );
    		var style = 'fill: #777; stroke: #aaa;';
    		for ( var i = 0 ; i < this.patterns[ig].triangles.length ; i++ )
    		{
    			if ( this.patterns[ig].guid == pguid ) style = 'fill: #ddd; stroke: #aaa;';
    			var tmptri = this.patterns[ig].trianglesflatcoord[i];
    			var svgtrigon =  tmptri[0].c[0]+', '+tmptri[0].c[1]+
    							 ' '+tmptri[1].c[0]+', '+tmptri[1].c[1]+
    							 ' '+tmptri[2].c[0]+', '+tmptri[2].c[1];
    			var svg = document.createElementNS("http://www.w3.org/2000/svg",'polygon');
    			svg.setAttribute('points', svgtrigon);
    			//svg.setAttribute('class', 'flatshape' );
    			svg.setAttribute('style', style );

    			g.appendChild(svg);
    			g.appendChild(svg);
    		}
    		var pp = this.patterns[ig];
    		var svgpolygon = pp.nodes[pp.nodes.length-1].c[0]+', '+pp.nodes[pp.nodes.length-1].c[1];
    		for ( var i = 0 ; i < pp.nodes.length ; i++ )
    			svgpolygon +=  " "+pp.nodes[i].c[0]+', '+pp.nodes[i].c[1];

    			var svg2 = document.createElementNS("http://www.w3.org/2000/svg",'polygon');
    			svg2.setAttribute('points', svgpolygon);
    			svg2.setAttribute('class', 'nodeshape' );
    			g.appendChild(svg2);

    		renderplane.appendChild(g);

    	}
    }
    /** @description

    	check collision between pattern and stuff already on the page
    */
    Page.prototype.collisionTest = function ( pattern )
    {
    	var x = pattern.position.x;
    	var y = pattern.position.y;
    	var w = pattern.width;
    	var h = pattern.height;

    	for ( var i = 0 ; i < this.patterns.length ; i++ )
    	{
    		var x1 = this.patterns[i].position.x;
    		var y1 = this.patterns[i].position.y;
    		var w1 = this.patterns[i].width;
    		var h1 = this.patterns[i].height;
    		//fl(x+' '+y+' | '+w+' '+h+' - '+x1+' '+y1+' | '+w1+' '+h1)

    		var cx = false;
    		var cy = false;
    		if ( x < ( x1 + w1 ) && ( x + w ) > x1 ) cx = true;
    		if ( y < ( y1 + h1 ) && ( y + h ) > y1 ) cy = true;

    		if ( cx && cy ) return true;
    		if (x < 5 | y < 5 ) return true;
    	}
    	return false;
    }
    /** @description

    	add pattern to the page
    */

    Page.prototype.addPattern = function ( pattern )
    {/** @description

    	add pattern to the page
    */

    	pattern.position.x = this.width - pattern.width;
    	pattern.position.y = this.height - pattern.height;

    	var c = this.collisionTest ( pattern );

    	var add = false;
    	while ( c == false )
    	{
    		add = true;

    			pattern.position.x -= 0;
    			pattern.position.y -= 10;

    			c = this.collisionTest ( pattern );
    			if (c)
    				pattern.position.y += 10;

    	}
    			c = this.collisionTest ( pattern );
    	while ( c == false )
    	{
    		add = true;

    			pattern.position.x -= 10;

    			c = this.collisionTest ( pattern );
    			if (c)
    				pattern.position.x += 10;

    	}
    		if ( add == true )
    		{
    			this.patterns.push(pattern);
    			return 0;
    		}
    	return -1;
    }

    /** @constructor */

    function Dispatcher (patterns)
    {
    	this.pages = [];
    	this.p = patterns;
    }
    /** @description

    	indicate the number of specified page size
    */

    Dispatcher.prototype.nSize = function ( size )
    {
    	var tmp = 0;
    	for ( var i = 0 ; i < this.pages.length ; i++ )
    	{
    		if ( this.pages[i].size == size ) tmp++;
    	}
    	return tmp;
    }
    /** @description

    	update patternn ref
    */
    Dispatcher.prototype.updatePatterns = function ( patterns )
    {
    	this.p = patterns;
    }
    /** @description

    	-
    */
    Dispatcher.prototype.queryPages = function ( size )
    {
    	//TODO return bigger sized
    	var tmp = [];
    	for ( var i = 0 ; i < this.pages.length ; i++ )
    	{
    		var nn = size.replace(/A/i, '');

    		var tn = this.pages[i].size.replace(/A/i, '');
    		if ( nn >= tn ) tmp.push( this.pages[i] );
    	}
    	return tmp;
    }
    /** @description

    	-
    */

    Dispatcher.prototype.outPageTriangle = function ( tid, container )
    {
    	for ( var i = 0 ; i < this.pages.length ; i++ )
    		for ( var j = 0 ; j < this.pages[i].patterns.length ; j++ )
    			for ( var k = 0 ; k < this.pages[i].patterns[j].triangles.length ; k++ )
    			if ( this.pages[i].patterns[j].triangles[k] == tid )
    				this.pages[i].out(container, this.pages[i].patterns[j].guid )

    }
    /** @description

    	-
    */
    Dispatcher.prototype.getPagepattern = function ( pid )
    {
    	for ( var i = 0 ; i < this.pages.length ; i++ )
    		for ( var j = 0 ; j < this.pages[i].patterns.length ; j++ )
    			if ( this.pages[i].patterns[j].guid == patterns.children[pid].guid )
    				return  this.pages[i];

    }
    /** @description

    	-
    */
    Dispatcher.prototype.outPattern = function ( tid, container )
    {
    	var idx = patterns.findTriangleOwner (tid);
    	if ( idx > -1 )
    	{
    	var pat =  patterns.children[idx];

    		$('#svg7').attr('viewBox', '0 0 '+(pat.width+10)+' '+(pat.height+10));

    	renderplane.innerHTML = "";

    		var g = document.createElementNS("http://www.w3.org/2000/svg",'g');
    		g.setAttribute( 'id', pat.guid );
    		g.setAttribute( 'transform', 'translate(0, 0)' );

    		for ( var i = 0 ; i < pat.triangles.length ; i++ )
    		{

    			var tmptri = pat.trianglesflatcoord[i];
    			var svgtrigon =  tmptri[0].c[0]+', '+tmptri[0].c[1]+
    							 ' '+tmptri[1].c[0]+', '+tmptri[1].c[1]+
    							 ' '+tmptri[2].c[0]+', '+tmptri[2].c[1];
    			var svg = document.createElementNS("http://www.w3.org/2000/svg",'polygon');
    			svg.setAttribute('points', svgtrigon);
    			//svg.setAttribute('class', 'flatshape' );
    			if ( pat.triangles[i] == tid )
    				svg.setAttribute('style', 'fill: #f94; stroke: #aaa;' );
    			else
    				svg.setAttribute('style', 'fill: #ddd; stroke: #aaa;' );
    			g.appendChild(svg);
    		}
    		var pp = pat;
    		var svgpolygon = pp.nodes[pp.nodes.length-1].c[0]+', '+pp.nodes[pp.nodes.length-1].c[1];
    		for ( var i = 0 ; i < pp.nodes.length ; i++ )
    			svgpolygon +=  " "+pp.nodes[i].c[0]+', '+pp.nodes[i].c[1];

    			var svg2 = document.createElementNS("http://www.w3.org/2000/svg",'polygon');
    			svg2.setAttribute('points', svgpolygon);
    		//	svg2.setAttribute('class', 'nodeshape' );
    			svg2.setAttribute('style', 'fill: #ccc0; stroke: #000; stroke-width: 2px;' );
    			g.appendChild(svg2);

    		renderplane.appendChild(g);

    	}

    }
    /** @description

    	-
    */

    Dispatcher.prototype.fullDispatch = function (  )
    {

    	//console.clear();
    	this.pages.splice( 0, this.pages.length );
    	 for ( var i = 0 ; i < this.p.children.length ; i++ )
    	 {
    		 dispatcher.dispatch(this.p.children[i]);
    	 }
    	// fl(this.pages);
    }
    /** @description

    	-
    */
    Dispatcher.prototype.dispatch = function ( p )
    {

    		var pgs = this.queryPages (p.papersizereq.s);

    		if ( pgs.length < 1 )
    		{
    			this.pages.push( new Page (p) );
    			return 0;
    		}
    		pgs = this.queryPages (p.papersizereq.s);

    		for ( var i = 0 ; i < pgs.length ; i++ )
    			if ( pgs[i].addPattern (p) != -1 )
    			{
    				return 0;
    			}
    		p.position.x = 0;
    		p.position.y = 0;
    		this.pages.push( new Page (p) );
    		return 0;
    }
    /** @description

    	-
    */
    Dispatcher.prototype.sortChildren = function ()
    {
    	var a = 0;
    	return a;
    }
    /** @constructor */

    function Node (sid, tid, coordinate )
    {
    	this.sid = sid;
    	this.tid = [tid];

    	this.c = coordinate;
    	this.guid = uuidv4();
    }
    /** @description
    	Indicates if the node and the one passed in parameter are located on the same
    	flat triangle.

    	@param {object} nde
    	@returns {boolean=} true If they are.
    	@returns {boolean=} false If they are not.
     */

    Node.prototype.shareFlatTriangleWith = function (nde)
    {
    	for ( var i = 0 ; i < nde.tid.length ; i++ )
    	{
    		for( var j = 0 ; j < this.tid.length ; j++ )
    			if (this.tid[j] == nde.tid[i] ) return true;
    	}
    	return false;
    }
    /** @constructor */
    function Pattern (targetmesh)
    {
    	this.triangles = [];
    	this.trianglesflatcoord = [];
    	this.edges = [];
    	this.nodes = [];
    	this.frontier = [];
    	this.guid = uuidv4();
    	this.targetMesh = targetmesh;
    	this.height = 0;
    	this.width = 0;
    	this.position = { x:0, y:0 };

    }
    /** @constructor
     * @param {object}                   targetmesh - The mesh to flatten.
     * @property {Array.Pattern}           children - The array tht contain patterns.
     * @property {object}                targetMesh - The mesh to flatten.
     * @property {string}             		    guid - A global identifier.

     */
    function Patterns (targetmesh)
    {
    	this.targetMesh = targetmesh;
    	this.children = [];
    	// GUID might always be useless ... or not
    	this.guid = uuidv4();
    }
    /** @description
    	Update the targeted mesh. That is needed when user load a new mesh.

    	 @param {object} targetmesh - the new mesh to flatten.

     */

    Patterns.prototype.updateTargetMesh = function (targetmesh)
    {
    	this.targetMesh = targetmesh;
    	this.guid = uuidv4();
    }
    /**
     * @description
     *	simply add a new pattern
     *
     *	@param {object} pattern - the pattern to add
     *
     */
    Patterns.prototype.addPattern = function (pattern)
    {
    	this.children.push(pattern);
    }

    Patterns.prototype.flFreezed = function ()
    {
    	var tmp = '\nfreezed :';
    	for ( var i = 0 ; i < this.targetMesh.edges.length ; i++ )
    		if (edgestate(this.targetMesh, i) == "freeze" ) tmp+=' '+i;
    		fl(tmp);

    }

    /*
     * @description
     *	try to rebuild patterns.
     * this method build pattern using freezd edges

       step by step, it try to dispatch freezed egdes into patterns by looking if
       they are joined.
       When it cannot, it creat a new pattern.

       a temporary var is used, so, if sometings go wrong, it leave the pattern as they were

     *	@return {boolean=} true  If it succesed to dispatch every edges
     *	@return {boolean=} false  If something goes wrong.
     *
     */
    Patterns.prototype.rebuild = function (feid)
    {
    	$('#main-app-dialog-title').text("Rebuild patterns");
    	// back up patterns in case of fail
    	var pbu = $.extend(true, [], this.children);

    	// we need to create and fill freezed edges list
    	var freezedlist = [];
    /*/
    	if ( feid != undefined )
    	{
    		fl('feid : '+feid);

    		var tpp = this.getFrontierOwnerChildren(feid);
    		fl(tpp);
    		if (tpp.length = 1 )
    		{
    		for ( var i = 0 ; i < tpp[0].edges.length ; i++ )
    			freezedlist.push (tpp[0].edges[i]);

    		}
    		if (tpp.length = 2 )
    		{

    		}

    	}
    	else
    	{
    		// first of all, we erase patterns
    		this.children.splice (0, this.children.length);
    		for ( var i = 0 ; i < this.targetMesh.edges.length ; i++ )
    			if (edgestate(this.targetMesh, i) == "freeze" ) freezedlist.push(i);

    	}

    	/*/

    			// first of all, we erase patterns
    		this.children.splice (0, this.children.length);
    		for ( var i = 0 ; i < this.targetMesh.edges.length ; i++ )
    			if (edgestate(this.targetMesh, i) == "freeze" ) freezedlist.push(i);

    	/**/

    	// Let's now dispatch those freezed edges into different patterns

    	while ( freezedlist.length > 0 )
    	{
    		var add = -1;
    		var i = 0 ;
    		while ( add == -1 && i < freezedlist.length )
    		{
    			var j = 0;
    			while ( add == -1 && j < this.children.length )
    			{
    				add = this.children[j].addEdge (freezedlist[i] );
    				if ( add != -1 )
    					freezedlist.splice(i, 1);
    				j++;
    			}
    			i++;
    		}
    		if ( add == -1 )
    		{
    			var tmp = new Pattern (this.targetMesh);
    			tmp.addEdge(freezedlist[0]);
    			tmp.gentriangles();
    			this.addPattern(tmp);
    			freezedlist.splice(0, 1);
    		}
    	}

    	// precedent code produce patterns defs from freezed edges, by this way,
    	// it cannot add single triangle patterns, the fallowing for loop process
    	// those single triangles pattern

    	for (var i = 0 ; i < this.targetMesh.triangles.length ; i++ )
    		if ( this.findTriangleOwner(i) == -1 && shapestate(this.targetMesh, i) == "solid" )
    		{
    			var tmp = new Pattern (this.targetMesh);
    			tmp.addTriangle(i);
    			tmp.genFrontiers ();
    			this.addPattern(tmp);
    		}
    	// show some help to the user if no pattern has been created yet

    	//fl('dispatch freezed edge ok');
    //	fl(this.children);
     	for ( var i = 0 ; i < this.children.length ; i++ )
       {
    	   if (!this.children[i].flatten())
    	   {

    		   this.children = $.extend(true, [], pbu);

    		   $('#processing-indicator').fadeOut(200);
    			$('#processing-fail-indicator').fadeIn( 1 ).delay( 3600 ).fadeOut( 400 );
    			fl('############### rebuil error ##################')
    		   return false;
    	   }
       }
    	//fl('flattening ok');
    	// Let's finally blank and refill the final document with our new computed
    	// patterns
       dispatcher.updatePatterns (this);
    	dispatcher.fullDispatch();

    	//fl('dispatch patterns ok');

     	//$('#main-app-dialog-info').text('no pattern');

       /*
    	for ( var i = 0 ; i < this.children.length ; i++ )
    	{
    		this.children[i].addToFinalDocument(renderplane);
    	}*/
    //	fl(this.children);
    	$('#processing-indicator').fadeOut(200);
    	$('#processing-success-indicator').fadeIn( 100 ).delay( 200 ).fadeOut( 400 );

    	return true;
    }
    Patterns.prototype.findTriangleOwner = function (triangle)
    {
    	for (var i = 0 ; i < this.children.length ; i++ )
    		for ( var j = 0 ; j < this.children[i].triangles.length ; j ++ )
    			if ( this.children[i].triangles[j] == triangle )
    				return i;
    	return -1;
    }
    Patterns.prototype.getFrontierOwnerChildren = function (feid)
    {
    	var tmp = [];
    	for (var i = 0 ; i < this.children.length ; i++ )
    		for ( var j = 0 ; j < this.children[i].frontier.length ; j ++ )
    			if ( this.children[i].frontier[j] == feid )
    				tmp.push( this.children[i] );
    	return tmp;
    }

    Pattern.prototype.tryToReachRatio = function ( r )
    {
    	var tmp = this;

    	var diff = 1000;
    	var trystep =$.extend(true, {}, tmp);

    }
    /** @description
      Search for a triangle by it's id into pattern
      @param {number} t - The id of the triangle to look for
      @returns {number} The index of the triangle into member
      triangles[] or -1 if the pattern doesn' own the triangle
      */
    Pattern.prototype.owntriangle = function (t)
    {
    	for( var i = 0 ; i < this.triangles.length ; i++ )
    		if ( this.triangles[i] == t ) return i;
    	return -1;
    }
    /** @description
    	compute the frontier's nodes of the pattern, using the pattern's flattened triangles summits
    	And order them to corectly define a shape frontier
     */
    Pattern.prototype.genNodes = function ()
    {	//console.clear();
    	var tmp = [];
    	for( var i = 0 ; i < this.trianglesflatcoord.length ; i++ )
    	{
    		for (var j = 0 ; j < this.trianglesflatcoord[i].length ; j++)
    		{
    			var duplicate = false;
    			for ( var k = 0 ; k < tmp.length ; k++ )
    			{
    				if ( distance (tmp[k].c, this.trianglesflatcoord[i][j].c ) < tolerance &&
    				 	  tmp[k].sid == this.trianglesflatcoord[i][j].sid )
    				{
    					duplicate = true;
    					tmp[k].tid.push(this.triangles[i]);
    				}
    			}
    			if ( duplicate == false )
    				tmp.push(new Node ( this.trianglesflatcoord[i][j].sid, this.triangles[i],
    					                 [ this.trianglesflatcoord[i][j].c[0],
    					                   this.trianglesflatcoord[i][j].c[1],
    					                   this.trianglesflatcoord[i][j].c[2] ] ) );

    		}
    	}

    	var cnt =0;

    	for ( var k = 0 ; k < tmp.length-1 ; k++ )
    	{
    		var isok = false;

    		if ( tmp[k].shareFlatTriangleWith( tmp[k+1] ) )
    		{

    			// good start, nodes are on the same flattened triangle

    			// are they shariing a freezed edge ?

    			var frz = false;
    			var edg = getEdgeId (this.targetMesh, tmp[k+1].sid, tmp[k].sid);
    			if ( edg > -1 )
    				if ( edgestate (this.targetMesh, edg) == "freeze" ) frz = true;

    			if ( frz == false )
    			{
    				isok = true;
    			}
    		}
    		if (isok == false )
    		{
    			var tmp2 = $.extend(true, {}, tmp[k+1]);
    			tmp.splice(k+1, 1);
    			tmp.push (tmp2);
    			k--;
    		}
    	}
    	this.nodes = tmp;

    }
    /** @description
    	Find the pattern triangles using pattern edges
     */
    Pattern.prototype.gentriangles = function ()
    {

    	for( var i = 0 ; i < this.edges.length ; i++ )
    		for( var j = 0 ; j < this.targetMesh.edges[this.edges[i]].tri.length ; j++ )
    			if ( this.owntriangle ( this.targetMesh.edges[this.edges[i]].tri[j] ) == -1 )
    			{
    				this.addTriangle(this.targetMesh.edges[this.edges[i]].tri[j]);
    				setshapestate(this.targetMesh, this.targetMesh.edges[this.edges[i]].tri[j], "solid");
    			}
    	this.genFrontiers();
    }
    /**
      Add triangle id to the member triangles[]
      @param {number} t - The id of the triangle to add
      @returns {number} 0 if succesful, -1 if triangle is already owned
      */
    Pattern.prototype.addTriangle = function (t)
    {
    	for ( var i = 0 ; i < this.triangles.length ; i++ )
    	{
    		if ( this.triangles[i] == t ) return -1;
    	}
    	this.triangles.push(t);
    	return 0;
    }
    /** @description
    	Try to ad an edges to the pattern, by looking for a triangle that is joined to it.
    	@param {number} edge - the id of the edge to add.
    	@return {number=} -2 when egde is already owned by the pattern
    	@return {number=} -1 when was not able to add the edge
     */
    Pattern.prototype.addEdge = function (edge)
    {

    	if ( this.triangles.length == 0 && this.edges.length == 0)
    	{
    		this.edges.push (edge);
    		return 0;
    	}
    	var t1 = this.targetMesh.edges[edge].tri[0];
    	var t2 = this.targetMesh.edges[edge].tri[1];
    	//l(pattern);
    	for ( var i = 0 ; i < this.edges.length ; i++ )
    		if ( this.edges[i] == edge )	return -2;
    	for ( var i = 0 ; i < this.triangles.length ; i++ )
    	{
    		if (t1 == this.triangles[i] | t2 == this.triangles[i] )
    		{
    			this.edges.push(edge);
    			this.gentriangles ();
    			return 1;
    		}
    	}
    	return -1;
    }
    /** @description
    	indicate the index of the specified triangle into menber triangles[]
    	@param {number} tid - the id of the wanted triangle
    	@returns {number} the index of the wanted triangle in member "triangles[]"
    	-1 if cannot be founded
     */
    Pattern.prototype.getTriangleIndex = function (tid)
    {
    	for ( var i = 0 ; i < this.triangles.length ; i++ )
    		if ( tid == this.triangles[i] ) return i;
    	return -1;
    }
    /** @description
    	Update some stats
     */
    Pattern.prototype.smartPositioning = function ()
    {
    	//TODO
    	var nNod, nTri, nEdg, nFro, maxX, minX, maxY, minY, w, h;
    	var n = this.nodes;
    //	fl(n);
    	maxX = -1000;
    	minX = 1000;
    	maxY = -1000;
    	minY = 1000;
    	for ( var i = 0 ; i < n.length ; i++ )
    	{
    		if ( maxX < n[i].c[0] ) maxX = n[i].c[0];
    		if ( minX > n[i].c[0] ) minX = n[i].c[0];
    		if ( maxY < n[i].c[1] ) maxY = n[i].c[1];
    		if ( minY > n[i].c[1] ) minY = n[i].c[1];
    	}

    	this.translate ((0 - minX), (0 - minY));
    	maxX = -1000;
    	minX = 1000;
    	maxY = -1000;
    	minY = 1000;
    	for ( var i = 0 ; i < n.length ; i++ )
    	{
    		if ( maxX < n[i].c[0] ) maxX = n[i].c[0];
    		if ( minX > n[i].c[0] ) minX = n[i].c[0];
    		if ( maxY < n[i].c[1] ) maxY = n[i].c[1];
    		if ( minY > n[i].c[1] ) minY = n[i].c[1];
    	}
    	h = maxY - minY;
    	w = maxX - minX;

    //	fl('range : x['+minX.toFixed(2)+' - '+maxX.toFixed(2)+']\n y['+minY.toFixed(2)+' - '+maxY.toFixed(2)+'] ');
    //	fl('size (w, h) : '+w+', '+h);

    	this.height = h;
    	this.width = w;
    //	fl('rr: '+rr);
    	var a4 = 'A4';
    	var a3 = 'A3';
    	var a2 = 'A2';
    	var a1 = 'A1';
    	var a0 = 'A0';

    	//fl('\n # requierements : '+this.papersizereq.s+' '+this.papersizereq.w+' x '+this.papersizereq.h);
    	//this.rotate(11);
    	//TODO smart rotate

    	var _this = $.extend( true, {}, this);
    	var r = w/h;
    	var scrs = [];
    	var score = r;

    	var inc = 1;
    	var range = 360;
    	for ( var a = 0 ; a < range ; a++ )
    	{
    		this.rotate (inc);

    	maxX = -1000;
    	minX = 1000;
    	maxY = -1000;
    	minY = 1000;
    		for ( var i = 0 ; i < n.length ; i++ )
    		{
    			if ( maxX < n[i].c[0] ) maxX = n[i].c[0];
    			if ( minX > n[i].c[0] ) minX = n[i].c[0];
    			if ( maxY < n[i].c[1] ) maxY = n[i].c[1];
    			if ( minY > n[i].c[1] ) minY = n[i].c[1];
    		}
    		h = maxY - minY;
    		w = maxX - minX;

    		r = w/h;
    		scrs.push(r);
    	}
    	var sc = 10000;
    	var iscr = -1;
    	for (var i = 0 ; i < scrs.length ; i++ )
    	{
    		if ( scrs[i] < sc )
    		{
    			iscr = i;
    			sc =scrs[i];
    		}

    	}

    	var srot = (iscr+1) * inc;
    	this.rotate (srot);

    	maxX = -1000;
    	minX = 1000;
    	maxY = -1000;
    	minY = 1000;
    	for ( var i = 0 ; i < n.length ; i++ )
    	{
    		if ( maxX < n[i].c[0] ) maxX = n[i].c[0];
    		if ( minX > n[i].c[0] ) minX = n[i].c[0];
    		if ( maxY < n[i].c[1] ) maxY = n[i].c[1];
    		if ( minY > n[i].c[1] ) minY = n[i].c[1];
    	}
    	h = maxY - minY;
    	w = maxX - minX;

    	this.height = h;
    	this.width = w;
    	this.translate ((0 - minX), (0 - minY));
    		var rr = w / h;
    	if ( rr > 0.7070 )
    	{
    		this.papersizereq = { s: a4, w : 210, h : 297};
    		if ( w > 210) this.papersizereq = {s: a3, w : 297, h : 420};
    		if ( w > 297) this.papersizereq = {s: a2, w : 420, h : 594};
    		if ( w > 420) this.papersizereq = {s: a1, w : 594, h : 840};
    		if ( w > 594) this.papersizereq = {s: a0, w : 840, h :1188 };
    	}
    	else
    	{
    		this.papersizereq = {s: a4, w : 210, h : 297};
    		if ( h > 297) this.papersizereq = {s: a3, w : 297, h : 420};
    		if ( h > 420) this.papersizereq = {s: a2, w : 420, h : 594};
    		if ( h > 594) this.papersizereq = {s: a1, w : 594, h : 840};
    		if ( h > 840) this.papersizereq = {s: a0, w : 840, h :1188 };
    	}

    }
    Pattern.prototype.translate = function (x, y)
    {
    	var tmptxymat = gentmat( x, y, 0.0 );
    	for ( var i = 0 ; i < this.nodes.length ; i++ )
    		this.nodes[i].c = applymat(tmptxymat, this.nodes[i].c);
    	for ( var i = 0 ; i < this.trianglesflatcoord.length ; i++ )
    		for ( var j = 0 ; j < this.trianglesflatcoord[i].length ; j++)
    			this.trianglesflatcoord[i][j].c = applymat(tmptxymat, this.trianglesflatcoord[i][j].c);

    }
    Pattern.prototype.rotate = function (rz)
    {
    	var tmprzmat = genrmat(0.0, 0.0, rz );
    	for ( var i = 0 ; i < this.nodes.length ; i++ )
    		this.nodes[i].c = applymat(tmprzmat, this.nodes[i].c);
    	for ( var i = 0 ; i < this.trianglesflatcoord.length ; i++ )
    		for ( var j = 0 ; j < this.trianglesflatcoord[i].length ; j++)
    			this.trianglesflatcoord[i][j].c = applymat(tmprzmat, this.trianglesflatcoord[i][j].c);

    }

    Pattern.prototype.ownFrontier = function (f)
    {
    	for( var i = 0 ; i < this.frontier.length ; i++ )
    		if ( this.frontier[i] == f ) return i;
    	return -1;
    }

    /** @description
    	find edges id witch are frontier of the pattern.
     */
    Pattern.prototype.genFrontiers = function () // find fronier from junctions && triangles lists
    {

    	l('## PATTERNgenfrontier ##');
    	for( var i = 0 ; i < this.triangles.length ; i++ )
    	{
    		var tmp = TRIANGLEgetedges (this.targetMesh, this.triangles[i]);
    		for ( var j = 0 ; j < tmp.length ; j++ )
    			if ( ( this.ownFrontier( tmp[j]) == -1 ) && ( edgestate(this.targetMesh, tmp[j]) != "freeze" ) )
    			{
    				this.frontier.push (tmp[j]);
    				setedgestate (this.targetMesh, tmp[j], "visible");
    			}
    	}
    }

    /** @description
    	symply call flattenTrianglesCoord (), assembleFlattenedTriangles ()
    	and genNodes (). In this order.
     */
    Pattern.prototype.flatten = function ()
    {
    	//fl(' # flatten triangle '+this.guid);
    	this.flattenTrianglesCoord ();
    	//fl(' # asm triangle');
    	this.assembleFlattenedTriangles ();
    	//fl(' # check freezed edges');
    	if ( ! this.checkFreezedEdges() ) return false;
    	//fl(' # gen nodes');
    	this.genNodes ();
    	//fl(' # smart positioning');
    	this.smartPositioning();
    	//dispatcher.updateChildren();
    	return true;
    }
    /** @description
    	 calculate the flat coord of each triangle.
    	 mistake was done earlyer, pattern triangles ids are store in an array of array
        instead of an array of (js) objects
     	 dont wand to rewrite this now, so flat coordinates will be store in a separate array
     	 of array - SO - P.triangles[x] & P.trianlesflatcoord[x] refer to the same triangle
     	 and will always be.
     	 the other reason why is that full OOP rewrite is planed
     */
    Pattern.prototype.flattenTrianglesCoord = function ()
    {
    	for ( var j = 0 ; j < this.triangles.length ; j++ )
    	{
    		var id = this.triangles[j];
    		var n = this.targetMesh.trianglesnorm[id];

    		var target = new Vector ([0.0, 0.0, 0.0], [0.0, 0.0, 1.0], 1.0);
    		var bullet = new Vector ([0.0, 0.0, 0.0], n, 1.0);
    		// La,matrice de transformation peut etre construite
    		var itpmat = geninterpmat (bullet, target);

    		var w = $.extend( true, {}, this.targetMesh);

    		for ( var i = 0 ; i < w.nv ; i++ )
    			w.vertices[i] = applymatNscale(itpmat, w.vertices[i]);

    		var tmptri = [ {c : [(w.vertices[w.triangles[id][0]][0]),
    							 (w.vertices[w.triangles[id][0]][1]), 0], sid : this.targetMesh.triangles[id][0] },

    							{c : [(w.vertices[w.triangles[id][1]][0]),
    							 (w.vertices[w.triangles[id][1]][1]), 0], sid : this.targetMesh.triangles[id][1] },

    						   {c : [(w.vertices[w.triangles[id][2]][0]),
    							 (w.vertices[w.triangles[id][2]][1]), 0], sid : this.targetMesh.triangles[id][2] }];

    		this.trianglesflatcoord.push(tmptri);
    	}
    }
    /** @description

    	Assemble the pattern, must not be called until the triangles have been flattened
    */
    Pattern.prototype.assembleFlattenedTriangles = function ()
    {

    	var done = new processedelements();
    	done.add ( this.triangles[0] );
    	var k;
    	var mesh = this.targetMesh;
    	for ( var j = 0 ; j < this.edges.length ; j++ )
    	{
    		// pattern cannot own single triangle edges, so we're safe with
    		// fallowing code
    		var vt1s, vt1e, vt2s, vt2e;
    		var vs = mesh.edges[this.edges[j]].som[0];
    		var ve = mesh.edges[this.edges[j]].som[1];

    		for ( var m = 0 ; m < mesh.triangles[mesh.edges[this.edges[j]].tri[0] ].length ; m++)
    		{
    			if ( mesh.triangles[mesh.edges[this.edges[j]].tri[0] ][m] == vs )
    				vt1s = m;
    			if ( mesh.triangles[mesh.edges[this.edges[j]].tri[0] ][m] == ve )
    				vt1e = m;
    		}
    		for ( var m = 0 ; m < mesh.triangles[mesh.edges[this.edges[j]].tri[1] ].length ; m++)
    		{
    			if ( mesh.triangles[mesh.edges[this.edges[j]].tri[1] ][m] == vs )
    				vt2s = m;
    			if ( mesh.triangles[mesh.edges[this.edges[j]].tri[1] ][m] == ve )
    				vt2e = m;
    		}
    		var t = this.getTriangleIndex ( mesh.edges[this.edges[j]].tri[0] );
    		var t2 = this.getTriangleIndex( mesh.edges[this.edges[j]].tri[1] );

    		var target = vectfromvertices (this.trianglesflatcoord[t][vt1s].c,
    												 this.trianglesflatcoord[t][vt1e].c);

    		var bullet = vectfromvertices (this.trianglesflatcoord[t2][vt2s].c,
    												 this.trianglesflatcoord[t2][vt2e].c);

    		if ( done.is ( mesh.edges[this.edges[j]].tri[0] ) )
    		{
    			var itpmat = geninterpmat (bullet, target);
    		   var pp = $.extend( true, [], this.trianglesflatcoord[t2]);
    			for ( var ii = 0 ; ii < 3 ; ii++ )
    				this.trianglesflatcoord[t2][ii].c = applymat(itpmat, pp[ii].c);
    			k = 1;
    		}
    		else
    		{
    			var itpmat = geninterpmat (target, bullet);
    			var pp = $.extend(true, [], this.trianglesflatcoord[t]);
    			for ( var ii = 0 ; ii < 3 ; ii++ )
    				this.trianglesflatcoord[t][ii].c = applymat(itpmat, pp[ii].c);
    			k = 0;
    		}
    		done.add ( mesh.edges[ this.edges[j] ].tri[k] );
    	}
    }
    /** @description

    	returns the flattened triangle object that matches the triangle of the mesh
    	of which the id is Mtid

    	@param {number} mtid - mesh triangle's id
    	@returns {object} flattened triangle

     */
    Pattern.prototype.getFlatTriangle = function (mtid)
    {

    	for (var i = 0 ; i < this.triangles.length ; i++)
    		if (mtid == this.triangles[i] ) return this.trianglesflatcoord[i];
    	return null;
    }

    Pattern.prototype.checkFreezedEdges = function ()
    {
    	var ww = 0;
    	for ( var i = 0 ; i < this.edges.length ; i++ )
    	{
    		var joined = this.areFlatTriangleJoined (  this.edges[i],
    		                                     this.getFlatTriangle(this.targetMesh.edges[this.edges[i]].tri[0]),
    		                                     this.getFlatTriangle(this.targetMesh.edges[this.edges[i]].tri[1]) );
    		if (!joined) ww++;
    	}
    	if (ww == 0 ) return true;
    	else return false;
    }/**
    	test if two triangles are joined, by checking the distance between shared summits.

    */

    Pattern.prototype.areFlatTriangleJoined = function (eid, ft1, ft2 )
    {
    	// more test must be needed but, right now, this method only hasnone caller

    	var joined = true;
    	for ( var i = 0 ; i < ft1.length ; i++ )
    	for ( var j = 0 ; j < ft2.length ; j++ )
    		if ( ft1[i].sid == ft2[j].sid )
    		{
    			if ( distance ( ft1[i].c, ft2[j].c) > tolerance ) joined = false;
    		}
    	return joined;

    }