#! /usr/bin/env python
'''
Copyright (C) 2011 Mark Schafer <neon.mark (a) gmail dot com>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
'''

# Build a Jigsaw puzzle for Lasercutting.
# User defines:
# - dimensions,
# - number of pieces in X and Y,
# - notch size,
# - random amount of perturbation for uniqueness,
# - border and rounding for border and inner corners
# - random or random seed for repeats

### 0.1 make basic jigsaw for lasercut - March 2011
### 0.2 add random seed so repeatable, add pieces for manual booleans - May 2011
### 0.3 add some no-knob edges - June 2019

### Todo
# add option to cut pieces:
#    - taking two rows(cols) at a time - reverse the second one and concat on end - add z to close
#    - taking a row and a col - do intersect = piece.

__version__ = "0.3"

import inkex, simplestyle, simpletransform, cubicsuperpath
from simplepath import *
import sys, math, random, copy

def randomize(x_y, radius, norm=True, absolute=False):
    """ return x,y moved by a random amount inside a radius.
        use uniform distribution unless
         - norm = True - then use a normal distribution
        If absolute is true - ensure random is only added to x,y  """
    # if norm:
        # r = abs(random.normalvariate(0.0,0.5*radius))
    # else:
        # r = random.uniform(0.0,radius)
    x, y = x_y
    a = random.uniform(0.0,2*math.pi)
    x += math.cos(a)*radius
    y += math.sin(a)*radius
    if absolute:
        x = abs(x)
        y = abs(y)
    return [x, y]


def add_rounded_rectangle(startx, starty, radius, width, height, style, name, parent, mask=False):
    line_path = [['M', [startx, starty+radius]]]
    if radius > 0.0: # rounded corners
        line_path.append(['c', [0, -radius/2, radius/2, -radius, radius, -radius]])
        if mask == "Below":
               line_path.append(['m', [width-2*radius, 0,]])
        else:
            line_path.append(['c', [radius/2, 0, width-2*radius-radius/2, 0, width-2*radius,0 ]]) # top line
        line_path.append(['c', [radius/2, 0, radius, radius/2, radius, radius]])
        line_path.append(['c', [0, radius/2, 0, height-2*radius-radius/2, 0, height-2*radius]]) # RHS line
        line_path.append(['c', [0, radius/2, -radius/2, radius, -radius, radius]])
        line_path.append(['c', [-radius/2,0, -width+2*radius+radius/2,0, -width+2*radius,0]]) # bottom line
        line_path.append(['c', [-radius/2, 0, -radius, -radius/2, -radius, -radius]])
        if mask == "Right":
            line_path.append(['m', [0, height]])
        else:
            line_path.append(['c', [0, -radius/2, 0, -height+2*radius+radius/2, 0, -height+2*radius]]) # LHS line
    else: # square corners
        if mask == "Below":
            line_path.append(['m', [width, 0]])
            line_path.append(['l', [0, height, -width, 0, 0, -height]])
        elif mask == "Right":
            line_path.append(['l', [width, 0, 0, height, -width, 0,]])
        else: # separate
            line_path.append(['l', [width, 0, 0, height, -width, 0, 0, -height]])
    #
    #sys.stderr.write("%s\n"% line_path)
    attribs = {'style':simplestyle.formatStyle(style),
                    inkex.addNS('label','inkscape'):name,
                    'd':formatPath(line_path)}
    #sys.stderr.write("%s\n"% attribs)
    inkex.etree.SubElement(parent, inkex.addNS('path','svg'), attribs )



###----------------------
### all for intersection  from http://www.kevlindev.com/gui/index.htm
"""polynomial constructor
    Polynomial.prototype.init = function(coefs) {
    this.coefs = new Array();

    for ( var i = coefs.length - 1; i >= 0; i-- )
        this.coefs.push( coefs[i] );

    this._variable = "t";
    this._s = 0;   """

def get_derivative(polynomial):
    """ Polynomial.prototype.getDerivative = function() {
    var derivative = new Polynomial();

    for ( var i = 1; i < this.coefs.length; i++ ) {
        derivative.coefs.push(i*this.coefs[i]);
    }

    return derivative;   """
    deriv = []
    for i in range(len(polynomial)):
        deriv.append(i* polynomial[i])
    return deriv


def getroots_in_interval():
    """ Polynomial.prototype.getRootsInInterval = function(min, max) {
    var roots = new Array();
    var root;

    if ( this.getDegree() == 1 ) {
        root = this.bisection(min, max);
        if ( root != null ) roots.push(root);
    } else {
        // get roots of derivative
        var deriv  = this.getDerivative();
        var droots = deriv.getRootsInInterval(min, max);

        if ( droots.length > 0 ) {
            // find root on [min, droots[0]]
            root = this.bisection(min, droots[0]);
            if ( root != null ) roots.push(root);

            // find root on [droots[i],droots[i+1]] for 0 <= i <= count-2
            for ( i = 0; i <= droots.length-2; i++ ) {
                root = this.bisection(droots[i], droots[i+1]);
                if ( root != null ) roots.push(root);
            }

            // find root on [droots[count-1],xmax]
            root = this.bisection(droots[droots.length-1], max);
            if ( root != null ) roots.push(root);
        } else {
            // polynomial is monotone on [min,max], has at most one root
            root = this.bisection(min, max);
            if ( root != null ) roots.push(root);
        }
    }

    return roots;   """
    pass


def bisection(polynomial, minimum, maximum):
    """ Polynomial.prototype.bisection = function(min, max) {
    var minValue = this.eval(min);
    var maxValue = this.eval(max);
    var result;
    
    if ( Math.abs(minValue) <= Polynomial.TOLERANCE )
        result = min;
    else if ( Math.abs(maxValue) <= Polynomial.TOLERANCE )
        result = max;
    else if ( minValue * maxValue <= 0 ) {
        var tmp1  = Math.log(max - min);
        var tmp2  = Math.LN10 * Polynomial.ACCURACY;
        var iters = Math.ceil( (tmp1+tmp2) / Math.LN2 );

        for ( var i = 0; i < iters; i++ ) {
            result = 0.5 * (min + max);
            var value = this.eval(result);

            if ( Math.abs(value) <= Polynomial.TOLERANCE ) {
                break;
            }

            if ( value * minValue < 0 ) {
                max = result;
                maxValue = value;
            } else {
                min = result;
                minValue = value;
            }
        }
    }

    return result;   """
    pass


