Search in sources :

Example 66 with XYZPoint

use of org.jwildfire.create.tina.base.XYZPoint in project JWildfire by thargor6.

the class CubicLattice3DFunc method transform.

@Override
public void transform(FlameTransformationContext pContext, XForm pXForm, XYZPoint pAffineTP, XYZPoint pVarTP, double pAmount) {
    /* cubicLattice_3D by Larry Berlin, http://aporev.deviantart.com/art/3D-Plugins-Collection-One-138514007?q=gallery%3Aaporev%2F8229210&qo=15 */
    double fill, exnze, wynze, znxy;
    if (fabs(this.xpand) <= 1.0) {
        // values up to 0.5
        fill = this.xpand * 0.5;
    } else {
        // values above 0.5
        fill = sqrt(this.xpand) * 0.5;
    }
    if (_iStyle == 2) {
        exnze = cos(atan2(pAffineTP.x, pAffineTP.z));
        wynze = sin(atan2(pAffineTP.y, pAffineTP.z));
        znxy = (exnze + wynze) / 2.0;
    } else {
        exnze = 1.0;
        wynze = 1.0;
        znxy = 1.0;
    }
    // optionally * 0.5;
    double lattd = pAmount;
    int useNode = 0;
    int rchoice = (int) trunc(pContext.random() * 8.0);
    useNode = rchoice;
    if (useNode == 0) {
        pVarTP.x = (pVarTP.x + pAffineTP.x) * fill * exnze + lattd;
        pVarTP.y = (pVarTP.y + pAffineTP.y) * fill * wynze + lattd;
        pVarTP.z = (pVarTP.z + pAffineTP.z) * fill * znxy + lattd;
    } else if (useNode == 1) {
        pVarTP.x = (pVarTP.x + pAffineTP.x) * fill * exnze + lattd;
        pVarTP.y = (pVarTP.y + pAffineTP.y) * fill * wynze - lattd;
        pVarTP.z = (pVarTP.z + pAffineTP.z) * fill * znxy + lattd;
    } else if (useNode == 2) {
        pVarTP.x = (pVarTP.x + pAffineTP.x) * fill * exnze + lattd;
        pVarTP.y = (pVarTP.y + pAffineTP.y) * fill * wynze + lattd;
        pVarTP.z = (pVarTP.z + pAffineTP.z) * fill * znxy - lattd;
    } else if (useNode == 3) {
        pVarTP.x = (pVarTP.x + pAffineTP.x) * fill * exnze + lattd;
        pVarTP.y = (pVarTP.y + pAffineTP.y) * fill * wynze - lattd;
        pVarTP.z = (pVarTP.z + pAffineTP.z) * fill * znxy - lattd;
    } else if (useNode == 4) {
        pVarTP.x = (pVarTP.x + pAffineTP.x) * fill * exnze - lattd;
        pVarTP.y = (pVarTP.y + pAffineTP.y) * fill * wynze + lattd;
        pVarTP.z = (pVarTP.z + pAffineTP.z) * fill * znxy + lattd;
    } else if (useNode == 5) {
        pVarTP.x = (pVarTP.x + pAffineTP.x) * fill * exnze - lattd;
        pVarTP.y = (pVarTP.y + pAffineTP.y) * fill * wynze - lattd;
        pVarTP.z = (pVarTP.z + pAffineTP.z) * fill * znxy + lattd;
    } else if (useNode == 6) {
        pVarTP.x = (pVarTP.x + pAffineTP.x) * fill * exnze - lattd;
        pVarTP.y = (pVarTP.y + pAffineTP.y) * fill * wynze + lattd;
        pVarTP.z = (pVarTP.z + pAffineTP.z) * fill * znxy - lattd;
    } else if (useNode == 7) {
        pVarTP.x = (pVarTP.x + pAffineTP.x) * fill * exnze - lattd;
        pVarTP.y = (pVarTP.y + pAffineTP.y) * fill * wynze - lattd;
        pVarTP.z = (pVarTP.z + pAffineTP.z) * fill * znxy - lattd;
    }
}
Also used : XYZPoint(org.jwildfire.create.tina.base.XYZPoint)

Example 67 with XYZPoint

use of org.jwildfire.create.tina.base.XYZPoint in project JWildfire by thargor6.

the class Grid3DWFFunc method transform.

@Override
public void transform(FlameTransformationContext pContext, XForm pXForm, XYZPoint pAffineTP, XYZPoint pVarTP, double pAmount) {
    /* grid3d_wf by thargor6, inspired by dc_cube by Xyrus02 */
    int cxn = (int) (pAffineTP.x / size);
    int cyn = (int) (pAffineTP.y / size);
    int czn = (int) (pAffineTP.z / size);
    double sizeSpread = getSizeSpread(cxn, cyn, czn);
    double sizescl = spacing * (size + sizeSpread * 0.1);
    double dx = 0.0, dy = 0.0, dz = 0.0;
    switch(pContext.random(3)) {
        case 0:
            boolean left = pContext.random() < 0.5;
            dx = sizescl * (left ? -0.5 : 0.5);
            dy = sizescl * (pContext.random() - 0.5);
            dz = sizescl * (pContext.random() - 0.5);
            pVarTP.color = left ? c1 : c2;
            break;
        case 1:
            boolean top = pContext.random() < 0.5;
            dx = sizescl * (pContext.random() - 0.5);
            dy = sizescl * (top ? -0.5 : 0.5);
            dz = sizescl * (pContext.random() - 0.5);
            pVarTP.color = top ? c3 : c4;
            break;
        case 2:
            boolean front = pContext.random() < 0.5;
            dx = sizescl * (pContext.random() - 0.5);
            dy = sizescl * (pContext.random() - 0.5);
            dz = sizescl * (front ? -0.5 : 0.5);
            pVarTP.color = front ? c5 : c6;
            break;
        default:
            // nothing to do
            break;
    }
    if (doRotate) {
        double a = alpha + getAlphaSpread(cxn, cyn, czn);
        double b = beta + getBetaSpread(cxn, cyn, czn);
        double g = gamma + getGammaSpread(cxn, cyn, czn);
        double sina = sin(a);
        double cosa = cos(a);
        double sinb = sin(b);
        double cosb = cos(b);
        double sing = sin(g);
        double cosg = cos(g);
        double dxr = dx * (cosb * cosg) + dy * (cosg * sina * sinb - cosa * sing) + dz * (cosa * cosg * sinb + sina * sing);
        double dyr = dx * (cosb * sing) + dy * (cosa * cosg + sina * sinb * sing) + dz * (-cosg * sina + cosa * sinb * sing);
        double dzr = dx * (-sinb) + dy * (cosb * sina) + dz * (cosa * cosb);
        dx = dxr;
        dy = dyr;
        dz = dzr;
    }
    pVarTP.x += cxn * size + dx;
    pVarTP.y += cyn * size + dy;
    pVarTP.z += czn * size + dz;
}
Also used : XYZPoint(org.jwildfire.create.tina.base.XYZPoint)

Example 68 with XYZPoint

use of org.jwildfire.create.tina.base.XYZPoint in project JWildfire by thargor6.

the class MandelbrotFunc method transform.

@Override
public void transform(FlameTransformationContext pContext, XForm pXForm, XYZPoint pAffineTP, XYZPoint pVarTP, double pAmount) {
    double x1 = _x0;
    double x = _x0;
    double y1 = _y0;
    double y = _y0;
    int currIter;
    boolean inverted = randGen.random() < this.invert;
    if (inverted) {
        currIter = 0;
    } else {
        currIter = iter;
    }
    while ((inverted && (currIter < iter)) || ((!inverted) && ((currIter >= iter) || ((skin < 1) && (currIter < 0.1 * iter * (1 - skin)))))) {
        if ((_x0 == 0) && (_y0 == 0)) {
            // Choose a point at random
            if (max_points > 0) {
                if (xP.size() >= max_points) {
                    _x0 = xP.get(_pIdx);
                    _y0 = yP.get(_pIdx);
                    _z0 = zP.get(_pIdx++);
                    if (_pIdx >= max_points) {
                        _pIdx = 0;
                    }
                } else {
                    _x0 = (xmax - xmin) * randGen.random() + xmin;
                    _y0 = (ymax - ymin) * randGen.random() + ymin;
                    _z0 = randGen.random() * rnd_z_range;
                    xP.add(_x0);
                    yP.add(_y0);
                    zP.add(_z0);
                }
            } else {
                _x0 = (xmax - xmin) * randGen.random() + xmin;
                _y0 = (ymax - ymin) * randGen.random() + ymin;
                _z0 = randGen.random() * rnd_z_range;
            }
        } else {
            // Choose a point close to previous point
            _x0 = (skin + 0.001) * (randGen.random() - 0.5) + _x0;
            _y0 = (skin + 0.001) * (randGen.random() - 0.5) + _y0;
        // _z0 = (skin + 0.001) * (randGen.random() - 0.5) + _z0;
        }
        x1 = _x0;
        y1 = _y0;
        x = _x0;
        y = _y0;
        currIter = 0;
        while (((x * x + y * y < 2 * 2) && (currIter < iter))) {
            double xtemp = x * x - y * y + _x0;
            y = 2.0 * x * y + _y0;
            x = xtemp;
            currIter++;
        }
        if ((currIter >= iter) || (skin == 1) || (currIter < 0.1 * (iter * (1 - skin)))) {
            // Random point next time
            _x0 = 0;
            _y0 = 0;
        }
    }
    // + FTx^;
    pVarTP.x += pAmount * (x1 + cx * x);
    // + FTy^;
    pVarTP.y += pAmount * (y1 + cy * y);
    pVarTP.z += _z0;
}
Also used : XYZPoint(org.jwildfire.create.tina.base.XYZPoint)

Example 69 with XYZPoint

use of org.jwildfire.create.tina.base.XYZPoint in project JWildfire by thargor6.

the class MaurerLinesFunc method transform.

@Override
public void transform(FlameTransformationContext pContext, XForm pXForm, XYZPoint pAffineTP, XYZPoint pVarTP, double pAmount) {
    count++;
    setValues();
    double xin = pAffineTP.x;
    double yin = pAffineTP.y;
    // atan2 range is [-PI, PI], so tin covers 2PI, or 1 cycle (from -0.5 to 0.5 cycle)
    double tin;
    if (randomize) {
        // tin = (Math.random() * M_2PI) - M_PI; // random angle, range [-Pi .. +Pi]
        // random angle, range [0.. +2Pi]
        tin = (Math.random() * M_2PI);
    } else {
        // polar coordinate angle (theta in radians) of incoming point [-Pi .. +Pi]
        tin = atan2(yin, xin);
    }
    // theta parameter for curve calculation
    double t = cycles * tin;
    // reusing curve_point
    // curve_point = getCurveCoords(t, curve_point);
    curve.getCurvePoint(t, curve_point);
    double x = curve_point.x;
    double y = curve_point.y;
    double r = sqrt(x * x + y * y);
    double rinx, riny;
    double rout;
    double xout = 0, yout = 0, zout = 0;
    double step_number, theta1, theta2;
    double x1, y1, x2, y2;
    // map to a Maurer Rose line
    // find nearest step
    step_number = floor(t / theta_step_radians);
    // find distance along line??
    // find radians per full line
    // double line_fraction = (t % theta_step_radians)/radians_per_full_line
    // find polar and cartesian coordinates for endpoints of Maure Rose line
    theta1 = (step_number * theta_step_radians) + initial_theta_radians;
    theta2 = theta1 + theta_step_radians;
    // reusing end_point1
    // end_point1 = getCurveCoords(theta1, end_point1);
    curve.getCurvePoint(theta1, end_point1);
    x1 = end_point1.x;
    y1 = end_point1.y;
    // reusing end_point2
    // end_point2 = getCurveCoords(theta2, end_point2);
    curve.getCurvePoint(theta2, end_point2);
    x2 = end_point2.x;
    y2 = end_point2.y;
    /* if (DEBUG_COSETS && count % 50000 == 0) {
      System.out.println("step_number: " + step_number + ", theta1: " + theta1 + ", theta2: " + theta2);
      System.out.println("point1: " + x1 + ", " + y1 + "  point2: " + x2 + ", " + y2 );
    }
    */
    // find the slope and length of the line
    double ydiff = y2 - y1;
    double xdiff = x2 - x1;
    // slope of line (m in y=mx+b line equation)
    double line_slope = ydiff / xdiff;
    // y-intercept of line (b in y=mx+b line equation)
    double line_intercept;
    // special-casing for slope being NaN (xdiff = 0) ==> line_intercept set to NaN as well
    if (Double.isNaN(line_slope)) {
        line_intercept = Double.NaN;
    } else {
        line_intercept = y1 - (line_slope * x1);
    }
    double point_angle = getPointAngle(end_point1, end_point2);
    // atan2 range is [-Pi..+Pi]
    double raw_line_angle = atan2(ydiff, xdiff);
    // if (raw_line_angle < 0) { raw_line_angle += M_2PI; }   // map to range [0..+2Pi]
    // delta_from_yaxis should be 0 if parallel to y-axis, and M_PI/2 if parallel to x-axis
    double unscaled_line_angle = abs(abs(raw_line_angle) - (M_PI / 2.0));
    // scale to range [0..1]; (0 parallel to y-axis, 1 parallel to x-axis)
    double line_angle = unscaled_line_angle / (M_PI / 2.0);
    double line_length = Math.sqrt((xdiff * xdiff) + (ydiff * ydiff));
    // yoffset = [+-] line_slope * d / (sqrt(1 + line_slope^2))
    // double xoffset=0, yoffset=0, zoffset = 0;
    double line_delta = 0;
    // use midlength of Maurer line as radius
    double midlength = line_length / 2.0;
    double xmid = (x1 + x2) / 2.0;
    double ymid = (y1 + y2) / 2.0;
    // double speed1 = curve.getSpeed(theta1, end_point1);
    boolean use_render_mode = true;
    if (show_points_param > 0 || show_curve_param > 0) {
        double rnd = pContext.random();
        /**
         *  overrides of rendering mode
         */
        double xoffset = 0, yoffset = 0, zoffset = 0;
        double rand2 = Math.random();
        if (rand2 <= show_points_param) {
            // drawing Maurer anchor points instead of specified render_mode
            use_render_mode = false;
            if (point_thickness != 0) {
                double roffset = pContext.random() * point_thickness;
                double rangle = (pContext.random() * M_2PI);
                xoffset = roffset * cos(rangle);
                yoffset = roffset * sin(rangle);
            } else {
                xoffset = 0;
                yoffset = 0;
            }
            if (rnd <= (show_points_param / 2)) {
                xout = x1 + xoffset;
                yout = y1 + yoffset;
            } else {
                xout = x2 + xoffset;
                yout = y2 + yoffset;
            }
            zout = 0;
        } else if (rand2 <= (show_points_param + show_curve_param)) {
            // drawing base curve instead of specified render_mode
            use_render_mode = false;
            if (curve_thickness != 0) {
                xout = x + ((pContext.random() - 0.5) * curve_thickness);
                yout = y + ((pContext.random() - 0.5) * curve_thickness);
            } else {
                xout = x;
                yout = y;
            }
            zout = 0;
        } else {
            use_render_mode = true;
            xout = 0;
            yout = 0;
        }
    }
    if (use_render_mode) {
        double xoffset = 0, yoffset = 0, zoffset = 0;
        // pick a random point along the Maurer line specified by P1 and P2 endpoints
        // t1 ranges from [0..1] as line ranges from P1..P2
        // calculate the corresponding point PML along the Maurer line
        double t1 = Math.random();
        mpoint.x = (x1 * (1 - t1)) + (x2 * t1);
        // same as mpoint.x = x1 + (t1 * (x2-x1));
        mpoint.y = (y1 * (1 - t1)) + (y2 * t1);
        // same as mpoint.y = y1 + (t1 * (y2-y1));
        line_delta = t1 * line_length;
        /**
         *   RENDER MODES
         */
        if (render_mode == LINES || render_mode == DEFAULT) {
            // draw lines
            xout = mpoint.x;
            yout = mpoint.y;
        } else if (render_mode == CIRCLES) {
            // and use midlength of Maurer line as radius
            if (render_submode == Z_STROKE) {
                double ang = t1 * M_2PI;
                double rad = midlength;
                double mid_delta = rad * cos(ang);
                zout = rad * sin(ang);
                // move along line, out from midpoint by mid_delta
                xout = xmid + (mid_delta * cos(raw_line_angle));
                yout = ymid + (mid_delta * sin(raw_line_angle));
                line_delta = sqrt(((x1 - xout) * (x1 - xout)) + ((y1 - yout) * (y1 - yout)));
            } else {
                double ang = t1 * M_2PI;
                double rad = midlength;
                xout = xmid + (rad * sin(ang));
                yout = ymid + (rad * cos(ang));
                // line_delta = ang;
                line_delta = sqrt(((x1 - xout) * (x1 - xout)) + ((y1 - yout) * (y1 - yout)));
            }
        } else if (render_mode == QUADRATIC_BEZIER) {
            // use origin (0,0) as control point, and endpoints of Maurer line as Bezier curve endpoints
            // (since using origin as control point, can drop middle term of standard Bezier curve calc
            // double bt = Math.random();  // want bt => [0:1]
            double bt = t1;
            double ax, ay;
            // use full formula with control point
            if (render_modifier1 != 1 || render_modifier2 != 1) {
                // render_modifier1 controls relative radius of control point
                // (fraction of distance from midpoint of line to origin,
                // so as render_modifier range: [0=>1] then control point radius range: [midpoint_radius=>0]
                // render_modifier2 controls relative angle of control point
                // (angle from midpoint-origin line to control point)
                // so if render_modifier = 1 then control point angle delta = 0
                double midpoint_radius = sqrt(xmid * xmid + ymid * ymid);
                double midpoint_angle = atan2(ymid, xmid);
                double cradius = (1 - render_modifier1) * midpoint_radius;
                double cangle = midpoint_angle + (M_2PI - (render_modifier2 * M_2PI));
                // double cangle = midpoint_angle;
                double cx = cradius * cos(cangle);
                double cy = cradius * sin(cangle);
                ax = ((1 - bt) * (1 - bt) * x1) + (2 * (1 - bt) * bt * cx) + (bt * bt * x2);
                ay = ((1 - bt) * (1 - bt) * y1) + (2 * (1 - bt) * bt * cy) + (bt * bt * y2);
            } else {
                // control point is origin (0,0), so can drop control point term of quadratic Bezier
                // and therefore skip midradius, midangle etc. control point calcs
                ax = ((1 - bt) * (1 - bt) * x1) + (bt * bt * x2);
                ay = ((1 - bt) * (1 - bt) * y1) + (bt * bt * y2);
            }
            // for now working on Z submodes here, but handling most submodes at end of transform() method...
            if (render_submode == Z_STROKE) {
                // not really working
                zout = ay;
                xoffset = line_delta / Math.sqrt(1 + line_slope * line_slope);
                // determine sign based on p2
                if (x2 < x1) {
                    xoffset = -1 * xoffset;
                }
                yoffset = Math.abs(line_slope * xoffset);
                if (y2 < y1) {
                    yoffset = -1 * yoffset;
                }
                xout = x1 + xoffset;
                yout = y1 + yoffset;
            } else // hijacking Z_STROKE_BOTH while trying different approaches for Z-Beziers
            if (render_submode == Z_BOTH_STROKE) {
                // xout = x1 + (line_delta * cos(raw_line_angle));
                // yout = y1 + (line_delta * sin(raw_line_angle));
                // unrotate:
                // oR.x = oP.x + (o.x - oP.x) * cos(theta) - (o.y - oP.y) * sin(theta)
                // oR.y = oP.y + (o.x - oP.x) * sin(theta) + (o.y - oP.y) * cos(theta)
                /* double newx = x1 + ((ax - x1) * cos(-raw_line_angle)) - ((ay - y1) * sin(-raw_line_angle));
          double newy = y1 + ((ax - x1) * sin(-raw_line_angle)) + ((ay - y1) * cos(-raw_line_angle));
          xout = ax;
          yout = (line_slope * xout) + line_intercept;
          zout = newy - y1;
          */
                // xoffset = line_delta / Math.sqrt(1 + line_slope*line_slope);
                // if (x2 < x1) { xoffset = -1 * xoffset; }  // determine sign based on p2
                // yoffset = Math.abs(line_slope * xoffset);
                // if (y2 < y1) { yoffset = -1 * yoffset; }
                xout = ax;
                // yout = (line_slope * xout) + y1;
                yout = (line_slope * xout) + line_intercept;
                // yout = y1 + ()
                zout = ((ax - x1) * sin(-raw_line_angle)) + ((ay - y1) * cos(-raw_line_angle));
            } else {
                xout = ax;
                yout = ay;
            }
        } else if (render_mode == ELLIPSES) {
            if (render_submode == Z_STROKE) {
                // ang ==> [-Pi : +Pi]  or [ 0 : 2Pi ] ?
                // double  ang = (Math.random() * M_2PI) - M_PI;  // ==> [ -Pi : +Pi ]
                // double ang = (Math.random() * M_2PI);   // ==> [ 0 : 2Pi ]
                double ang = t1 * M_2PI;
                // offset along line (relative to start of line)
                // if render_modifier1 == 1, then ranges are
                // delta_from_midlength ==> [-midlength : +midlength]
                // delta_from_start ==> [0 : line_length]
                double delta_from_midlength = (midlength * render_modifier1) * cos(ang);
                double delta_from_start = midlength - delta_from_midlength;
                line_delta = delta_from_start;
                // offset perpendicular to line:
                // shift angle by -pi/2 get range=>[-1:1] as line_ofset=>[0=>line_length],
                // then adding 1 to gets range=>[0:2],
                // then scaling by line_length/2 * amplitude gets perp_offset: [0:(line_length*amplitude)]
                double relative_perp_offset = (midlength * (render_modifier2 / 2)) * sin(ang);
                // shift to make offsets relative to start point (x1, y1)
                double line_offset = delta_from_midlength + x1 - midlength;
                double perp_offset = relative_perp_offset + y1;
                // then consider (line_offset, perp_offset) as point and rotate around start point (x1, y1) ?
                // should already have angle (raw_line_angle)
                // 2D rotation transformation of point B about a given fixed point A to give point C
                // C.x = A.x + (B.x - A.x) * cos(theta) - (B.y - A.y) * sin(theta)
                // C.y = A.y + (B.x - A.x) * sin(theta) + (B.y - A.y) * cos(theta)
                // drop last term for xout and yout since y offset (perp_offset) = y1  [[ or B.y = A.y in above equation ]
                // double newx = x1 + ((line_offset - x1) * cos(raw_line_angle)) - ((perp_offset - y1) * sin(raw_line_angle));
                // double newy = y1 + ((line_offset - x1) * sin(raw_line_angle)) + ((perp_offset - y1) * cos(raw_line_angle));
                xout = x1 + ((line_offset - x1) * cos(raw_line_angle + M_PI));
                // drop last term since y offset (perp_offset) = 0
                yout = y1 + ((line_offset - x1) * sin(raw_line_angle + M_PI));
                zout = relative_perp_offset;
            } else {
                // double ang = Math.random() * M_2PI;
                double ang = t1 * M_2PI;
                // double ang = (Math.random() * M_2PI) - M_PI;
                // offset along line (relative to start of line)
                double relative_line_offset = (midlength * render_modifier1) * cos(ang);
                line_delta = midlength * cos(ang);
                // offset perpendicular to line:
                // shift angle by -pi/2 get range=>[-1:1] as line_ofset=>[0=>line_length],
                // then adding 1 to gets range=>[0:2],
                // then scaling by line_length/2 * amplitude gets perp_offset: [0:(line_length*amplitude)]
                double relative_perp_offset = (midlength * (render_modifier2 / 2)) * sin(ang);
                // double relative_perp_offset = midlength * sin(ang);
                // shift to make offsets relative to start point (x1, y1)
                double line_offset = relative_line_offset + x1 - midlength;
                double perp_offset = relative_perp_offset + y1;
                // then consider (line_offset, perp_offset) as point and rotate around start point (x1, y1) ?
                // should already have angle (raw_line_angle)
                // 2D rotation transformation of point B about a given fixed point A to give point C
                // C.x = A.x + (B.x - A.x) * cos(theta) - (B.y - A.y) * sin(theta)
                // C.y = A.y + (B.x - A.x) * sin(theta) + (B.y - A.y) * cos(theta)
                // double newx = x1 + ((line_offset - x1) * cos(raw_line_angle)) - ((perp_offset - y1) * sin(raw_line_angle));
                // double newy = y1 + ((line_offset - x1) * sin(raw_line_angle)) + ((perp_offset - y1) * cos(raw_line_angle));
                double newx = x1 + ((line_offset - x1) * cos(raw_line_angle + M_PI)) - ((perp_offset - y1) * sin(raw_line_angle + M_PI));
                double newy = y1 + ((line_offset - x1) * sin(raw_line_angle + M_PI)) + ((perp_offset - y1) * cos(raw_line_angle + M_PI));
                xout = newx;
                yout = newy;
            // xout = line_offset;
            // yout = perp_offset;
            }
        } else if (render_mode == SINE_WAVES) {
            // amplitude calculated such that when render_modifier = 1, relative_perp_offset range: [0 ==> line_length/2]
            double amplitude = (line_length / 4) * render_modifier1;
            double frequency = render_modifier2;
            // range of [0 -> 2Pi ]
            // double ang = Math.random() * M_2PI;
            double ang = t1 * M_2PI;
            // offset along line (relative to start of line) ==> [0=>line_length]
            double relative_line_offset = ang * (line_length / M_2PI);
            line_delta = relative_line_offset;
            // offset perpendicular to line:
            // shift angle by -pi to get cos range=>[-1:1] as ang => [0:2Pi],
            // find perp offset for endpoints:
            // since midpoint is always at middle of range (0), and using cos,
            // endpoints should have same offset, so use either one
            // endpoints are at [+/-](Pi*frequency)
            // so at amp=1, freq=1, ==> -1
            double endpoint_perp_offset = amplitude * cos(M_PI * frequency);
            double relative_perp_offset = amplitude * (cos((ang - M_PI) * frequency));
            // adjust relative_per_offset so endpoints are at 0
            relative_perp_offset = relative_perp_offset - endpoint_perp_offset;
            // shift to make offsets relative to start point (x1, y1)
            double line_offset = relative_line_offset + x1;
            double perp_offset = relative_perp_offset + y1;
            // then consider (line_offset, perp_offset) as point and rotate around start point (x1, y1) ?
            // should already have angle (raw_line_angle)
            // 2D rotation about a given point
            // oR.x = oP.x + (o.x - oP.x) * cos(theta) - (o.y - oP.y) * sin(theta)
            // oR.y = oP.y + (o.x - oP.x) * sin(theta) + (o.y - oP.y) * cos(theta)
            double newx = x1 + ((line_offset - x1) * cos(raw_line_angle)) - ((perp_offset - y1) * sin(raw_line_angle));
            double newy = y1 + ((line_offset - x1) * sin(raw_line_angle)) + ((perp_offset - y1) * cos(raw_line_angle));
            // for now working on Z submodes here, but handling most submodes at end of transform() method...
            if (render_submode == Z_STROKE) {
                xout = x1 + (relative_line_offset * cos(raw_line_angle));
                // can save a sin() call by using slope-intercept once have xout
                // yout = y1 + (relative_line_offset * sin(raw_line_angle));
                yout = (line_slope * xout) + line_intercept;
                // plus z1?
                zout = relative_perp_offset;
            } else if (render_submode == Z_BOTH_STROKE) {
                xout = x1 + (relative_line_offset * cos(raw_line_angle));
                // yout = y1 + (relative_line_offset * sin(raw_line_angle));
                yout = (line_slope * xout) + line_intercept;
                if (Math.random() < 0.5) {
                    // plus z1?
                    zout = relative_perp_offset;
                } else {
                    zout = -relative_perp_offset;
                }
            } else {
                xout = newx;
                yout = newy;
            }
        } else if (render_mode == SEQUIN_CIRCLE_SPLINE) {
            // Circle Splines with angle-based trionometric interpolation
            // attempting to implement strategy described by Sequin, Lee, Yen in paper
            // "Fair, G2- and C2-continuous circle splines for the interpolation of sparse data points", 2005
            // Want to calculate interpolated point P(u) between P1 and P2, where u:[0=>1]
            // Need P0, P1, P2, P3
            // already have P1, P2, endpoints of current Maurer line
            // calculate P0 and P3, from t=(theta1-theta_offset) and t=(theta2+that_offset) respectively
            double theta0 = theta1 - this.theta_step_radians;
            double theta3 = theta2 + this.theta_step_radians;
            curve.getCurvePoint(theta0, end_point0);
            curve.getCurvePoint(theta3, end_point3);
            double x0 = end_point0.x;
            double y0 = end_point0.y;
            double x3 = end_point3.x;
            double y3 = end_point3.y;
            // then calculate unit direction vectors:
            // A = (P1-P0)/|P1-P0|
            // B = (P2-P1)/|P2-P1|
            // C = (P2-P0)/|P2-P0|
            // D = (P3-P2)/|P3-P2|
            // E = (P3-P1)/|P3-P1|
            // where |Pk - Pj| = norm(Pk-Pi) = sqrt((xk-xi)^2 + (yk-yi)^2)
            double norm10 = sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0));
            double norm21 = sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
            double norm20 = sqrt((x2 - x0) * (x2 - x0) + (y2 - y0) * (y2 - y0));
            double norm32 = sqrt((x3 - x2) * (x3 - x2) + (y3 - y2) * (y3 - y2));
            double norm31 = sqrt((x3 - x1) * (x3 - x1) + (y3 - y1) * (y3 - y1));
            vecA.x = (x1 - x0) / norm10;
            vecA.y = (y1 - y0) / norm10;
            vecB.x = (x2 - x1) / norm21;
            vecB.y = (y2 - y1) / norm21;
            vecC.x = (x2 - x0) / norm20;
            vecC.y = (y2 - y0) / norm20;
            vecD.x = (x3 - x2) / norm32;
            vecD.y = (y3 - y2) / norm32;
            vecE.x = (x3 - x1) / norm31;
            vecE.y = (y3 - y1) / norm31;
            // calculate tangent angles at P1 and P2:
            // (hmm, wonder if could substitute exact tangent calculation for base curve here?? not sure if it's the same angle though...)
            // a1 = arccos(A @ C) ==>  @ is vector dot product
            // a2 = arccos(E @ D)
            double a1 = Math.acos((vecA.x * vecC.x) + (vecA.y * vecC.y));
            double a2 = Math.acos((vecE.x * vecD.x) + (vecE.y * vecD.y));
            // trigonometric angle blending function between P0 and P1:
            // a(u) = (a1 * cos^2(u*Pi/2)) + (a2 * sin^2(u*Pi/2))
            // already have t1:[0=>1]
            double u = t1;
            double cu = cos(u * M_PI / 2);
            double su = sin(u * M_PI / 2);
            double au = (a1 * cu * cu) + (a2 * su * su);
            // now can calculate distance of P(u), d(P(u)) from P1:
            // b = |P2-P1|  ==> should already have this as line length for current Maurer line
            // d(P(u)) = b * sin(u * a(u)) / sin(a(u))
            // same as line_length?
            double bp = norm21;
            double pd = bp * sin(u * au) / sin(au);
            // and deviation angle from Maurer line is
            // dangle(u) = (1-u) * a(u)
            double pa = (1 - u) * au;
            // ???
            pa = -pa;
            // then place point along Maurer line P1P2 at distance d(P(u)) from endpoint P1,
            // http://math.stackexchange.com/questions/409689/how-do-i-find-a-point-a-given-distance-from-another-point-along-a-line
            double linex = x1 + ((x2 - x1) * pd / norm21);
            double liney = y1 + ((y2 - y1) * pd / norm21);
            // and rotate by deviation angle dangle(u) around P1
            // to get final position position of P(u)
            // 2D rotation transformation of point B about a given fixed point A to give point C
            // C.x = A.x + (B.x - A.x) * cos(theta) - (B.y - A.y) * sin(theta)
            // C.y = A.y + (B.x - A.x) * sin(theta) + (B.y - A.y) * cos(theta)
            xout = x1 + ((linex - x1) * cos(pa)) - ((liney - y1) * sin(pa));
            yout = y1 + ((linex - x1) * sin(pa)) + ((liney - y1) * cos(pa));
        } else if (render_mode == CARDINAL_SPLINE || render_mode == UNIFORM_CATMULL_ROM_SPLINE || render_mode == CARDINAL_SPLINE_SWIZZLE1 || render_mode == CARDINAL_SPLINE_SWIZZLE2 || render_mode == CARDINAL_SPLINE_SWIZZLE3) {
            // cobbled together from:
            // https://en.wikipedia.org/wiki/Cubic_Hermite_spline#Catmull.E2.80.93Rom_spline
            // http://paulbourke.net/miscellaneous/interpolation/
            // http://cubic.org/docs/hermite.htm
            // 
            // Hermite Basis Functions:
            // range from [0:1] along [0..line_length]
            // already have t1, randome [0:1]
            double t2 = t1 * t1;
            double t3 = t1 * t1 * t1;
            double h00 = 2 * t3 - 3 * t2 + 1;
            double h10 = t3 - 2 * t2 + t1;
            double h01 = -2 * t3 + 3 * t2;
            double h11 = t3 - t2;
            // have points p1 and p2, get p0 and p3 (previous point and next point)
            double theta0 = theta1 - theta_step_radians;
            double theta3 = theta2 + theta_step_radians;
            curve.getCurvePoint(theta0, end_point0);
            curve.getCurvePoint(theta3, end_point3);
            double x0, y0, x3, y3;
            if (render_mode == CARDINAL_SPLINE_SWIZZLE1) {
                x0 = end_point0.y;
                y0 = end_point0.x;
                x3 = end_point3.y;
                y3 = end_point3.x;
            } else if (render_mode == CARDINAL_SPLINE_SWIZZLE2) {
                x0 = end_point3.x;
                y0 = end_point3.y;
                x3 = end_point0.x;
                y3 = end_point0.y;
            } else if (render_mode == CARDINAL_SPLINE_SWIZZLE3) {
                x0 = end_point3.y;
                y0 = end_point3.x;
                x3 = end_point0.y;
                y3 = end_point0.x;
            } else {
                // render_mode == CARDINAL_SPLINE or UNIFORM_CATMULL_ROM_SPLINE
                x0 = end_point0.x;
                y0 = end_point0.y;
                x3 = end_point3.x;
                y3 = end_point3.y;
            }
            // m1 ==> cardinal spline calculated tangent for end_point1
            // m2 ==> cardinal spline calculated tangent for end_point1
            // M1 = (P2 - P0) / (t2 - t0)
            // M2 = (P3 - P1) / (t3 - t1)
            // for now assuming t(i+1) = t(i) + 1, or in other words (t2-t0) = 2 and (t3-t1) = 2
            // (I think this is a standard assumption for baseline Catmull-Rom, see for example http://cubic.org/docs/hermite.htm)
            // so
            // M1 = (P2 - PO)/2
            // M2 = (P3 - P1)/2
            double m1x = 0.5 * (x2 - x0);
            double m2x = 0.5 * (x3 - x1);
            double m1y = 0.5 * (y2 - y0);
            double m2y = 0.5 * (y3 - y1);
            // also trying addition of a tightness parameter
            // for the case where tanscale = 1 (render_modifier1 = 0),
            // get the uniform Catmull-Rom spline as a special case of cardinal splines
            // for the case where transcale = 0 (render_modifier1 = 1), tangents have no effect
            double tanscale;
            if (render_mode == UNIFORM_CATMULL_ROM_SPLINE) {
                tanscale = 1;
            } else {
                tanscale = (1 - render_modifier1);
            }
            m1x = m1x * tanscale;
            m1y = m1y * tanscale;
            m2x = m2x * tanscale;
            m2y = m2y * tanscale;
            double xnew = (h00 * x1) + (h10 * m1x) + (h01 * x2) + (h11 * m2x);
            double ynew = (h00 * y1) + (h10 * m1y) + (h01 * y2) + (h11 * m2y);
            // line_delta = t1 * line_length;
            xout = xnew;
            yout = ynew;
        } else if (render_mode == NONUNIFORM_CATMULL_ROM_SPLINE || render_mode == CENTRIPETAL_CATMULL_ROM_SPLINE || render_mode == CHORDAL_CATMULL_ROM_SPLINE) {
            // cobbled together from:
            // https://en.wikipedia.org/wiki/Centripetal_Catmull%E2%80%93Rom_spline
            // https://en.wikipedia.org/wiki/Cubic_Hermite_spline#Catmull.E2.80.93Rom_spline
            // most helpful for actual implementation:
            // http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/19283471#19283471
            // 
            // Original Paper: "A Recursive Evaluation Algorithm for a Class of Catmull-Rom Splines", Barry and Goldman
            // Hermite Basis Functions:
            // range from [0:1] along [0..line_length]
            // already have t1, random [0:1]
            double t2 = t1 * t1;
            double t3 = t1 * t1 * t1;
            double h00 = 2 * t3 - 3 * t2 + 1;
            double h10 = t3 - 2 * t2 + t1;
            double h01 = -2 * t3 + 3 * t2;
            double h11 = t3 - t2;
            // have points p1 and p2, get p0 and p3 (previous point and next point)
            double theta0 = theta1 - theta_step_radians;
            double theta3 = theta2 + theta_step_radians;
            curve.getCurvePoint(theta0, end_point0);
            curve.getCurvePoint(theta3, end_point3);
            double x0 = end_point0.x;
            double y0 = end_point0.y;
            double x3 = end_point3.x;
            double y3 = end_point3.y;
            double alpha = 0;
            if (render_mode == CENTRIPETAL_CATMULL_ROM_SPLINE) {
                alpha = 0.5;
            } else if (render_mode == CHORDAL_CATMULL_ROM_SPLINE) {
                alpha = 1.0;
            } else {
                // render_mode == NONUNIFORM_CATMULL_ROM_SPLINE
                // nonuniform Catmull-Rom spline, with special cases:
                // rm = 0 ==> uniform Catmull-Rom spline (essentially same as cardinal spline)
                // rm = 0.5 ==> centripetal Catmull-Rom spline
                // rm = 1.0 ==> chordal Catmull-Rom spline
                alpha = render_modifier2;
            }
            // centripetal/chordal Catmull-Rom tangent caclulations
            // M1 = (P1 - P0) / (t1 - t0) - (P2 - P0) / (t2 - t0) + (P2 - P1) / (t2 - t1)
            // M2 = (P2 - P1) / (t2 - t1) - (P3 - P1) / (t3 - t1) + (P3 - P2) / (t3 - t2)
            // and t[i+1] = (((x[i+1]-x[i])^2 + (y[i+1]-y[i])^2)^0.5)^a + t[i]
            // t[i+1] = (((x[i+1]-x[i])^2 + (y[i+1]-y[i])^2))^(a/2) + t[i]
            // rewrite in terms of deltas, then can drop the last term
            // dt0 = t1-t0 = (((x1-x0)^2 + (y1-y0)^2)) ^(a/2)
            // dt1 = t2-t1
            // dt2 = t3-t2
            // M1 = ((P1-P0)/dt0) - ((P2-P0)/(dt0+dt1)) + ((P2-P1)/dt1)
            // M2 = ((P2-P1)/dt1) - ((P3-P1)/(dt1+dt2)) + ((P3-P2)/dt2)
            // also need to rescale by dt1 (so can parameterize t from [0:1]) ??
            // do scaling when combining terms?
            double dt0 = Math.pow(((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)), (alpha / 2));
            double dt1 = Math.pow(((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)), (alpha / 2));
            double dt2 = Math.pow(((x3 - x2) * (x3 - x2) + (y3 - y2) * (y3 - y2)), (alpha / 2));
            double m1x = ((x1 - x0) / dt0) - ((x2 - x0) / (dt0 + dt1)) + ((x2 - x1) / dt1);
            double m1y = ((y1 - y0) / dt0) - ((y2 - y0) / (dt0 + dt1)) + ((y2 - y1) / dt1);
            double m2x = ((x2 - x1) / dt1) - ((x3 - x1) / (dt1 + dt2)) + ((x3 - x2) / dt2);
            double m2y = ((y2 - y1) / dt1) - ((y3 - y1) / (dt1 + dt2)) + ((y3 - y2) / dt2);
            // scaling tangents (analagous to normalizing to the unit tangent?)
            // double tanscale = dt1;
            // also trying addition of a tightness parameter
            double tanscale = dt1 * (1 - render_modifier1);
            m1x = m1x * tanscale;
            m1y = m1y * tanscale;
            m2x = m2x * tanscale;
            m2y = m2y * tanscale;
            double xnew = (h00 * x1) + (h10 * m1x) + (h01 * x2) + (h11 * m2x);
            double ynew = (h00 * y1) + (h10 * m1y) + (h01 * y2) + (h11 * m2y);
            // line_delta = t1 * line_length;
            xout = xnew;
            yout = ynew;
        } else if (render_mode == KOCHANEK_BARTELS_SPLINE || render_mode == KOCHANEK_BARTELS_SPLINE_SWIZZLE1 || render_mode == KOCHANEK_BARTELS_SPLINE_SWIZZLE2 || render_mode == KOCHANEK_BARTELS_SPLINE_SWIZZLE3) {
            // cobbled together from:
            // Online:
            // https://en.wikipedia.org/wiki/Kochanek%E2%80%93Bartels_spline
            // https://en.wikipedia.org/wiki/Cubic_Hermite_spline#Catmull.E2.80.93Rom_spline
            // http://paulbourke.net/miscellaneous/interpolation/
            // http://cubic.org/docs/hermite.htm
            // Original Paper: "Interpolating splines with local tension, continuity, and bias control", Kochanek & Bartels
            // Reference Book: "Curves and Surfaces for Computer Graphics", David Saloman
            // GDC2012 Presentation: "Interpolation and Splines", Squirrel Eiserloh
            // 
            // Hermite Basis Functions:
            // range from [0:1] along [0..line_length]
            // double t1 = Math.random();
            double t2 = t1 * t1;
            double t3 = t1 * t1 * t1;
            double h00 = 2 * t3 - 3 * t2 + 1;
            double h10 = t3 - 2 * t2 + t1;
            double h01 = -2 * t3 + 3 * t2;
            double h11 = t3 - t2;
            // have points p1 and p2, get p0 and p3 (previous point and next point)
            double theta0 = theta1 - theta_step_radians;
            double theta3 = theta2 + theta_step_radians;
            curve.getCurvePoint(theta0, end_point0);
            curve.getCurvePoint(theta3, end_point3);
            double x0, y0, x3, y3;
            if (render_mode == KOCHANEK_BARTELS_SPLINE_SWIZZLE1) {
                x0 = end_point0.y;
                y0 = end_point0.x;
                x3 = end_point3.y;
                y3 = end_point3.x;
            } else if (render_mode == KOCHANEK_BARTELS_SPLINE_SWIZZLE2) {
                x0 = end_point3.x;
                y0 = end_point3.y;
                x3 = end_point0.x;
                y3 = end_point0.y;
            } else if (render_mode == KOCHANEK_BARTELS_SPLINE_SWIZZLE3) {
                x0 = end_point3.y;
                y0 = end_point3.x;
                x3 = end_point0.y;
                y3 = end_point0.x;
            } else {
                // KOCHANEK_BARTELS_SPLINE
                x0 = end_point0.x;
                y0 = end_point0.y;
                x3 = end_point3.x;
                y3 = end_point3.y;
            }
            // but want defaults for tension, bias, continuity to be 0?
            double tension = render_modifier1;
            double continuity = render_modifier2;
            double bias = render_modifier3;
            // m2 ==> KB-calculated tangent for end_point2
            double c110 = (1 - tension) * (1 + bias) * (1 + continuity) / 2;
            double c121 = (1 - tension) * (1 - bias) * (1 - continuity) / 2;
            double c221 = (1 - tension) * (1 + bias) * (1 - continuity) / 2;
            double c232 = (1 - tension) * (1 - bias) * (1 + continuity) / 2;
            // m1 ==> KB-calculated tangent for end_point1
            // m2 ==> KB-calculated tangent for end_point1
            // note that when tension = bias = continuity = 0, all coefficients become 0.5,
            // and thus this reduces to Catmull-Rom spline, for example:
            // m1x = (c110 * (x1-x0)) + (c121 * (x2-x1))
            // = (0.5 * (x1-x0)) + (0.5 *(x2-x1))
            // =  0.5 * (x1 - x0 + x2 - x1)
            // =  0.5 * (x2 - x1)
            // 
            double m1x = (c110 * (x1 - x0)) + (c121 * (x2 - x1));
            double m2x = (c221 * (x2 - x1)) + (c232 * (x3 - x2));
            double m1y = (c110 * (y1 - y0)) + (c121 * (y2 - y1));
            double m2y = (c221 * (y2 - y1)) + (c232 * (y3 - y2));
            double xnew = (h00 * x1) + (h10 * m1x) + (h01 * x2) + (h11 * m2x);
            double ynew = (h00 * y1) + (h10 * m1y) + (h01 * y2) + (h11 * m2y);
            // line_delta = t1 * line_length;   // already calculated at start of use_render_mode loop
            xout = xnew;
            yout = ynew;
        } else if (render_mode == CUBIC_HERMITE_SPLINE || render_mode == CUBIC_HERMITE_HAPPY_ACCIDENT1 || render_mode == CUBIC_HERMITE_HAPPY_ACCIDENT2 || render_mode == CUBIC_HERMITE_HAPPY_ACCIDENT3 || render_mode == CUBIC_HERMITE_HAPPY_ACCIDENT4 || render_mode == CUBIC_HERMITE_HAPPY_ACCIDENT5) {
            // using general cubic hermite spline, with tangent vectors determined by derivative of underlying curve
            // using formulation for interpolation on an arbitrary interval:
            // https://en.wikipedia.org/wiki/Cubic_Hermite_spline
            // w is random theta between theta1 and theta2
            // double w = theta1 + (Math.random() * (theta2 - theta1));
            // double t1 = (w-theta1)/(theta2-theta1);
            // but the above w & t1 calcs are equivalent to t1 randomly ranging from [0:1], so just use that instead...
            // t1 ranges from [0:1]
            // double t1 = Math.random();
            double t2 = t1 * t1;
            double t3 = t1 * t1 * t1;
            // Hermite Basis Functions:
            double h00 = 2 * t3 - 3 * t2 + 1;
            double h10 = t3 - 2 * t2 + t1;
            double h01 = -2 * t3 + 3 * t2;
            double h11 = t3 - t2;
            double dt1, dt2;
            // use render_modifier2 as theta offset (- for dt1, + for dt2) to find points to determine tangent vectors
            if (tangent_submode == THETA_TANGENT) {
                dt1 = theta1 - ((render_modifier2 / 360) * M_2PI);
                dt2 = theta2 + ((render_modifier2 / 360) * M_2PI);
            } else // (-rm2*theta_step_radians for dt1, +rm2*theta_line_offset for dt2)
            if (tangent_submode == INDEX_TANGENT) {
                dt1 = theta1 - (render_modifier2 * this.theta_step_radians);
                dt2 = theta2 + (render_modifier2 * this.theta_step_radians);
            } else {
                dt1 = theta1;
                dt2 = theta2;
            }
            double tan1x, tan1y, tan2x, tan2y;
            if (render_mode == CUBIC_HERMITE_HAPPY_ACCIDENT1 || render_mode == CUBIC_HERMITE_HAPPY_ACCIDENT2 || render_mode == CUBIC_HERMITE_HAPPY_ACCIDENT3 || render_mode == CUBIC_HERMITE_HAPPY_ACCIDENT4 || render_mode == CUBIC_HERMITE_HAPPY_ACCIDENT5) {
                // using wrong (but interesting) calculations for first derivative
                double k = a / b;
                first_derivative_point1.x = -(cos(k * dt1) + c) * sin(dt1);
                first_derivative_point1.y = (cos(k * dt1) + c) * cos(dt1);
                first_derivative_point2.x = -(cos(k * dt2) + c) * sin(dt2);
                first_derivative_point2.y = (cos(k * dt2) + c) * cos(dt2);
                if (render_mode == CUBIC_HERMITE_HAPPY_ACCIDENT2) {
                    // flip signs for x of tangents
                    first_derivative_point1.x = -1 * first_derivative_point1.x;
                    first_derivative_point2.x = -1 * first_derivative_point2.x;
                } else if (render_mode == CUBIC_HERMITE_HAPPY_ACCIDENT3) {
                    // flip signs for y of tangents
                    first_derivative_point1.y = -1 * first_derivative_point1.y;
                    first_derivative_point2.y = -1 * first_derivative_point2.y;
                } else if (render_mode == CUBIC_HERMITE_HAPPY_ACCIDENT4) {
                    // flip signs for both x and y of tangents
                    first_derivative_point1.x = -1 * first_derivative_point1.x;
                    first_derivative_point2.x = -1 * first_derivative_point2.x;
                    first_derivative_point1.y = -1 * first_derivative_point1.y;
                    first_derivative_point2.y = -1 * first_derivative_point2.y;
                } else if (render_mode == CUBIC_HERMITE_HAPPY_ACCIDENT5) {
                    // flip tangent points
                    DoublePoint2D ptmp = first_derivative_point1;
                    first_derivative_point1 = first_derivative_point2;
                    first_derivative_point2 = ptmp;
                }
            } else {
                // CUBIC_HERMITE
                // calculating tangent vectors
                // use first derivative of curve (currently using rhodonea) at each endpoint for tangent vectors
                // tangent = f'(t)
                curve.getFirstDerivative(dt1, first_derivative_point1);
                curve.getFirstDerivative(dt2, first_derivative_point2);
            }
            tan1x = first_derivative_point1.x;
            tan1y = first_derivative_point1.y;
            tan2x = first_derivative_point2.x;
            tan2y = first_derivative_point2.y;
            if (Double.isNaN(tan1x) || Double.isNaN(tan1y) || Double.isNaN(tan2x) || Double.isNaN(tan2y)) {
                // if can't find first derivative, bailout with point unchanged
                xout = xin;
                yout = yin;
            } else {
                double tanscale;
                // if *_UNSCALED_* then don't apply scaling adjustment to vectors
                if (tangent_submode == UNSCALED_TANGENT || tangent_submode == UNSCALED_UNIT_TANGENT || tangent_submode == UNSCALED_NORMAL || tangent_submode == UNSCALED_UNIT_NORMAL) {
                    tanscale = 1;
                } else {
                    // apply standard Hermite non-unit-interval scaling to vectors
                    // TANGENT (or NORMAL?)
                    tanscale = theta2 - theta1;
                }
                double dx1, dx2, dy1, dy2;
                // if *_NORMAL_* then set (dx1, dy1) and (dx2, dy2) to normal vectors instead of tangent vectors
                if (tangent_submode == NORMAL_VECTOR || tangent_submode == UNIT_NORMAL || tangent_submode == UNSCALED_NORMAL || tangent_submode == UNSCALED_UNIT_NORMAL) {
                    // rotate tangent vector around point to get normal vector?
                    // 2D rotation transformation of point B about a given fixed point A to give point C
                    // C.x = A.x + (B.x - A.x) * cos(theta) - (B.y - A.y) * sin(theta)
                    // C.y = A.y + (B.x - A.x) * sin(theta) + (B.y - A.y) * cos(theta)
                    double rota = -M_PI / 2.0;
                    dx1 = x1 + ((tan1x - x1) * cos(rota)) - ((tan1y - y1) * sin(rota));
                    dy1 = y1 + ((tan1x - x1) * sin(rota)) + ((tan1y - y1) * cos(rota));
                    dx2 = x2 + ((tan2x - x2) * cos(rota)) - ((tan2y - y2) * sin(rota));
                    dy2 = y2 + ((tan2x - x2) * sin(rota)) + ((tan2y - y2) * cos(rota));
                } else if (tangent_submode == ROTATION_TANGENT) {
                    // user render_modifier2 to determine angle (in degrees) to rotate tangent1
                    // user render_modifier3 to determine angle (in degrees) to rotate tangent2
                    // (or maybe should only use one modifier, and rotates both tangents?)
                    // (or one to modify x of both tangents, one to modify y of both tangents? ==> like happy_accident modes?)
                    double rota1 = (render_modifier2 / 360) * M_2PI;
                    dx1 = x1 + ((tan1x - x1) * cos(rota1)) - ((tan1y - y1) * sin(rota1));
                    dy1 = y1 + ((tan1x - x1) * sin(rota1)) + ((tan1y - y1) * cos(rota1));
                    double rota2 = (render_modifier3 / 360) * M_2PI;
                    dx2 = x2 + ((tan2x - x2) * cos(rota2)) - ((tan2y - y2) * sin(rota2));
                    dy2 = y2 + ((tan2x - x2) * sin(rota2)) + ((tan2y - y2) * cos(rota2));
                } else // scale y of tangent by render_modifier3 param
                if (tangent_submode == XY_SCALE_TANGENT) {
                    dx1 = tan1x * render_modifier2;
                    dx2 = tan2x * render_modifier2;
                    dy1 = tan1y * render_modifier3;
                    dy2 = tan2y * render_modifier3;
                } else {
                    // otherwise set (dx1,dy1) and (dx2,dy2) to tangent vectors
                    dx1 = tan1x;
                    dy1 = tan1y;
                    dx2 = tan2x;
                    dy2 = tan2y;
                }
                // if *_UNIT_* then normalize to unit vectors
                if (tangent_submode == UNIT_TANGENT || tangent_submode == UNIT_NORMAL || tangent_submode == UNSCALED_UNIT_TANGENT || tangent_submode == UNSCALED_UNIT_NORMAL) {
                    // normalizing the vectors
                    double sr1 = sqrt(dx1 * dx1 + dy1 * dy1);
                    double sr2 = sqrt(dx2 * dx2 + dy2 * dy2);
                    dx1 = dx1 / sr1;
                    dy1 = dy1 / sr1;
                    dx2 = dx2 / sr2;
                    dy2 = dy2 / sr2;
                } else {
                // otherwise leave unnormalized
                }
                // apply tension -- use render_modifier1 as tension parameter for cubic Hermite interpolation
                // by applying a (1 - tension) scaling factor to the tangent vector Hermite terms
                // thus if tension = 1, tangent terms will drop out and cubic Hermite interpolation will
                // reduce to linear interoplation ==> Maurer lines
                tanscale = (1 - render_modifier1) * tanscale;
                // trying to add parameter similar to Kochanek-Bartels "bias"
                // at tension = -1, first tangent term (h10) will be factored out
                // at tension = +1, second tangnt term (h11) will be factored out
                // double tanscale10 = (1 + render_modifier2) * tanscale;
                // double tanscale11 = (1 - render_modifier2) * tanscale;
                // double xnew = (h00 * x1) + (h10 * dx1 * tanscale10 ) + (h01 * x2) + (h11 * dx2 * tanscale11);
                // double ynew = (h00 * y1) + (h10 * dy1 * tanscale10 ) + (h01 * y2) + (h11 * dy2 * tanscale11);
                double xnew = (h00 * x1) + (h10 * dx1 * tanscale) + (h01 * x2) + (h11 * dx2 * tanscale);
                double ynew = (h00 * y1) + (h10 * dy1 * tanscale) + (h01 * y2) + (h11 * dy2 * tanscale);
                xout = xnew;
                yout = ynew;
            }
        } else if (render_mode == CUBIC_HERMITE_TANGENT_FORM2) {
            // using general cubic hermite spline, with tangent vectors determined by derivative of underlying curve
            // trying long-form 3rd degree hermite polynomial equation from http://www3.nd.edu/~zxu2/acms40390F12/Lec-3.4-5.pdf
            // for SPLINE6, attempting to introduce render modifier parameters (somewhat analogous to KB tension, continuity, bias?)
            double w0 = theta1;
            curve.getFirstDerivative(w0, first_derivative_point1);
            double ffw0 = first_derivative_point1.x;
            double ggw0 = first_derivative_point1.y;
            // double ffw0 = (-k * sin(k*w0) * cos(w0)) - ((cos(k*w0)+c) * sin(w0));  // first x derivative at P0 (of rhodonea)
            // double ggw0 = (-k * sin(k*w0) * sin(w0)) + ((cos(k*w0)+c) * cos(w0));  // first y derivative at P0 (of rhodonea)
            double w1 = theta2;
            curve.getFirstDerivative(w1, first_derivative_point2);
            double ffw1 = first_derivative_point2.x;
            double ggw1 = first_derivative_point2.y;
            // double ffw1 = (-k * sin(k*w1) * cos(w1)) - ((cos(k*w1)+c) * sin(w1));  // first x derivative at P1 (of rhodonea)
            // double ggw1 = (-k * sin(k*w1) * sin(w1)) + ((cos(k*w1)+c) * cos(w1));  // first y derivative at P1 (of rhodonea)
            double fw0 = x1;
            double gw0 = y1;
            double fw1 = x2;
            double gw1 = y2;
            if (Double.isNaN(ffw0) || Double.isNaN(ggw0) || Double.isNaN(ffw1) || Double.isNaN(ggw1)) {
                // if can't find first derivatives, bailout with point unchanged
                xout = xin;
                yout = yin;
            } else {
                // w (standing in for x from above hermite equation) ranges from [theta1:theta2]
                double w = theta1 + (t1 * (theta2 - theta1));
                if (DEBUG_TANGENTS && Math.random() < 0.01) {
                    double point_threshold = Math.random();
                    if (point_threshold < 0.5) {
                        // linear interp between point and tangent(point)
                        double fraction_of_line = 2 * point_threshold;
                        xout = (fw0 * (1 - fraction_of_line)) + (ffw0 * fraction_of_line);
                        yout = (gw0 * (1 - fraction_of_line)) + (ggw0 * fraction_of_line);
                    } else {
                        double fraction_of_line = 2 * (point_threshold - 0.5);
                        xout = (fw1 * (1 - fraction_of_line)) + (ffw1 * fraction_of_line);
                        yout = (gw1 * (1 - fraction_of_line)) + (ggw1 * fraction_of_line);
                    }
                } else {
                    double tw = (w - w0) / (w1 - w0);
                    double tw1 = ((w1 - w) / (w1 - w0));
                    double tw0 = ((w0 - w) / (w0 - w1));
                    double hf0 = (1 + (2 * tw)) * tw1 * tw1 * fw0;
                    double hff0 = (w - w0) * tw1 * tw1 * ffw0;
                    double hf1 = (1 + (2 * tw1)) * tw0 * tw0 * fw1;
                    double hff1 = (w - w1) * tw0 * tw0 * ffw1;
                    double fw = hf0 + hff0 + hf1 + hff1;
                    double hg0 = (1 + (2 * tw)) * tw1 * tw1 * gw0;
                    double hgg0 = (w - w0) * tw1 * tw1 * ggw0;
                    double hg1 = (1 + (2 * tw1)) * tw0 * tw0 * gw1;
                    double hgg1 = (w - w1) * tw0 * tw0 * ggw1;
                    double gw = hg0 + hgg0 + hg1 + hgg1;
                    xout = fw;
                    yout = gw;
                }
            }
        } else // end CUBIC_HERMITE_TANGENT_FORM2
        {
            // default to Maurer lines
            xout = mpoint.x;
            yout = mpoint.y;
        }
        // 
        if (render_submode == STROKE || render_submode == DEFAULT) {
        // do nothing
        } else if (render_submode == REFLECTED_STROKE) {
            double bc = (((x2 - x1) * (xout - x1)) + ((y2 - y1) * (yout - y1))) / (((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1)));
            xout = (2 * (x1 + ((x2 - x1) * bc))) - xout;
            yout = (2 * (y1 + ((y2 - y1) * bc))) - yout;
        } else if (render_submode == BOTH_STROKE) {
            if (Math.random() < 0.5) {
                // reflect half the points, leave other half unaltered
                double bc = (((x2 - x1) * (xout - x1)) + ((y2 - y1) * (yout - y1))) / (((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1)));
                xout = (2 * (x1 + ((x2 - x1) * bc))) - xout;
                yout = (2 * (y1 + ((y2 - y1) * bc))) - yout;
            }
        } else if (render_submode == ALTERNATING_STROKE) {
            if (step_number % 2 != 0) {
                // reflect odd steps, leave even  steps unaltered
                double bc = (((x2 - x1) * (xout - x1)) + ((y2 - y1) * (yout - y1))) / (((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1)));
                xout = (2 * (x1 + ((x2 - x1) * bc))) - xout;
                yout = (2 * (y1 + ((y2 - y1) * bc))) - yout;
            }
        } else if (render_submode == FILL_TO_LINE || render_submode == FILL_TO_CURVE) {
            DoublePoint2D opoint;
            if (render_submode == FILL_TO_CURVE) {
                opoint = curve_point;
            } else {
                opoint = mpoint;
            }
            // randomly place point on line from outpoint to mpoint
            double rfill = Math.random();
            xout = (xout * (1 - rfill)) + (opoint.x * rfill);
            yout = (yout * (1 - rfill)) + (opoint.y * rfill);
        } else if (render_submode == FILL_BETWEEN_LINE_CURVE) {
            // ignores render mode
            double rfill = Math.random();
            xout = (mpoint.x * (1 - rfill)) + (curve_point.x * rfill);
            yout = (mpoint.y * (1 - rfill)) + (curve_point.y * rfill);
        } else if (render_submode == FILL_BETWEEN_LINE_CURVE_SWIZZLE) {
            // ignores render mode
            double rfill = Math.random();
            xout = (mpoint.x * (1 - rfill)) + (curve_point.y * rfill);
            yout = (mpoint.y * (1 - rfill)) + (curve_point.x * rfill);
        } else if (render_submode == REFLECTED_FILL_TO_LINE || render_submode == REFLECTED_FILL_TO_CURVE) {
            DoublePoint2D opoint;
            if (render_submode == REFLECTED_FILL_TO_CURVE) {
                opoint = curve_point;
            } else {
                opoint = mpoint;
            }
            double bc = (((x2 - x1) * (xout - x1)) + ((y2 - y1) * (yout - y1))) / (((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1)));
            xout = (2 * (x1 + ((x2 - x1) * bc))) - xout;
            yout = (2 * (y1 + ((y2 - y1) * bc))) - yout;
            double rfill = Math.random();
            xout = (xout * (1 - rfill)) + (opoint.x * rfill);
            yout = (yout * (1 - rfill)) + (opoint.y * rfill);
        } else if (render_submode == BOTH_FILL_TO_LINE || render_submode == BOTH_FILL_TO_CURVE) {
            DoublePoint2D opoint;
            if (render_submode == BOTH_FILL_TO_CURVE) {
                opoint = curve_point;
            } else {
                opoint = mpoint;
            }
            if (Math.random() < 0.5) {
                // reflect half the points, leave other half unaltered
                double bc = (((x2 - x1) * (xout - x1)) + ((y2 - y1) * (yout - y1))) / (((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1)));
                xout = (2 * (x1 + ((x2 - x1) * bc))) - xout;
                yout = (2 * (y1 + ((y2 - y1) * bc))) - yout;
            }
            double rfill = Math.random();
            xout = (xout * (1 - rfill)) + (opoint.x * rfill);
            yout = (yout * (1 - rfill)) + (opoint.y * rfill);
        } else if (render_submode == ALTERNATING_FILL_TO_LINE || render_submode == ALTERNATING_FILL_TO_CURVE) {
            DoublePoint2D opoint;
            if (render_submode == ALTERNATING_FILL_TO_CURVE) {
                opoint = curve_point;
            } else {
                opoint = mpoint;
            }
            if (step_number % 2 != 0) {
                // reflect odd steps, leave even steps unaltered
                double bc = (((x2 - x1) * (xout - x1)) + ((y2 - y1) * (yout - y1))) / (((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1)));
                xout = (2 * (x1 + ((x2 - x1) * bc))) - xout;
                yout = (2 * (y1 + ((y2 - y1) * bc))) - yout;
            }
            double rfill = Math.random();
            xout = (xout * (1 - rfill)) + (opoint.x * rfill);
            yout = (yout * (1 - rfill)) + (opoint.y * rfill);
        }
        // handling thickness
        if (line_thickness != 0) {
            if (line_thickness_strategy == RANDOM) {
                // previous simple random offset from center of line
                xout += ((pContext.random() - 0.5) * line_thickness);
                yout += ((pContext.random() - 0.5) * line_thickness);
            } else if (line_thickness_strategy == PERPENDICULAR) {
                // random distance _perpendicular_ to unmodified Maurer line;
                double xnorm = xdiff / line_length;
                double ynorm = ydiff / line_length;
                double xperpnorm = -ynorm;
                double yperpnorm = xnorm;
                double perp_offset = (pContext.random() - 0.5) * line_thickness;
                double xperp = perp_offset * xperpnorm;
                double yperp = perp_offset * yperpnorm;
                xout += xperp;
                yout += yperp;
            } else if (line_thickness_strategy == ROUNDED_CAPS) {
                // random distance _perpendicular_ to unmodified Maurer line, but with rounded caps
                if (line_delta < line_thickness || ((line_length - line_delta) < line_thickness)) {
                    xout += ((pContext.random() - 0.5) * line_thickness);
                    yout += ((pContext.random() - 0.5) * line_thickness);
                } else {
                    double xnorm = xdiff / line_length;
                    double ynorm = ydiff / line_length;
                    double xperpnorm = -ynorm;
                    double yperpnorm = xnorm;
                    double perp_offset = (pContext.random() - 0.5) * line_thickness;
                    double xperp = perp_offset * xperpnorm;
                    double yperp = perp_offset * yperpnorm;
                    xout += xperp;
                    yout += yperp;
                }
            }
        }
    }
    // 
    // FILTERING
    // 
    pVarTP.doHide = false;
    boolean cumulative_pass = true;
    for (int findex = 0; findex < filter_count; findex++) {
        MaurerFilter mfilter = (MaurerFilter) filters.get(findex);
        int fmode = mfilter.mode;
        // if filter mode is OFF, then skip this filter
        if (fmode != OFF) {
            int measure = mfilter.measure;
            int op = mfilter.operator;
            double low_thresh, high_thresh;
            double val;
            double[] sampled_vals;
            if (measure == LINE_LENGTH_LINES) {
                val = line_length;
                sampled_vals = sampled_line_lengths;
            } else if (measure == LINE_ANGLE_LINES) {
                val = line_angle;
                sampled_vals = sampled_line_angles;
            } else if (measure == POINT_ANGLE_LINES) {
                val = point_angle;
                sampled_vals = sampled_point_angles;
            } else if (measure == DISTANCE_ALONG_LINE_POINTS) {
                val = line_delta;
                // percentiles calculated directly for DISTANCE_ALONG_LINE_POINTS
                sampled_vals = null;
            } else if (measure == DISTANCE_FROM_MIDLINE_POINTS) {
                val = abs(line_delta - midlength);
                // percentiles calculated directly for DISTANCE_FROM_MIDLINE_POINTS
                sampled_vals = null;
            } else if (measure == DISTANCE_FROM_NEAREST_END_POINTS) {
                val = Math.min(line_delta, line_length - line_delta);
                // percentiles calculated directly for DISTANCE_FROM_NEAREST_END_POINTS
                sampled_vals = null;
            } else if (measure == Z_MINMAX_POINTS) {
                // min/max zout should be +/- radius of circle ==> +/-(line_length/2)
                // val = abs(zout*2); // min/max zout should be
                // range of val should be 0 to line_length;
                val = zout + (line_length / 2);
                sampled_vals = sampled_line_lengths;
            } else if (measure == Z_ABSOLUTE_MINMAX_POINTS) {
                // min/max zout should be +/- radius of circle ==> +/-(line_length/2)
                // range of val should be 0 to line_length
                val = abs(zout) * 2;
                sampled_vals = sampled_line_lengths;
            } else {
                // default to line length
                val = line_length;
                sampled_vals = sampled_line_lengths;
            }
            if (fmode == BAND_PASS_VALUE || fmode == BAND_STOP_VALUE) {
                low_thresh = mfilter.low_thresh;
                high_thresh = mfilter.high_thresh;
            } else if (fmode == BAND_PASS_PERCENT || fmode == BAND_STOP_PERCENT) {
                if (measure == DISTANCE_ALONG_LINE_POINTS) {
                    low_thresh = mfilter.low_thresh * line_length;
                    high_thresh = mfilter.high_thresh * line_length;
                } else if (measure == DISTANCE_FROM_MIDLINE_POINTS || measure == DISTANCE_FROM_NEAREST_END_POINTS) {
                    // low_thresh and high_thresh for DISTANCE_FROM_MIDLINE_POINTS and DISTANCE_FROM_NEAREST_END_POINTS
                    // can behave differently when thresholding by value, but act the same when thresholding by percentile
                    low_thresh = mfilter.low_thresh * midlength;
                    high_thresh = mfilter.high_thresh * midlength;
                } else {
                    int low_index, high_index;
                    // should probably round here instead of flooring with (int) cast?
                    if (mfilter.low_thresh <= 0 || mfilter.low_thresh >= 1) {
                        low_index = 0;
                    } else {
                        low_index = (int) (mfilter.low_thresh * sampled_vals.length);
                    }
                    // if high_thresh not in (0 -> 1.0) exclusive range, clamp  at 100%
                    if (mfilter.high_thresh >= 1 || mfilter.high_thresh <= 0) {
                        high_index = (sampled_vals.length - 1);
                    } else {
                        high_index = (int) (mfilter.high_thresh * sampled_vals.length);
                    }
                    low_thresh = sampled_vals[low_index];
                    high_thresh = sampled_vals[high_index];
                }
            } else {
                // default to values
                low_thresh = mfilter.low_thresh;
                high_thresh = mfilter.high_thresh;
            }
            boolean inband = (val >= low_thresh && val <= high_thresh);
            boolean current_pass;
            if (fmode == BAND_PASS_VALUE || fmode == BAND_PASS_PERCENT) {
                current_pass = inband;
            } else if (fmode == BAND_STOP_VALUE || fmode == BAND_STOP_PERCENT) {
                // effectively the same as additional NOT operation on this filter
                current_pass = !inband;
            } else {
                // default to bandpass: passing values that are within filter band
                current_pass = inband;
            }
            if (findex == 0) {
                // no combo operator for first filter
                cumulative_pass = current_pass;
            } else {
                if (op == AND) {
                    cumulative_pass = cumulative_pass && current_pass;
                } else if (op == OR) {
                    cumulative_pass = cumulative_pass || current_pass;
                } else if (op == XOR) {
                    cumulative_pass = (cumulative_pass && !current_pass) || (!cumulative_pass && current_pass);
                } else if (op == ANOTB) {
                    cumulative_pass = cumulative_pass && !current_pass;
                } else if (op == BNOTA) {
                    cumulative_pass = !cumulative_pass && current_pass;
                } else {
                    // default to AND
                    cumulative_pass = cumulative_pass && current_pass;
                }
            }
        }
    }
    pVarTP.doHide = !cumulative_pass;
    // Add final values in to variations totals
    if (diff_mode) {
        pVarTP.x = pAffineTP.x + (pAmount * (xout - pAffineTP.x));
        pVarTP.y = pAffineTP.y + (pAmount * (yout - pAffineTP.y));
        pVarTP.z = pAffineTP.z + (pAmount * (zout - pAffineTP.z));
    } else {
        pVarTP.x += pAmount * xout;
        pVarTP.y += pAmount * yout;
        pVarTP.z += pAmount * zout;
    }
    // 
    if (direct_color_measure != NONE && direct_color_gradient != OFF) {
        double val;
        double[] sampled_vals;
        if (direct_color_measure == LINE_LENGTH_LINES) {
            val = line_length;
            sampled_vals = sampled_line_lengths;
        } else if (direct_color_measure == SPEED_AT_ENDPOINT1) {
            // val = speed1;
            val = curve.getSpeed(theta1, end_point1);
            sampled_vals = sampled_speeds;
        } else if (direct_color_measure == CURRENT_THETA) {
            val = theta1 / M_2PI;
            sampled_vals = sampled_thetas;
        } else if (direct_color_measure == LINE_ANGLE_LINES) {
            val = line_angle;
            sampled_vals = sampled_line_angles;
        } else if (direct_color_measure == POINT_ANGLE_LINES) {
            val = point_angle;
            sampled_vals = sampled_point_angles;
        } else if (direct_color_measure == DISTANCE_ALONG_LINE_POINTS) {
            val = line_delta;
            sampled_vals = null;
        } else if (direct_color_measure == DISTANCE_FROM_MIDLINE_POINTS) {
            val = abs(line_delta - midlength);
            sampled_vals = null;
        } else if (direct_color_measure == META_INDEX && meta_mode != OFF) {
            val = current_meta_step;
            sampled_vals = null;
        } else if (direct_color_measure == Z_MINMAX_POINTS) {
            // min/max zout should be +/- radius of circle ==> +/-(line_length/2)
            // val = abs(zout*2); // min/max zout should be
            // range of val should be 0 to line_length;
            val = zout + (line_length / 2);
            sampled_vals = sampled_line_lengths;
        } else if (direct_color_measure == Z_ABSOLUTE_MINMAX_POINTS) {
            // min/max zout should be +/- radius of circle ==> +/-(line_length/2)
            // range of val should be 0 to line_length
            val = abs(zout) * 2;
            sampled_vals = sampled_line_lengths;
        } else if (direct_color_measure == DISTANCE_FROM_NEAREST_END_POINTS) {
            val = Math.min(line_delta, line_length - line_delta);
            // percentiles calculated directly for DISTANCE_FROM_NEAREST_END_POINTS
            sampled_vals = null;
        } else {
            // default to LINE_LENGTH_LINES
            val = line_length;
            sampled_vals = sampled_line_lengths;
        }
        double baseColor = 0;
        double low_value, high_value;
        // ignore percentile option and direct_color_thesholding if using META_INDEX mode??
        if (direct_color_measure == META_INDEX && meta_mode != OFF) {
            low_value = 0;
            high_value = meta_steps;
        } else {
            if (direct_color_thesholding == PERCENT) {
                if (direct_color_measure == DISTANCE_ALONG_LINE_POINTS) {
                    low_value = color_low_thresh * line_length;
                    high_value = color_high_thresh * line_length;
                } else if (direct_color_measure == DISTANCE_FROM_MIDLINE_POINTS) {
                    low_value = color_low_thresh * midlength;
                    high_value = color_high_thresh * midlength;
                } else if (direct_color_measure == DISTANCE_FROM_MIDLINE_POINTS || direct_color_measure == DISTANCE_FROM_NEAREST_END_POINTS) {
                    // low_thresh and high_thresh for DISTANCE_FROM_MIDLINE_POINTS and DISTANCE_FROM_NEAREST_END_POINTS
                    // can behave differently when thresholding by value, but act the same when thresholding by percentile
                    low_value = color_low_thresh * midlength;
                    high_value = color_high_thresh * midlength;
                } else {
                    int low_index, high_index;
                    if (color_low_thresh < 0 || color_low_thresh >= 1) {
                        low_index = 0;
                    } else {
                        low_index = (int) (color_low_thresh * sample_size);
                    }
                    if (color_high_thresh >= 1 || color_high_thresh < 0) {
                        high_index = sample_size - 1;
                    } else {
                        high_index = (int) (color_high_thresh * (sample_size - 1));
                    }
                    low_value = sampled_vals[low_index];
                    high_value = sampled_vals[high_index];
                }
            } else {
                // default is by value
                low_value = color_low_thresh;
                high_value = color_high_thresh;
            }
            if (low_value > high_value) {
                double temp = low_value;
                low_value = high_value;
                high_value = temp;
            }
        }
        if (val < low_value) {
            baseColor = 0;
        } else if (val >= high_value) {
            baseColor = 255;
        } else {
            baseColor = ((val - low_value) / (high_value - low_value)) * 255;
        }
        if (direct_color_gradient == COLORMAP_CLAMP) {
            pVarTP.rgbColor = false;
            pVarTP.color = baseColor / 255.0;
            if (pVarTP.color < 0) {
                pVarTP.color = 0;
            }
            if (pVarTP.color > 1.0) {
                pVarTP.color = 1.0;
            }
        } else if (direct_color_gradient == COLORMAP_WRAP) {
            pVarTP.rgbColor = false;
            // if val is outside range, wrap it around (cylce) to keep within range
            if (val < low_value) {
                val = high_value - ((low_value - val) % (high_value - low_value));
            } else if (val > high_value) {
                val = low_value + ((val - low_value) % (high_value - low_value));
            }
            baseColor = ((val - low_value) / (high_value - low_value)) * 255;
            pVarTP.color = baseColor / 255.0;
            if (pVarTP.color < 0) {
                pVarTP.color = 0;
            }
            if (pVarTP.color > 1.0) {
                pVarTP.color = 1.0;
            }
        } else if (direct_color_gradient == RED_GREEN) {
            // 
            pVarTP.rgbColor = true;
            pVarTP.redColor = baseColor;
            pVarTP.greenColor = 255 - baseColor;
            pVarTP.blueColor = 0;
        } else if (direct_color_gradient == RED_BLUE) {
            pVarTP.rgbColor = true;
            pVarTP.redColor = baseColor;
            pVarTP.greenColor = 0;
            pVarTP.blueColor = 255 - baseColor;
        } else if (direct_color_gradient == BLUE_GREEN) {
            pVarTP.rgbColor = true;
            pVarTP.redColor = 0;
            pVarTP.greenColor = 255 - baseColor;
            pVarTP.blueColor = baseColor;
        }
    }