##def intersect_bezier(a1, a2, a3, a4, b1, b2, b3, b4):
##    """
##        """
##    # Calculate the coefficients of cubic polynomial
##    #a = a1.multiply(-1);
##    a = [i*-1 for i in a1]
##    #b = a2.multiply(3);
##    b = [i*3 for i in a2]
##    #c = a3.multiply(-3);
##    c = [i*-3 for i in a3]
##    #d = a.add(b.add(c.add(a4)));
##    d = [a[i]+b[i]+c[i]+a4[i] for i in range(len(a))]
##    #c13 = new Vector2D(d.x, d.y);
##    c13 = copy.deepcopy(d)
##
##    #a = a1.multiply(3);
##    a = [i*3 for i in a1]
##    #b = a2.multiply(-6);
##    b = [i*-6 for i in a2]
##    #c = a3.multiply(3);
##    c = [i*3 for i in a3]
##    #d = a.add(b.add(c));
##    d = [a[i]+b[i]+c[i] for i in range(len(a))]
##    #c12 = new Vector2D(d.x, d.y);
##    c12 = copy.deepcopy(d)
##
##    #a = a1.multiply(-3);
##    a = [i*-3 for i in a1]
##    #b = a2.multiply(3);
##    b = [i*3 for i in a2]
##    #c = a.add(b);
##    c = [a[i]+b[i] for i in range(len(a))]
##    #c11 = new Vector2D(c.x, c.y);
##    c11 = copy.deepcopy(c)
##    #c10 = new Vector2D(a1.x, a1.y);
##    c10 = copy.deepcopy(a1)
##
##    #a = b1.multiply(-1);
##    a = [i*-1 for i in b1]
##    #b = b2.multiply(3);
##    b = [i*3 for i in b2]
##    #c = b3.multiply(-3);
##    c = [i*-3 for i in b3]
##    #d = a.add(b.add(c.add(b4)));
##    d = [a[i]+b[i]+c[i]+b4[i] for i in range(len(a))]
##    #c23 = new Vector2D(d.x, d.y);
##    c23 = copy.deepcopy(d)
##
##    #a = b1.multiply(3);
##    a = [i*3 for i in b1]
##    #b = b2.multiply(-6);
##    b = [i*-6 for i in b2]
##    #c = b3.multiply(3);
##    c = [i*3 for i in b3]
##    #d = a.add(b.add(c));
##    d = [b[i]+c[i] for i in range(len(b))]
##    #c22 = new Vector2D(d.x, d.y);
##    c22 = copy.deepcopy(d)
##
##    #a = b1.multiply(-3);
##    a = [i*-3 for i in b1]
##    #b = b2.multiply(3);
##    b = [i*3 for i in b2]
##    #c = a.add(b);
##    c = [a[i]+b[i] for i in range(len(a))]
##    #c21 = new Vector2D(c.x, c.y);
##    c21 = copy.deepcopy(c)
##    #c20 = new Vector2D(b1.x, b1.y);
##    c20 = copy.deepcopy(b)
##
##    c10x2 = c10[0]*c10[0]
##    c10x3 = c10[0]*c10[0]*c10[0]
##    c10y2 = c10[1]*c10[1]
##    c10y3 = c10.y*c10[1]*c10[1]
##    c11x2 = c11[0]*c11[0]
##    c11x3 = c11[0]*c11[0]*c11[0]
##    c11y2 = c11[1]*c11[1]
##    c11y3 = c11[1]*c11[1]*c11[1]
##    c12x2 = c12[0]*c12[0]
##    c12x3 = c12[0]*c12[0]*c12[0]
##    c12y2 = c12[1]*c12.[1]
##    c12y3 = c12[1]*c12[1]*c12[1]
##    c13x2 = c13[0]*c13[0]
##    c13x3 = c13[0]*c13[0]*c13[0]
##    c13y2 = c13[1]*c13[1]
##    c13y3 = c13[1]*c13[1]*c13[1]
##    c20x2 = c20[0]*c20[0]
##    c20x3 = c20[0]*c20[0]*c20[0]
##    c20y2 = c20[1]*c20[1]
##    c20y3 = c20[1]*c20[1]*c20[1]
##    c21x2 = c21[0]*c21[0]
##    c21x3 = c21[0]*c21[0]*c21[0]
##    c21y2 = c21[1]*c21[1]
##    c22x2 = c22[0]*c22[0]
##    c22x3 = c22[0]*c22[0]*c22[0]
##    c22y2 = c22[1]*c22[1]
##    c23x2 = c23[0]*c23[0]
##    c23x3 = c23[0]*c23[0]*c23[0]
##    c23y2 = c23[1]*c23[1]
##    c23y3 = c23[1]*c23[1]*c23[1]
##    # create degree 9 (10 coef) polynomial
##    poly = [-c13x3*c23y3 + c13y3*c23x3 - 3*c13.x*c13y2*c23x2*c23.y + 3*c13x2*c13.y*c23.x*c23y2,
##            -6*c13.x*c22.x*c13y2*c23.x*c23.y + 6*c13x2*c13.y*c22.y*c23.x*c23.y + 3*c22.x*c13y3*c23x2 -
##                3*c13x3*c22.y*c23y2 - 3*c13.x*c13y2*c22.y*c23x2 + 3*c13x2*c22.x*c13.y*c23y2,
##            -6*c21.x*c13.x*c13y2*c23.x*c23.y - 6*c13.x*c22.x*c13y2*c22.y*c23.x + 6*c13x2*c22.x*c13.y*c22.y*c23.y +
##                3*c21.x*c13y3*c23x2 + 3*c22x2*c13y3*c23.x + 3*c21.x*c13x2*c13.y*c23y2 - 3*c13.x*c21.y*c13y2*c23x2 -
##                3*c13.x*c22x2*c13y2*c23.y + c13x2*c13.y*c23.x*(6*c21.y*c23.y + 3*c22y2) + c13x3*(-c21.y*c23y2 -
##                2*c22y2*c23.y - c23.y*(2*c21.y*c23.y + c22y2)),
##            c11.x*c12.y*c13.x*c13.y*c23.x*c23.y - c11.y*c12.x*c13.x*c13.y*c23.x*c23.y + 6*c21.x*c22.x*c13y3*c23.x +
##                3*c11.x*c12.x*c13.x*c13.y*c23y2 + 6*c10.x*c13.x*c13y2*c23.x*c23.y - 3*c11.x*c12.x*c13y2*c23.x*c23.y -
##                3*c11.y*c12.y*c13.x*c13.y*c23x2 - 6*c10.y*c13x2*c13.y*c23.x*c23.y - 6*c20.x*c13.x*c13y2*c23.x*c23.y +
##                3*c11.y*c12.y*c13x2*c23.x*c23.y - 2*c12.x*c12y2*c13.x*c23.x*c23.y - 6*c21.x*c13.x*c22.x*c13y2*c23.y -
##                6*c21.x*c13.x*c13y2*c22.y*c23.x - 6*c13.x*c21.y*c22.x*c13y2*c23.x + 6*c21.x*c13x2*c13.y*c22.y*c23.y +
##                2*c12x2*c12.y*c13.y*c23.x*c23.y + c22x3*c13y3 - 3*c10.x*c13y3*c23x2 + 3*c10.y*c13x3*c23y2 +
##                3*c20.x*c13y3*c23x2 + c12y3*c13.x*c23x2 - c12x3*c13.y*c23y2 - 3*c10.x*c13x2*c13.y*c23y2 +
##                3*c10.y*c13.x*c13y2*c23x2 - 2*c11.x*c12.y*c13x2*c23y2 + c11.x*c12.y*c13y2*c23x2 - c11.y*c12.x*c13x2*c23y2 +
##                2*c11.y*c12.x*c13y2*c23x2 + 3*c20.x*c13x2*c13.y*c23y2 - c12.x*c12y2*c13.y*c23x2 -
##                3*c20.y*c13.x*c13y2*c23x2 + c12x2*c12.y*c13.x*c23y2 - 3*c13.x*c22x2*c13y2*c22.y +
##                c13x2*c13.y*c23.x*(6*c20.y*c23.y + 6*c21.y*c22.y) + c13x2*c22.x*c13.y*(6*c21.y*c23.y + 3*c22y2) +
##                c13x3*(-2*c21.y*c22.y*c23.y - c20.y*c23y2 - c22.y*(2*c21.y*c23.y + c22y2) - c23.y*(2*c20.y*c23.y + 2*c21.y*c22.y)),
##            6*c11.x*c12.x*c13.x*c13.y*c22.y*c23.y + c11.x*c12.y*c13.x*c22.x*c13.y*c23.y + c11.x*c12.y*c13.x*c13.y*c22.y*c23.x -
##                c11.y*c12.x*c13.x*c22.x*c13.y*c23.y - c11.y*c12.x*c13.x*c13.y*c22.y*c23.x - 6*c11.y*c12.y*c13.x*c22.x*c13.y*c23.x -
##                6*c10.x*c22.x*c13y3*c23.x + 6*c20.x*c22.x*c13y3*c23.x + 6*c10.y*c13x3*c22.y*c23.y + 2*c12y3*c13.x*c22.x*c23.x -
##                2*c12x3*c13.y*c22.y*c23.y + 6*c10.x*c13.x*c22.x*c13y2*c23.y + 6*c10.x*c13.x*c13y2*c22.y*c23.x +
##                6*c10.y*c13.x*c22.x*c13y2*c23.x - 3*c11.x*c12.x*c22.x*c13y2*c23.y - 3*c11.x*c12.x*c13y2*c22.y*c23.x +
##                2*c11.x*c12.y*c22.x*c13y2*c23.x + 4*c11.y*c12.x*c22.x*c13y2*c23.x - 6*c10.x*c13x2*c13.y*c22.y*c23.y -
##                6*c10.y*c13x2*c22.x*c13.y*c23.y - 6*c10.y*c13x2*c13.y*c22.y*c23.x - 4*c11.x*c12.y*c13x2*c22.y*c23.y -
##                6*c20.x*c13.x*c22.x*c13y2*c23.y - 6*c20.x*c13.x*c13y2*c22.y*c23.x - 2*c11.y*c12.x*c13x2*c22.y*c23.y +
##                3*c11.y*c12.y*c13x2*c22.x*c23.y + 3*c11.y*c12.y*c13x2*c22.y*c23.x - 2*c12.x*c12y2*c13.x*c22.x*c23.y -
##                2*c12.x*c12y2*c13.x*c22.y*c23.x - 2*c12.x*c12y2*c22.x*c13.y*c23.x - 6*c20.y*c13.x*c22.x*c13y2*c23.x -
##                6*c21.x*c13.x*c21.y*c13y2*c23.x - 6*c21.x*c13.x*c22.x*c13y2*c22.y + 6*c20.x*c13x2*c13.y*c22.y*c23.y +
##                2*c12x2*c12.y*c13.x*c22.y*c23.y + 2*c12x2*c12.y*c22.x*c13.y*c23.y + 2*c12x2*c12.y*c13.y*c22.y*c23.x +
##                3*c21.x*c22x2*c13y3 + 3*c21x2*c13y3*c23.x - 3*c13.x*c21.y*c22x2*c13y2 - 3*c21x2*c13.x*c13y2*c23.y +
##                c13x2*c22.x*c13.y*(6*c20.y*c23.y + 6*c21.y*c22.y) + c13x2*c13.y*c23.x*(6*c20.y*c22.y + 3*c21y2) +
##                c21.x*c13x2*c13.y*(6*c21.y*c23.y + 3*c22y2) + c13x3*(-2*c20.y*c22.y*c23.y - c23.y*(2*c20.y*c22.y + c21y2) -
##                c21.y*(2*c21.y*c23.y + c22y2) - c22.y*(2*c20.y*c23.y + 2*c21.y*c22.y)),
##            c11.x*c21.x*c12.y*c13.x*c13.y*c23.y + c11.x*c12.y*c13.x*c21.y*c13.y*c23.x + c11.x*c12.y*c13.x*c22.x*c13.y*c22.y -
##                c11.y*c12.x*c21.x*c13.x*c13.y*c23.y - c11.y*c12.x*c13.x*c21.y*c13.y*c23.x - c11.y*c12.x*c13.x*c22.x*c13.y*c22.y -
##                6*c11.y*c21.x*c12.y*c13.x*c13.y*c23.x - 6*c10.x*c21.x*c13y3*c23.x + 6*c20.x*c21.x*c13y3*c23.x +
##                2*c21.x*c12y3*c13.x*c23.x + 6*c10.x*c21.x*c13.x*c13y2*c23.y + 6*c10.x*c13.x*c21.y*c13y2*c23.x +
##                6*c10.x*c13.x*c22.x*c13y2*c22.y + 6*c10.y*c21.x*c13.x*c13y2*c23.x - 3*c11.x*c12.x*c21.x*c13y2*c23.y -
##                3*c11.x*c12.x*c21.y*c13y2*c23.x - 3*c11.x*c12.x*c22.x*c13y2*c22.y + 2*c11.x*c21.x*c12.y*c13y2*c23.x +
##                4*c11.y*c12.x*c21.x*c13y2*c23.x - 6*c10.y*c21.x*c13x2*c13.y*c23.y - 6*c10.y*c13x2*c21.y*c13.y*c23.x -
##                6*c10.y*c13x2*c22.x*c13.y*c22.y - 6*c20.x*c21.x*c13.x*c13y2*c23.y - 6*c20.x*c13.x*c21.y*c13y2*c23.x -
##                6*c20.x*c13.x*c22.x*c13y2*c22.y + 3*c11.y*c21.x*c12.y*c13x2*c23.y - 3*c11.y*c12.y*c13.x*c22x2*c13.y +
##                3*c11.y*c12.y*c13x2*c21.y*c23.x + 3*c11.y*c12.y*c13x2*c22.x*c22.y - 2*c12.x*c21.x*c12y2*c13.x*c23.y -
##                2*c12.x*c21.x*c12y2*c13.y*c23.x - 2*c12.x*c12y2*c13.x*c21.y*c23.x - 2*c12.x*c12y2*c13.x*c22.x*c22.y -
##                6*c20.y*c21.x*c13.x*c13y2*c23.x - 6*c21.x*c13.x*c21.y*c22.x*c13y2 + 6*c20.y*c13x2*c21.y*c13.y*c23.x +
##                2*c12x2*c21.x*c12.y*c13.y*c23.y + 2*c12x2*c12.y*c21.y*c13.y*c23.x + 2*c12x2*c12.y*c22.x*c13.y*c22.y -
##                3*c10.x*c22x2*c13y3 + 3*c20.x*c22x2*c13y3 + 3*c21x2*c22.x*c13y3 + c12y3*c13.x*c22x2 +
##                3*c10.y*c13.x*c22x2*c13y2 + c11.x*c12.y*c22x2*c13y2 + 2*c11.y*c12.x*c22x2*c13y2 -
##                c12.x*c12y2*c22x2*c13.y - 3*c20.y*c13.x*c22x2*c13y2 - 3*c21x2*c13.x*c13y2*c22.y +
##                c12x2*c12.y*c13.x*(2*c21.y*c23.y + c22y2) + c11.x*c12.x*c13.x*c13.y*(6*c21.y*c23.y + 3*c22y2) +
##                c21.x*c13x2*c13.y*(6*c20.y*c23.y + 6*c21.y*c22.y) + c12x3*c13.y*(-2*c21.y*c23.y - c22y2) +
##                c10.y*c13x3*(6*c21.y*c23.y + 3*c22y2) + c11.y*c12.x*c13x2*(-2*c21.y*c23.y - c22y2) +
##                c11.x*c12.y*c13x2*(-4*c21.y*c23.y - 2*c22y2) + c10.x*c13x2*c13.y*(-6*c21.y*c23.y - 3*c22y2) +
##                c13x2*c22.x*c13.y*(6*c20.y*c22.y + 3*c21y2) + c20.x*c13x2*c13.y*(6*c21.y*c23.y + 3*c22y2) +
##                c13x3*(-2*c20.y*c21.y*c23.y - c22.y*(2*c20.y*c22.y + c21y2) - c20.y*(2*c21.y*c23.y + c22y2) -
##                c21.y*(2*c20.y*c23.y + 2*c21.y*c22.y)),
##            -c10.x*c11.x*c12.y*c13.x*c13.y*c23.y + c10.x*c11.y*c12.x*c13.x*c13.y*c23.y + 6*c10.x*c11.y*c12.y*c13.x*c13.y*c23.x -
##                6*c10.y*c11.x*c12.x*c13.x*c13.y*c23.y - c10.y*c11.x*c12.y*c13.x*c13.y*c23.x + c10.y*c11.y*c12.x*c13.x*c13.y*c23.x +
##                c11.x*c11.y*c12.x*c12.y*c13.x*c23.y - c11.x*c11.y*c12.x*c12.y*c13.y*c23.x + c11.x*c20.x*c12.y*c13.x*c13.y*c23.y +
##                c11.x*c20.y*c12.y*c13.x*c13.y*c23.x + c11.x*c21.x*c12.y*c13.x*c13.y*c22.y + c11.x*c12.y*c13.x*c21.y*c22.x*c13.y -
##                c20.x*c11.y*c12.x*c13.x*c13.y*c23.y - 6*c20.x*c11.y*c12.y*c13.x*c13.y*c23.x - c11.y*c12.x*c20.y*c13.x*c13.y*c23.x -
##                c11.y*c12.x*c21.x*c13.x*c13.y*c22.y - c11.y*c12.x*c13.x*c21.y*c22.x*c13.y - 6*c11.y*c21.x*c12.y*c13.x*c22.x*c13.y -
##                6*c10.x*c20.x*c13y3*c23.x - 6*c10.x*c21.x*c22.x*c13y3 - 2*c10.x*c12y3*c13.x*c23.x + 6*c20.x*c21.x*c22.x*c13y3 +
##                2*c20.x*c12y3*c13.x*c23.x + 2*c21.x*c12y3*c13.x*c22.x + 2*c10.y*c12x3*c13.y*c23.y - 6*c10.x*c10.y*c13.x*c13y2*c23.x +
##                3*c10.x*c11.x*c12.x*c13y2*c23.y - 2*c10.x*c11.x*c12.y*c13y2*c23.x - 4*c10.x*c11.y*c12.x*c13y2*c23.x +
##                3*c10.y*c11.x*c12.x*c13y2*c23.x + 6*c10.x*c10.y*c13x2*c13.y*c23.y + 6*c10.x*c20.x*c13.x*c13y2*c23.y -
##                3*c10.x*c11.y*c12.y*c13x2*c23.y + 2*c10.x*c12.x*c12y2*c13.x*c23.y + 2*c10.x*c12.x*c12y2*c13.y*c23.x +
##                6*c10.x*c20.y*c13.x*c13y2*c23.x + 6*c10.x*c21.x*c13.x*c13y2*c22.y + 6*c10.x*c13.x*c21.y*c22.x*c13y2 +
##                4*c10.y*c11.x*c12.y*c13x2*c23.y + 6*c10.y*c20.x*c13.x*c13y2*c23.x + 2*c10.y*c11.y*c12.x*c13x2*c23.y -
##                3*c10.y*c11.y*c12.y*c13x2*c23.x + 2*c10.y*c12.x*c12y2*c13.x*c23.x + 6*c10.y*c21.x*c13.x*c22.x*c13y2 -
##                3*c11.x*c20.x*c12.x*c13y2*c23.y + 2*c11.x*c20.x*c12.y*c13y2*c23.x + c11.x*c11.y*c12y2*c13.x*c23.x -
##                3*c11.x*c12.x*c20.y*c13y2*c23.x - 3*c11.x*c12.x*c21.x*c13y2*c22.y - 3*c11.x*c12.x*c21.y*c22.x*c13y2 +
##                2*c11.x*c21.x*c12.y*c22.x*c13y2 + 4*c20.x*c11.y*c12.x*c13y2*c23.x + 4*c11.y*c12.x*c21.x*c22.x*c13y2 -
##                2*c10.x*c12x2*c12.y*c13.y*c23.y - 6*c10.y*c20.x*c13x2*c13.y*c23.y - 6*c10.y*c20.y*c13x2*c13.y*c23.x -
##                6*c10.y*c21.x*c13x2*c13.y*c22.y - 2*c10.y*c12x2*c12.y*c13.x*c23.y - 2*c10.y*c12x2*c12.y*c13.y*c23.x -
##                6*c10.y*c13x2*c21.y*c22.x*c13.y - c11.x*c11.y*c12x2*c13.y*c23.y - 2*c11.x*c11y2*c13.x*c13.y*c23.x +
##                3*c20.x*c11.y*c12.y*c13x2*c23.y - 2*c20.x*c12.x*c12y2*c13.x*c23.y - 2*c20.x*c12.x*c12y2*c13.y*c23.x -
##                6*c20.x*c20.y*c13.x*c13y2*c23.x - 6*c20.x*c21.x*c13.x*c13y2*c22.y - 6*c20.x*c13.x*c21.y*c22.x*c13y2 +
##                3*c11.y*c20.y*c12.y*c13x2*c23.x + 3*c11.y*c21.x*c12.y*c13x2*c22.y + 3*c11.y*c12.y*c13x2*c21.y*c22.x -
##                2*c12.x*c20.y*c12y2*c13.x*c23.x - 2*c12.x*c21.x*c12y2*c13.x*c22.y - 2*c12.x*c21.x*c12y2*c22.x*c13.y -
##                2*c12.x*c12y2*c13.x*c21.y*c22.x - 6*c20.y*c21.x*c13.x*c22.x*c13y2 - c11y2*c12.x*c12.y*c13.x*c23.x +
##                2*c20.x*c12x2*c12.y*c13.y*c23.y + 6*c20.y*c13x2*c21.y*c22.x*c13.y + 2*c11x2*c11.y*c13.x*c13.y*c23.y +
##                c11x2*c12.x*c12.y*c13.y*c23.y + 2*c12x2*c20.y*c12.y*c13.y*c23.x + 2*c12x2*c21.x*c12.y*c13.y*c22.y +
##                2*c12x2*c12.y*c21.y*c22.x*c13.y + c21x3*c13y3 + 3*c10x2*c13y3*c23.x - 3*c10y2*c13x3*c23.y +
##                3*c20x2*c13y3*c23.x + c11y3*c13x2*c23.x - c11x3*c13y2*c23.y - c11.x*c11y2*c13x2*c23.y +
##                c11x2*c11.y*c13y2*c23.x - 3*c10x2*c13.x*c13y2*c23.y + 3*c10y2*c13x2*c13.y*c23.x - c11x2*c12y2*c13.x*c23.y +
##                c11y2*c12x2*c13.y*c23.x - 3*c21x2*c13.x*c21.y*c13y2 - 3*c20x2*c13.x*c13y2*c23.y + 3*c20y2*c13x2*c13.y*c23.x +
##                c11.x*c12.x*c13.x*c13.y*(6*c20.y*c23.y + 6*c21.y*c22.y) + c12x3*c13.y*(-2*c20.y*c23.y - 2*c21.y*c22.y) +
##                c10.y*c13x3*(6*c20.y*c23.y + 6*c21.y*c22.y) + c11.y*c12.x*c13x2*(-2*c20.y*c23.y - 2*c21.y*c22.y) +
##                c12x2*c12.y*c13.x*(2*c20.y*c23.y + 2*c21.y*c22.y) + c11.x*c12.y*c13x2*(-4*c20.y*c23.y - 4*c21.y*c22.y) +
##                c10.x*c13x2*c13.y*(-6*c20.y*c23.y - 6*c21.y*c22.y) + c20.x*c13x2*c13.y*(6*c20.y*c23.y + 6*c21.y*c22.y) +
##                c21.x*c13x2*c13.y*(6*c20.y*c22.y + 3*c21y2) + c13x3*(-2*c20.y*c21.y*c22.y - c20y2*c23.y -
##                c21.y*(2*c20.y*c22.y + c21y2) - c20.y*(2*c20.y*c23.y + 2*c21.y*c22.y)),
##            -c10.x*c11.x*c12.y*c13.x*c13.y*c22.y + c10.x*c11.y*c12.x*c13.x*c13.y*c22.y + 6*c10.x*c11.y*c12.y*c13.x*c22.x*c13.y -
##                6*c10.y*c11.x*c12.x*c13.x*c13.y*c22.y - c10.y*c11.x*c12.y*c13.x*c22.x*c13.y + c10.y*c11.y*c12.x*c13.x*c22.x*c13.y +
##                c11.x*c11.y*c12.x*c12.y*c13.x*c22.y - c11.x*c11.y*c12.x*c12.y*c22.x*c13.y + c11.x*c20.x*c12.y*c13.x*c13.y*c22.y +
##                c11.x*c20.y*c12.y*c13.x*c22.x*c13.y + c11.x*c21.x*c12.y*c13.x*c21.y*c13.y - c20.x*c11.y*c12.x*c13.x*c13.y*c22.y -
##                6*c20.x*c11.y*c12.y*c13.x*c22.x*c13.y - c11.y*c12.x*c20.y*c13.x*c22.x*c13.y - c11.y*c12.x*c21.x*c13.x*c21.y*c13.y -
##                6*c10.x*c20.x*c22.x*c13y3 - 2*c10.x*c12y3*c13.x*c22.x + 2*c20.x*c12y3*c13.x*c22.x + 2*c10.y*c12x3*c13.y*c22.y -
##                6*c10.x*c10.y*c13.x*c22.x*c13y2 + 3*c10.x*c11.x*c12.x*c13y2*c22.y - 2*c10.x*c11.x*c12.y*c22.x*c13y2 -
##                4*c10.x*c11.y*c12.x*c22.x*c13y2 + 3*c10.y*c11.x*c12.x*c22.x*c13y2 + 6*c10.x*c10.y*c13x2*c13.y*c22.y +
##                6*c10.x*c20.x*c13.x*c13y2*c22.y - 3*c10.x*c11.y*c12.y*c13x2*c22.y + 2*c10.x*c12.x*c12y2*c13.x*c22.y +
##                2*c10.x*c12.x*c12y2*c22.x*c13.y + 6*c10.x*c20.y*c13.x*c22.x*c13y2 + 6*c10.x*c21.x*c13.x*c21.y*c13y2 +
##                4*c10.y*c11.x*c12.y*c13x2*c22.y + 6*c10.y*c20.x*c13.x*c22.x*c13y2 + 2*c10.y*c11.y*c12.x*c13x2*c22.y -
##                3*c10.y*c11.y*c12.y*c13x2*c22.x + 2*c10.y*c12.x*c12y2*c13.x*c22.x - 3*c11.x*c20.x*c12.x*c13y2*c22.y +
##                2*c11.x*c20.x*c12.y*c22.x*c13y2 + c11.x*c11.y*c12y2*c13.x*c22.x - 3*c11.x*c12.x*c20.y*c22.x*c13y2 -
##                3*c11.x*c12.x*c21.x*c21.y*c13y2 + 4*c20.x*c11.y*c12.x*c22.x*c13y2 - 2*c10.x*c12x2*c12.y*c13.y*c22.y -
##                6*c10.y*c20.x*c13x2*c13.y*c22.y - 6*c10.y*c20.y*c13x2*c22.x*c13.y - 6*c10.y*c21.x*c13x2*c21.y*c13.y -
##                2*c10.y*c12x2*c12.y*c13.x*c22.y - 2*c10.y*c12x2*c12.y*c22.x*c13.y - c11.x*c11.y*c12x2*c13.y*c22.y -
##                2*c11.x*c11y2*c13.x*c22.x*c13.y + 3*c20.x*c11.y*c12.y*c13x2*c22.y - 2*c20.x*c12.x*c12y2*c13.x*c22.y -
##                2*c20.x*c12.x*c12y2*c22.x*c13.y - 6*c20.x*c20.y*c13.x*c22.x*c13y2 - 6*c20.x*c21.x*c13.x*c21.y*c13y2 +
##                3*c11.y*c20.y*c12.y*c13x2*c22.x + 3*c11.y*c21.x*c12.y*c13x2*c21.y - 2*c12.x*c20.y*c12y2*c13.x*c22.x -
##                2*c12.x*c21.x*c12y2*c13.x*c21.y - c11y2*c12.x*c12.y*c13.x*c22.x + 2*c20.x*c12x2*c12.y*c13.y*c22.y -
##                3*c11.y*c21x2*c12.y*c13.x*c13.y + 6*c20.y*c21.x*c13x2*c21.y*c13.y + 2*c11x2*c11.y*c13.x*c13.y*c22.y +
##                c11x2*c12.x*c12.y*c13.y*c22.y + 2*c12x2*c20.y*c12.y*c22.x*c13.y + 2*c12x2*c21.x*c12.y*c21.y*c13.y -
##                3*c10.x*c21x2*c13y3 + 3*c20.x*c21x2*c13y3 + 3*c10x2*c22.x*c13y3 - 3*c10y2*c13x3*c22.y + 3*c20x2*c22.x*c13y3 +
##                c21x2*c12y3*c13.x + c11y3*c13x2*c22.x - c11x3*c13y2*c22.y + 3*c10.y*c21x2*c13.x*c13y2 -
##                c11.x*c11y2*c13x2*c22.y + c11.x*c21x2*c12.y*c13y2 + 2*c11.y*c12.x*c21x2*c13y2 + c11x2*c11.y*c22.x*c13y2 -
##                c12.x*c21x2*c12y2*c13.y - 3*c20.y*c21x2*c13.x*c13y2 - 3*c10x2*c13.x*c13y2*c22.y + 3*c10y2*c13x2*c22.x*c13.y -
##                c11x2*c12y2*c13.x*c22.y + c11y2*c12x2*c22.x*c13.y - 3*c20x2*c13.x*c13y2*c22.y + 3*c20y2*c13x2*c22.x*c13.y +
##                c12x2*c12.y*c13.x*(2*c20.y*c22.y + c21y2) + c11.x*c12.x*c13.x*c13.y*(6*c20.y*c22.y + 3*c21y2) +
##                c12x3*c13.y*(-2*c20.y*c22.y - c21y2) + c10.y*c13x3*(6*c20.y*c22.y + 3*c21y2) +
##                c11.y*c12.x*c13x2*(-2*c20.y*c22.y - c21y2) + c11.x*c12.y*c13x2*(-4*c20.y*c22.y - 2*c21y2) +
##                c10.x*c13x2*c13.y*(-6*c20.y*c22.y - 3*c21y2) + c20.x*c13x2*c13.y*(6*c20.y*c22.y + 3*c21y2) +
##                c13x3*(-2*c20.y*c21y2 - c20y2*c22.y - c20.y*(2*c20.y*c22.y + c21y2)),
##            -c10.x*c11.x*c12.y*c13.x*c21.y*c13.y + c10.x*c11.y*c12.x*c13.x*c21.y*c13.y + 6*c10.x*c11.y*c21.x*c12.y*c13.x*c13.y -
##                6*c10.y*c11.x*c12.x*c13.x*c21.y*c13.y - c10.y*c11.x*c21.x*c12.y*c13.x*c13.y + c10.y*c11.y*c12.x*c21.x*c13.x*c13.y -
##                c11.x*c11.y*c12.x*c21.x*c12.y*c13.y + c11.x*c11.y*c12.x*c12.y*c13.x*c21.y + c11.x*c20.x*c12.y*c13.x*c21.y*c13.y +
##                6*c11.x*c12.x*c20.y*c13.x*c21.y*c13.y + c11.x*c20.y*c21.x*c12.y*c13.x*c13.y - c20.x*c11.y*c12.x*c13.x*c21.y*c13.y -
##                6*c20.x*c11.y*c21.x*c12.y*c13.x*c13.y - c11.y*c12.x*c20.y*c21.x*c13.x*c13.y - 6*c10.x*c20.x*c21.x*c13y3 -
##                2*c10.x*c21.x*c12y3*c13.x + 6*c10.y*c20.y*c13x3*c21.y + 2*c20.x*c21.x*c12y3*c13.x + 2*c10.y*c12x3*c21.y*c13.y -
##                2*c12x3*c20.y*c21.y*c13.y - 6*c10.x*c10.y*c21.x*c13.x*c13y2 + 3*c10.x*c11.x*c12.x*c21.y*c13y2 -
##                2*c10.x*c11.x*c21.x*c12.y*c13y2 - 4*c10.x*c11.y*c12.x*c21.x*c13y2 + 3*c10.y*c11.x*c12.x*c21.x*c13y2 +
##                6*c10.x*c10.y*c13x2*c21.y*c13.y + 6*c10.x*c20.x*c13.x*c21.y*c13y2 - 3*c10.x*c11.y*c12.y*c13x2*c21.y +
##                2*c10.x*c12.x*c21.x*c12y2*c13.y + 2*c10.x*c12.x*c12y2*c13.x*c21.y + 6*c10.x*c20.y*c21.x*c13.x*c13y2 +
##                4*c10.y*c11.x*c12.y*c13x2*c21.y + 6*c10.y*c20.x*c21.x*c13.x*c13y2 + 2*c10.y*c11.y*c12.x*c13x2*c21.y -
##                3*c10.y*c11.y*c21.x*c12.y*c13x2 + 2*c10.y*c12.x*c21.x*c12y2*c13.x - 3*c11.x*c20.x*c12.x*c21.y*c13y2 +
##                2*c11.x*c20.x*c21.x*c12.y*c13y2 + c11.x*c11.y*c21.x*c12y2*c13.x - 3*c11.x*c12.x*c20.y*c21.x*c13y2 +
##                4*c20.x*c11.y*c12.x*c21.x*c13y2 - 6*c10.x*c20.y*c13x2*c21.y*c13.y - 2*c10.x*c12x2*c12.y*c21.y*c13.y -
##                6*c10.y*c20.x*c13x2*c21.y*c13.y - 6*c10.y*c20.y*c21.x*c13x2*c13.y - 2*c10.y*c12x2*c21.x*c12.y*c13.y -
##                2*c10.y*c12x2*c12.y*c13.x*c21.y - c11.x*c11.y*c12x2*c21.y*c13.y - 4*c11.x*c20.y*c12.y*c13x2*c21.y -
##                2*c11.x*c11y2*c21.x*c13.x*c13.y + 3*c20.x*c11.y*c12.y*c13x2*c21.y - 2*c20.x*c12.x*c21.x*c12y2*c13.y -
##                2*c20.x*c12.x*c12y2*c13.x*c21.y - 6*c20.x*c20.y*c21.x*c13.x*c13y2 - 2*c11.y*c12.x*c20.y*c13x2*c21.y +
##                3*c11.y*c20.y*c21.x*c12.y*c13x2 - 2*c12.x*c20.y*c21.x*c12y2*c13.x - c11y2*c12.x*c21.x*c12.y*c13.x +
##                6*c20.x*c20.y*c13x2*c21.y*c13.y + 2*c20.x*c12x2*c12.y*c21.y*c13.y + 2*c11x2*c11.y*c13.x*c21.y*c13.y +
##                c11x2*c12.x*c12.y*c21.y*c13.y + 2*c12x2*c20.y*c21.x*c12.y*c13.y + 2*c12x2*c20.y*c12.y*c13.x*c21.y +
##                3*c10x2*c21.x*c13y3 - 3*c10y2*c13x3*c21.y + 3*c20x2*c21.x*c13y3 + c11y3*c21.x*c13x2 - c11x3*c21.y*c13y2 -
##                3*c20y2*c13x3*c21.y - c11.x*c11y2*c13x2*c21.y + c11x2*c11.y*c21.x*c13y2 - 3*c10x2*c13.x*c21.y*c13y2 +
##                3*c10y2*c21.x*c13x2*c13.y - c11x2*c12y2*c13.x*c21.y + c11y2*c12x2*c21.x*c13.y - 3*c20x2*c13.x*c21.y*c13y2 +
##                3*c20y2*c21.x*c13x2*c13.y,
##            c10.x*c10.y*c11.x*c12.y*c13.x*c13.y - c10.x*c10.y*c11.y*c12.x*c13.x*c13.y + c10.x*c11.x*c11.y*c12.x*c12.y*c13.y -
##                c10.y*c11.x*c11.y*c12.x*c12.y*c13.x - c10.x*c11.x*c20.y*c12.y*c13.x*c13.y + 6*c10.x*c20.x*c11.y*c12.y*c13.x*c13.y +
##                c10.x*c11.y*c12.x*c20.y*c13.x*c13.y - c10.y*c11.x*c20.x*c12.y*c13.x*c13.y - 6*c10.y*c11.x*c12.x*c20.y*c13.x*c13.y +
##                c10.y*c20.x*c11.y*c12.x*c13.x*c13.y - c11.x*c20.x*c11.y*c12.x*c12.y*c13.y + c11.x*c11.y*c12.x*c20.y*c12.y*c13.x +
##                c11.x*c20.x*c20.y*c12.y*c13.x*c13.y - c20.x*c11.y*c12.x*c20.y*c13.x*c13.y - 2*c10.x*c20.x*c12y3*c13.x +
##                2*c10.y*c12x3*c20.y*c13.y - 3*c10.x*c10.y*c11.x*c12.x*c13y2 - 6*c10.x*c10.y*c20.x*c13.x*c13y2 +
##                3*c10.x*c10.y*c11.y*c12.y*c13x2 - 2*c10.x*c10.y*c12.x*c12y2*c13.x - 2*c10.x*c11.x*c20.x*c12.y*c13y2 -
##                c10.x*c11.x*c11.y*c12y2*c13.x + 3*c10.x*c11.x*c12.x*c20.y*c13y2 - 4*c10.x*c20.x*c11.y*c12.x*c13y2 +
##                3*c10.y*c11.x*c20.x*c12.x*c13y2 + 6*c10.x*c10.y*c20.y*c13x2*c13.y + 2*c10.x*c10.y*c12x2*c12.y*c13.y +
##                2*c10.x*c11.x*c11y2*c13.x*c13.y + 2*c10.x*c20.x*c12.x*c12y2*c13.y + 6*c10.x*c20.x*c20.y*c13.x*c13y2 -
##                3*c10.x*c11.y*c20.y*c12.y*c13x2 + 2*c10.x*c12.x*c20.y*c12y2*c13.x + c10.x*c11y2*c12.x*c12.y*c13.x +
##                c10.y*c11.x*c11.y*c12x2*c13.y + 4*c10.y*c11.x*c20.y*c12.y*c13x2 - 3*c10.y*c20.x*c11.y*c12.y*c13x2 +
##                2*c10.y*c20.x*c12.x*c12y2*c13.x + 2*c10.y*c11.y*c12.x*c20.y*c13x2 + c11.x*c20.x*c11.y*c12y2*c13.x -
##                3*c11.x*c20.x*c12.x*c20.y*c13y2 - 2*c10.x*c12x2*c20.y*c12.y*c13.y - 6*c10.y*c20.x*c20.y*c13x2*c13.y -
##                2*c10.y*c20.x*c12x2*c12.y*c13.y - 2*c10.y*c11x2*c11.y*c13.x*c13.y - c10.y*c11x2*c12.x*c12.y*c13.y -
##                2*c10.y*c12x2*c20.y*c12.y*c13.x - 2*c11.x*c20.x*c11y2*c13.x*c13.y - c11.x*c11.y*c12x2*c20.y*c13.y +
##                3*c20.x*c11.y*c20.y*c12.y*c13x2 - 2*c20.x*c12.x*c20.y*c12y2*c13.x - c20.x*c11y2*c12.x*c12.y*c13.x +
##                3*c10y2*c11.x*c12.x*c13.x*c13.y + 3*c11.x*c12.x*c20y2*c13.x*c13.y + 2*c20.x*c12x2*c20.y*c12.y*c13.y -
##                3*c10x2*c11.y*c12.y*c13.x*c13.y + 2*c11x2*c11.y*c20.y*c13.x*c13.y + c11x2*c12.x*c20.y*c12.y*c13.y -
##                3*c20x2*c11.y*c12.y*c13.x*c13.y - c10x3*c13y3 + c10y3*c13x3 + c20x3*c13y3 - c20y3*c13x3 -
##                3*c10.x*c20x2*c13y3 - c10.x*c11y3*c13x2 + 3*c10x2*c20.x*c13y3 + c10.y*c11x3*c13y2 +
##                3*c10.y*c20y2*c13x3 + c20.x*c11y3*c13x2 + c10x2*c12y3*c13.x - 3*c10y2*c20.y*c13x3 - c10y2*c12x3*c13.y +
##                c20x2*c12y3*c13.x - c11x3*c20.y*c13y2 - c12x3*c20y2*c13.y - c10.x*c11x2*c11.y*c13y2 +
##                c10.y*c11.x*c11y2*c13x2 - 3*c10.x*c10y2*c13x2*c13.y - c10.x*c11y2*c12x2*c13.y + c10.y*c11x2*c12y2*c13.x -
##                c11.x*c11y2*c20.y*c13x2 + 3*c10x2*c10.y*c13.x*c13y2 + c10x2*c11.x*c12.y*c13y2 +
##                2*c10x2*c11.y*c12.x*c13y2 - 2*c10y2*c11.x*c12.y*c13x2 - c10y2*c11.y*c12.x*c13x2 + c11x2*c20.x*c11.y*c13y2 -
##                3*c10.x*c20y2*c13x2*c13.y + 3*c10.y*c20x2*c13.x*c13y2 + c11.x*c20x2*c12.y*c13y2 - 2*c11.x*c20y2*c12.y*c13x2 +
##                c20.x*c11y2*c12x2*c13.y - c11.y*c12.x*c20y2*c13x2 - c10x2*c12.x*c12y2*c13.y - 3*c10x2*c20.y*c13.x*c13y2 +
##                3*c10y2*c20.x*c13x2*c13.y + c10y2*c12x2*c12.y*c13.x - c11x2*c20.y*c12y2*c13.x + 2*c20x2*c11.y*c12.x*c13y2 +
##                3*c20.x*c20y2*c13x2*c13.y - c20x2*c12.x*c12y2*c13.y - 3*c20x2*c20.y*c13.x*c13y2 + c12x2*c20y2*c12.y*c13.x
##            ]
##    roots = poly.getRootsInInterval(0,1);
##    #
##    for i in range(len(roots)): #( var i = 0; i < roots.length; i++ ) {
##        s = roots[i]
##        xRoots = [c13.x,
##                  c12.x,
##                  c11.x,
##                  c10.x - c20.x - s*c21.x - s*s*c22.x - s*s*s*c23.x
##                  ].getRoots()
##        yRoots = [c13.y,
##                  c12.y,
##                  c11.y,
##                  c10.y - c20.y - s*c21.y - s*s*c22.y - s*s*s*c23.y
##                  ].getRoots()
##
##        if ( len(xRoots) > 0 and len(yRoots) > 0 ):
##            TOLERANCE = 1e-4;
##            #label:checkRoots
##            #
##            for j in range(len(xRoots): #( var j = 0; j < xRoots.length; j++ ) {
##                xRoot = xRoots[j]
##                #
##                if ( 0 <= xRoot and xRoot <= 1 ) {
##                    for k in range(len(yRoots)): #( var k = 0; k < yRoots.length; k++ ) {
##                        if ( math.abs( xRoot - yRoots[k] ) < TOLERANCE ):
##                            result.points.push( c23.multiply(s*s*s).add(c22.multiply(s*s).add(c21.multiply(s).add(c20))))
##                            break #checkRoots;
##    #
##    if ( result.points.length > 0 ): result.status = "Intersection";
##    return result




    
    