// END color_mode != normal
}
Also used : XYZPoint(org.jwildfire.create.tina.base.XYZPoint)

Example 70 with XYZPoint

use of org.jwildfire.create.tina.base.XYZPoint in project JWildfire by thargor6.

the class Hexaplay3DFunc method transform.

@Override
public void transform(FlameTransformationContext pContext, XForm pXForm, XYZPoint pAffineTP, XYZPoint pVarTP, double pAmount) {
    /* hexaplay3D by Larry Berlin, http://aporev.deviantart.com/art/3D-Plugins-Collection-One-138514007?q=gallery%3Aaporev%2F8229210&qo=15 */
    if (this._fcycle > 5) {
        this._fcycle = 0;
        // Chooses new 6 or 3 nodes
        this._rswtch = (int) trunc(pContext.random() * 3.0);
    }
    if (this._bcycle > 2) {
        this._bcycle = 0;
        // Chooses new 6 or 3 nodes
        this._rswtch = (int) trunc(pContext.random() * 3.0);
    }
    // Sets hexagon length radius - major plane
    double lrmaj = pAmount;
    // Boost is the separation distance between the two planes
    double boost = 0;
    int posNeg = 1;
    int loc60;
    int loc120;
    double scale = this.scale * 0.5;
    if (pContext.random() < 0.5) {
        posNeg = -1;
    }
    // Determine whether one or two major planes
    int majplane = 1;
    double abmajp = fabs(this.majp);
    if (abmajp <= 1.0) {
        // Want either 1 or 2
        majplane = 1;
    } else {
        majplane = 2;
        // distance above and below XY plane
        boost = (abmajp - 1.0) * 0.5;
    }
    // Creating Z factors relative to the planes
    if (majplane == 2) {
        pVarTP.z += pAffineTP.z * 0.5 * this.zlift + (posNeg * boost);
    } else {
        pVarTP.z += pAffineTP.z * 0.5 * this.zlift;
    }
    // Work out the segments and hexagonal nodes
    if (this._rswtch <= 1) {
        // Occasion to build using 60 degree segments
        // loc60 = trunc(pContext.random()*6.0);  // random nodes selection
        // sequential nodes selection
        loc60 = this._fcycle;
        pVarTP.x = ((pVarTP.x + pAffineTP.x) * scale) + (lrmaj * _seg60x[loc60]);
        pVarTP.y = ((pVarTP.y + pAffineTP.y) * scale) + (lrmaj * _seg60y[loc60]);
        this._fcycle += 1;
    } else {
        // Occasion to build on 120 degree segments
        // loc120 = trunc(pContext.random()*3.0);  // random nodes selection
        // sequential nodes selection
        loc120 = this._bcycle;
        pVarTP.x = ((pVarTP.x + pAffineTP.x) * scale) + (lrmaj * _seg120x[loc120]);
        pVarTP.y = ((pVarTP.y + pAffineTP.y) * scale) + (lrmaj * _seg120y[loc120]);
        this._bcycle += 1;
    }
}
Also used : XYZPoint(org.jwildfire.create.tina.base.XYZPoint)