###
class LasercutJigsaw(inkex.Effect):

    def __init__(self):
        inkex.Effect.__init__(self)
        self.OptionParser.add_option("-x", "--width",
                        action="store", type="float",
                        dest="width", default=50.0,
                        help="The Box Width - in the X dimension")
        self.OptionParser.add_option("-y", "--height",
                        action="store", type="float",
                        dest="height", default=30.0,
                        help="The Box Height - in the Y dimension")
        self.OptionParser.add_option("-u", "--units",
                        action="store", type="string",
                        dest="units", default="cm",
                        help="The unit of the box dimensions")
        self.OptionParser.add_option("-w", "--pieces_W",
                        action="store", type="int",
                        dest="pieces_W", default=11,
                        help="How many pieces across")
        self.OptionParser.add_option("-z", "--pieces_H",
                        action="store", type="int",
                        dest="pieces_H", default=11,
                        help="How many pieces down")
        self.OptionParser.add_option("-k", "--notch_percent",
                        action="store", type="float",
                        dest="notch_percent", default=0.0,
                        help="Notch relative size. 0 to 1. 0.15 is good")
        self.OptionParser.add_option("-r", "--rand",
                        action="store", type="float",
                        dest="rand", default=0.1,
                        help="Amount to perturb the basic piece grid.")
        self.OptionParser.add_option("-i", "--innerradius",
                        action="store", type="float",
                        dest="innerradius", default=5.0,
                        help="0 implies square corners")
        self.OptionParser.add_option("-b", "--border",
                        action="store", type="inkbool",
                        dest="border", default=False,
                        help="Add Outer Surround")
        self.OptionParser.add_option("-a", "--borderwidth",
                        action="store", type="float",
                        dest="borderwidth", default=10.0,
                        help="Size of external surrounding border.")
        self.OptionParser.add_option("-o", "--outerradius",
                        action="store", type="float",
                        dest="outerradius", default=5.0,
                        help="0 implies square corners")
        self.OptionParser.add_option("-p", "--pack",
                        action="store", type="string",
                        dest="pack", default="Below",
                        help="Where to place backing piece on page")
        self.OptionParser.add_option("-g", "--use_seed",
                        action="store", type="inkbool",
                        dest="use_seed", default=False,
                        help="Use the kerf value as the drawn line width")
        self.OptionParser.add_option("-s", "--seed",
                        action="store", type="int",
                        dest="seed", default=12345,
                        help="Random seed for repeatability")
        self.OptionParser.add_option("-j", "--pieces",
                        action="store", type="inkbool",
                        dest="pieces", default=False,
                        help="Make extra pieces for manual boolean separation.")
        self.OptionParser.add_option("-n", "--smooth_edges",
                        action="store", type="inkbool",
                        dest="smooth_edges", default=False,
                        help="Allow pieces with smooth edges.")
        self.OptionParser.add_option("-f", "--noknob_frequency",
                        action="store", type="float",
                        dest="noknob_frequency", default=10,
                        help="Percentage of smooth-sided edges.")						
        # dummy for the doc tab - which is named
        self.OptionParser.add_option("--tab",
                        action="store", type="string", 
                        dest="tab", default="use",
                        help="The selected UI-tab when OK was pressed")
        # internal useful variables
        self.stroke_width = 0.1 # default for visiblity
        self.line_style = {'stroke': '#0000FF', # Ponoko blue
                           'fill': 'none',
                           'stroke-width': self.stroke_width,
                           'stroke-linecap': 'butt',
                           'stroke-linejoin': 'miter'}


    def add_jigsaw_horiz_line(self, startx, starty, stepx, steps, width, style, name, parent):
        """ complex version All C smooth
            - get ctrl pt offset and use on both sides of each node (negate for smooth)"""
        line_path = []
        # starts with an M - then C with first point same as M = smooth (also last point still in C but doubled)
        line_path.append(['M', [startx, starty]])
        clist = [startx, starty] # duplicate 1st point so its smooth
        for i in range(1,steps+1):
            flip = 1
            if random.uniform(0.0,1.0) < 0.5:
                flip = -1
            do_smooth = False
            if self.smooth_edges:
                if random.uniform(0.0,100.0) < self.noknob_frequency:
                    do_smooth = True
            if do_smooth:
                pt1 = randomize((startx+i*stepx/2+stepx/2*(i-1), starty), self.random_radius/3, True)
                rand1 = randomize((0, 0), self.random_radius/4, True, True)
                # up to pt1
                ctrl1 = (-self.notch_step*1.5, self.notch_step*1.5)
                clist.extend([pt1[0]+ctrl1[0]-rand1[0], pt1[1]-ctrl1[1]*flip+rand1[1]*flip])
                clist.extend(pt1)
                # last ctrl point for next step
                clist.extend([pt1[0]-ctrl1[0]+rand1[0], pt1[1]+ctrl1[1]*flip-rand1[1]*flip])
            else:
                pt1 = randomize((startx-self.notch_step+i*stepx/2+stepx/2*(i-1), starty+self.notch_step/4*flip), self.random_radius/3, True)
                pt2 = randomize((startx-self.notch_step+i*stepx/2+stepx/2*(i-1), starty-self.notch_step*flip), self.random_radius/3, True)
                # pt3 is foor tip of the notch - required ?
                pt4 = randomize((startx+self.notch_step+i*stepx/2+stepx/2*(i-1), starty-self.notch_step*flip), self.random_radius/3, True) #mirror of 2
                pt5 = randomize((startx+self.notch_step+i*stepx/2+stepx/2*(i-1), starty+self.notch_step/4*flip), self.random_radius/3, True) # mirror of pt1
                # Create random local value for x,y of handle - then reflect to enforce smoothness
                rand1 = randomize((0, 0), self.random_radius/4, True, True)
                rand2 = randomize((0, 0), self.random_radius/4, True, True)
                rand4 = randomize((0, 0), self.random_radius/4, True, True)
                rand5 = randomize((0, 0), self.random_radius/4, True, True)
                # up to pt1
                #ctrl1_2 = (startx+i*stepx/2+(i-1)*stepx/2, starty-self.notch_step/3)
                ctrl1 = (self.notch_step/1.2, -self.notch_step/3)
                clist.extend([pt1[0]-ctrl1[0]-rand1[0], pt1[1]-ctrl1[1]*flip+rand1[1]*flip])
                clist.extend(pt1)
                # up to pt2
                clist.extend([pt1[0]+ctrl1[0]+rand1[0], pt1[1]+ctrl1[1]*flip-rand1[1]*flip])
                ctrl2 = (0, -self.notch_step/1.2)
                clist.extend([pt2[0]+ctrl2[0]-rand2[0], pt2[1]-ctrl2[1]*flip+rand2[1]*flip])
                clist.extend(pt2)
                # up to pt4
                clist.extend([pt2[0]-ctrl2[0]+rand2[0], pt2[1]+ctrl2[1]*flip-rand2[1]*flip])
                ctrl4 = (0, self.notch_step/1.2)
                clist.extend([pt4[0]+ctrl4[0]-rand4[0], pt4[1]-ctrl4[1]*flip+rand4[1]*flip])
                clist.extend(pt4)
                # up to pt5
                clist.extend([pt4[0]-ctrl4[0]+rand4[0], pt4[1]+ctrl4[1]*flip-rand4[1]*flip])
                ctrl5 = (self.notch_step/1.2, self.notch_step/3)
                clist.extend([pt5[0]-ctrl5[0]+rand5[0], pt5[1]-ctrl5[1]*flip-rand5[1]*flip])
                clist.extend(pt5)
                # last ctrl point for next step
                clist.extend([pt5[0]+ctrl5[0]-rand5[0], pt5[1]+ctrl5[1]*flip+rand5[1]*flip])        
        #
        clist.extend([width, starty, width, starty]) # doubled up at end for smooth curve
        line_path.append(['C',clist])
        #sys.stderr.write("%s\n"% line_path)
        line_style = simplestyle.formatStyle(style)
        attribs = { 'style':line_style, 'id':name, 'd':formatPath(line_path) }
        inkex.etree.SubElement(parent, inkex.addNS('path','svg'), attribs )

    def create_horiz_blocks(self, group, gridy, style):
        path = lastpath = 0
        blocks = []
        count = 0
        for node in gridy.iterchildren():
            if node.tag == inkex.addNS('path','svg'): # which they ALL should because we just made them
                path = cubicsuperpath.parsePath(node.get('d')) # turn it into a global C style SVG path
                #sys.stderr.write("count: %d\n"% count)
                if count == 0: # first one so use the top border
                    spath = node.get('d') # work on string instead of cubicpath
                    lastpoint = spath.split()[-2:]
                    lastx = float(lastpoint[0])
                    lasty = float(lastpoint[1])
                    #sys.stderr.write("lastpoint: %s\n"% lastpoint)
                    spath += ' %f %f %f %f %f %f' % (lastx,lasty-self.inner_radius, lastx,1.5*self.inner_radius, lastx,self.inner_radius)
                    spath += ' %f %f %f %f %f %f' % (self.width,self.inner_radius/2, self.width-self.inner_radius/2,0, self.width-self.inner_radius,0)
                    spath += ' %f %f %f %f %f %f' % (self.width-self.inner_radius/2,0, 1.5*self.inner_radius,0, self.inner_radius, 0)
                    spath += ' %f %f %f %f %f %f' % (self.inner_radius/2, 0, 0,self.inner_radius/2, 0,self.inner_radius)
                    spath += 'z'
                    #sys.stderr.write("spath: %s\n"% spath)
                    #
                    name = "RowPieces_%d" % (count)
                    attribs = { 'style':style, 'id':name, 'd':spath }
                    n = inkex.etree.SubElement(group, inkex.addNS('path','svg'), attribs )
                    blocks.append(n) # for direct traversal later
                else: # internal line - concat a reversed version with the last one
                    thispath = copy.deepcopy(path)
                    for i in range(len(thispath[0])): # reverse the internal C pairs
                        thispath[0][i].reverse()
                    thispath[0].reverse() # reverse the entire line
                    lastpath[0].extend(thispath[0]) # append
                    name = "RowPieces_%d" % (count)
                    attribs = { 'style':style, 'id':name, 'd':cubicsuperpath.formatPath(lastpath) }
                    n = inkex.etree.SubElement(group, inkex.addNS('path','svg'), attribs )
                    blocks.append(n) # for direct traversal later
                    n.set('d', n.get('d')+'z') # close it
                #
                count += 1
                lastpath = path
        # do the last row
        spath = node.get('d') # work on string instead of cubicpath
        lastpoint = spath.split()[-2:]
        lastx = float(lastpoint[0])
        lasty = float(lastpoint[1])
        #sys.stderr.write("lastpoint: %s\n"% lastpoint)
        spath += ' %f %f %f %f %f %f' % (lastx,lasty+self.inner_radius, lastx,self.height-1.5*self.inner_radius, lastx,self.height-self.inner_radius)
        spath += ' %f %f %f %f %f %f' % (self.width,self.height-self.inner_radius/2, self.width-self.inner_radius/2,self.height, self.width-self.inner_radius,self.height)
        spath += ' %f %f %f %f %f %f' % (self.width-self.inner_radius/2,self.height, 1.5*self.inner_radius,self.height, self.inner_radius, self.height)
        spath += ' %f %f %f %f %f %f' % (self.inner_radius/2, self.height, 0,self.height-self.inner_radius/2, 0,self.height-self.inner_radius)
        spath += 'z'
        #
        name = "RowPieces_%d" % (count)
        attribs = { 'style':style, 'id':name, 'd':spath }
        n = inkex.etree.SubElement(group, inkex.addNS('path','svg'), attribs )
        blocks.append(n) # for direct traversal later
        #
        return(blocks)
    

    def create_vert_blocks(self, group, gridx, style):
        path = lastpath = 0
        blocks = []
        count = 0
        for node in gridx.iterchildren():
            if node.tag == inkex.addNS('path','svg'): # which they ALL should because we just made them
                path = cubicsuperpath.parsePath(node.get('d')) # turn it into a global C style SVG path
                #sys.stderr.write("count: %d\n"% count)
                if count == 0: # first one so use the right border
                    spath = node.get('d') # work on string instead of cubicpath
                    lastpoint = spath.split()[-2:]
                    lastx = float(lastpoint[0])
                    lasty = float(lastpoint[1])
                    #sys.stderr.write("lastpoint: %s\n"% lastpoint)
                    spath += ' %f %f %f %f %f %f' % (lastx+self.inner_radius/2,lasty, self.width-1.5*self.inner_radius,lasty, self.width-self.inner_radius, lasty)
                    spath += ' %f %f %f %f %f %f' % (self.width-self.inner_radius/2,lasty, self.width,self.height-self.inner_radius/2, self.width,self.height-self.inner_radius)
                    spath += ' %f %f %f %f %f %f' % (self.width,self.height-1.5*self.inner_radius, self.width, 1.5*self.inner_radius, self.width,self.inner_radius)
                    spath += ' %f %f %f %f %f %f' % (self.width,self.inner_radius/2, self.width-self.inner_radius/2,0, self.width-self.inner_radius,0)
                    spath += 'z'
                    #sys.stderr.write("spath: %s\n"% spath)
                    #
                    name = "ColPieces_%d" % (count)
                    attribs = { 'style':style, 'id':name, 'd':spath }
                    n = inkex.etree.SubElement(group, inkex.addNS('path','svg'), attribs )
                    blocks.append(n) # for direct traversal later
                else: # internal line - concat a reversed version with the last one
                    thispath = copy.deepcopy(path)
                    for i in range(len(thispath[0])): # reverse the internal C pairs
                        thispath[0][i].reverse()
                    thispath[0].reverse() # reverse the entire line
                    lastpath[0].extend(thispath[0]) # append
                    name = "ColPieces_%d" % (count)
                    attribs = { 'style':style, 'id':name, 'd':cubicsuperpath.formatPath(lastpath) }
                    n = inkex.etree.SubElement(group, inkex.addNS('path','svg'), attribs )
                    blocks.append(n) # for direct traversal later
                    n.set('d', n.get('d')+'z') # close it
                #
                count += 1
                lastpath = path
        # do the last one (LHS)
        spath = node.get('d') # work on string instead of cubicpath
        lastpoint = spath.split()[-2:]
        lastx = float(lastpoint[0])
        lasty = float(lastpoint[1])
        #sys.stderr.write("lastpoint: %s\n"% lastpoint)
        spath += ' %f %f %f %f %f %f' % (lastx-self.inner_radius,lasty, 1.5*self.inner_radius, lasty, self.inner_radius,lasty)
        spath += ' %f %f %f %f %f %f' % (self.inner_radius/2,lasty, 0,lasty-self.inner_radius/2, 0,lasty-self.inner_radius)
        spath += ' %f %f %f %f %f %f' % (0,lasty-1.5*self.inner_radius, 0,1.5*self.inner_radius, 0,self.inner_radius)
        spath += ' %f %f %f %f %f %f' % (self.inner_radius/2,0, self.inner_radius,0, 1.5*self.inner_radius, 0)
        spath += 'z'
        #
        name = "ColPieces_%d" % (count)
        attribs = { 'style':style, 'id':name, 'd':spath }
        n = inkex.etree.SubElement(group, inkex.addNS('path','svg'), attribs )
        blocks.append(n) # for direct traversal later
        #
        return(blocks)

    
    def create_pieces(self, jigsaw, gridx, gridy):
        """ Loop through each row """
        # Treat outer edge carefully as border runs around. So special code the edges
        # Internal lines should be in pairs  -with second line reversed and appended to first. Close with a 'z'
        # Create new group
        g_attribs = {inkex.addNS('label','inkscape'):'JigsawPieces:X' + \
                     str( self.pieces_W )+':Y'+str( self.pieces_H ) }
        jigsaw_pieces = inkex.etree.SubElement(jigsaw, 'g', g_attribs)
        line_style = simplestyle.formatStyle(self.line_style)
        #
        xblocks = self.create_horiz_blocks(jigsaw_pieces, gridy, line_style)
        #sys.stderr.write("count: %s\n"% dir(gridx))
        yblocks = self.create_vert_blocks(jigsaw_pieces, gridx, line_style)
        #
        # for each xblock intersect it with each Y block
        #for x in range(len(xblocks)):
        #    for y in range(len(yblocks)):
                
        # delete the paths in xblocks and yblocks
        # transform them out of the way for now
        for node in xblocks:
            node.set('transform', 'translate(%f,%f)' % (self.width, 0))
            simpletransform.fuseTransform(node)
        for node in yblocks:
            node.set('transform', 'translate(%f,%f)' % (self.width, 0))
            simpletransform.fuseTransform(node)

        
    ###--------------------------------------------
    ### The main function called by the Inkscape UI
    def effect(self):
        # document dimensions (for centering)
        docW = self.unittouu(self.document.getroot().get('width'))
        docH = self.unittouu(self.document.getroot().get('height'))
        # extract fields from UI
        self.width  = self.unittouu( str(self.options.width)  + self.options.units )
        self.height  = self.unittouu( str(self.options.height) + self.options.units )
        self.pieces_W = self.options.pieces_W
        self.pieces_H = self.options.pieces_H
        average_block = (self.width/self.pieces_W + self.height/self.pieces_H) / 2
        self.notch_step = average_block * self.options.notch_percent / 3 # 3 = a useful notch size factor
        self.smooth_edges = self.options.smooth_edges
        self.noknob_frequency = self.options.noknob_frequency		
        self.random_radius = self.options.rand * average_block / 5 # 5 = a useful range factor
        self.inner_radius = self.options.innerradius
        if self.inner_radius < 0.01: self.inner_radius = 0.0 # snap to 0 for UI error when setting spinner to 0.0
        self.border = self.options.border
        self.borderwidth = self.options.borderwidth
        self.outer_radius = self.options.outerradius
        if self.outer_radius < 0.01: self.outer_radius = 0.0 # snap to 0 for UI error when setting spinner to 0.0
        self.pack = self.options.pack
        # pieces
        self.pieces = self.options.pieces
        # random function
        if not self.options.use_seed:
            random.seed(self.options.seed)
        
        #
        # set up the main object in the current layer - group gridlines
        g_attribs = {inkex.addNS('label','inkscape'):'Jigsaw:X' + \
                     str( self.pieces_W )+':Y'+str( self.pieces_H ) }
        jigsaw_group = inkex.etree.SubElement(self.current_layer, 'g', g_attribs)
        #Group for X grid
        g_attribs = {inkex.addNS('label','inkscape'):'X_Gridlines'}
        gridx = inkex.etree.SubElement(jigsaw_group, 'g', g_attribs)
        #Group for Y grid
        g_attribs = {inkex.addNS('label','inkscape'):'Y_Gridlines'}
        gridy = inkex.etree.SubElement(jigsaw_group, 'g', g_attribs)

        # Draw the Border
        add_rounded_rectangle(0,0, self.inner_radius, self.width, self.height, self.line_style, 'innerborder', jigsaw_group)
        # Do the Border
        if self.border:
            add_rounded_rectangle(-self.borderwidth,-self.borderwidth, self.outer_radius, self.borderwidth*2+self.width,
                                  self.borderwidth*2+self.height, self.line_style, 'outerborder', jigsaw_group)
            # make a second copy below the jigsaw for the cutout BG
            if self.pack == "Below":
                add_rounded_rectangle(-self.borderwidth,self.borderwidth+ self.height, self.outer_radius, self.borderwidth*2+self.width,
                                      self.borderwidth*2+self.height, self.line_style, 'BG', jigsaw_group, self.pack)
            elif self.pack == "Right":
                add_rounded_rectangle(self.width+self.borderwidth,-self.borderwidth, self.outer_radius, self.borderwidth*2+self.width,
                                      self.borderwidth*2+self.height, self.line_style, 'BG', jigsaw_group, self.pack)
            else: # Separate
                add_rounded_rectangle(self.width+self.borderwidth*2,-self.borderwidth, self.outer_radius, self.borderwidth*2+self.width,
                                      self.borderwidth*2+self.height, self.line_style, 'BG', jigsaw_group)

        # Step through the Grid
        Xstep = self.width / (self.pieces_W)
        Ystep = self.height / (self.pieces_H)
        # Draw Horizontal lines on Y step with Xstep notches
        for i in range(1, self.pieces_H):
            self.add_jigsaw_horiz_line(0, Ystep*i, Xstep, self.pieces_W, self.width, self.line_style, 'YDiv'+str(i), gridy)
        # Draw Vertical lines on X step with Ystep notches
        for i in range(1, self.pieces_W):
            self.add_jigsaw_horiz_line(0, Xstep*i, Ystep, self.pieces_H, self.height, self.line_style, 'XDiv'+str(i), gridx)
        # Rotate lines into pos
        # actualy transform can have multiple transforms in it e.g. 'translate(10,10) rotate(10)'
        for node in gridx.iterchildren():
            if node.tag == inkex.addNS('path','svg'):
                node.set('transform', 'rotate(%d)' % 90)
                simpletransform.fuseTransform(node)
                node.set('transform', 'translate(%f,%f)' % (self.width, 0))
                simpletransform.fuseTransform(node)
        # center the jigsaw
        jigsaw_group.set('transform', 'translate(%f,%f)' % ( (docW-self.width)/2, (docH-self.height)/2 ) )
        
        # pieces
        if self.pieces:
            self.create_pieces(jigsaw_group, gridx,gridy)
            # needs manual boolean ops until that is exposed or we get all the commented code working up top :-(
        

###
if __name__ == '__main__':
    e = LasercutJigsaw()
    e.affect()