Aggregations

XYZPoint (org.jwildfire.create.tina.base.XYZPoint)102 XForm (org.jwildfire.create.tina.base.XForm)16 Layer (org.jwildfire.create.tina.base.Layer)13 Test (org.junit.Test)4 VectorD (org.jwildfire.base.mathlib.VecMathLib.VectorD)4 Flame (org.jwildfire.create.tina.base.Flame)4 ZigguratRandomGenerator (org.jwildfire.create.tina.random.ZigguratRandomGenerator)4 FlameRenderer (org.jwildfire.create.tina.render.FlameRenderer)4 SimpleHDRImage (org.jwildfire.image.SimpleHDRImage)4 SimpleImage (org.jwildfire.image.SimpleImage)4 Complex (org.jwildfire.base.mathlib.Complex)2 XYZProjectedPoint (org.jwildfire.create.tina.base.XYZProjectedPoint)2 Face (org.jwildfire.create.tina.variation.mesh.Face)2 Vertex (org.jwildfire.create.tina.variation.mesh.Vertex)2 VertexWithUV (org.jwildfire.create.tina.variation.mesh.VertexWithUV)2 RenderColor (org.jwildfire.create.tina.palette.RenderColor)1 CannabisCurveWFFunc (org.jwildfire.create.tina.variation.CannabisCurveWFFunc)1 CloverLeafWFFunc (org.jwildfire.create.tina.variation.CloverLeafWFFunc)1 DCPerlinFunc (org.jwildfire.create.tina.variation.DCPerlinFunc)1 FlowerFunc (org.jwildfire.create.tina.variation.FlowerFunc)1