root/src/SWFShapeMaker.cpp

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. doSetup
  2. lineToR
  3. curveToR
  4. curveTo
  5. smoothCurveToR
  6. smoothCurveTo
  7. intersectLines
  8. midLine
  9. bezierSplit
  10. cubicToRec
  11. cubicTo
  12. cubicToR
  13. smoothCubicTo
  14. smoothCubicToR
  15. close
  16. finish
  17. setupR
  18. setup
  19. lineTo
  20. rect
  21. ellipseSegment
  22. ellipse
  23. arcTo
  24. arcToR
  25. boundsWriteXML

#include "SWFShapeMaker.h"
#include <stdarg.h>
#include "SWFShapeItem.h"
#include "SWFItem.h"
#include "SWF.h"

#define TMP_STRLEN 0xFF

namespace SWF {

// TODO assure bits is representable in 4 bits!
ShapeMaker::ShapeMaker( List<ShapeItem>* e, double fx, double fy, double ofsx, double ofsy ) {
        edges = e;
        factorx = fx;
        factory = fy;
        offsetx = ofsx;
        offsety = ofsy;
        diffx = diffy = 0.0;
        lastx = lasty = 0.0;
        smoothx = smoothy = 0.0;
        minx = miny = maxx = maxy = 0.0;
        have_first = false;
        
        fillStyle0 = lineStyle = fillStyle1 = -1;

        roundReset();
}

void ShapeMaker::doSetup( double _x, double _y, bool hasMoveTo, int _fillStyle0, int _fillStyle1, int _lineStyle ) {
        // append shapesetup (whithout styles, this is glyph only for now)
        ShapeSetup *setup = new ShapeSetup;


        if( _fillStyle0 != -1 ) {
                setup->setfillStyle0( _fillStyle0 );
                setup->sethasFillStyle0( 1 );
        }
        if( _fillStyle1 != -1 ) {
                setup->setfillStyle1( _fillStyle1 );
                setup->sethasFillStyle1( 1 );
        }
        if( _lineStyle != -1 ) {
                setup->setlineStyle( _lineStyle );
                setup->sethasLineStyle( 1 );
        }
        
        if( hasMoveTo ) {
                roundReset();
                int x = roundX(factorx * ( _x ) );
                int y = roundY(factory * ( _y ) );

                diffx = diffy = 0;

                setup->setxybits( SWFMaxBitsNeeded( true, 2, x, y ) );
        
                setup->sethasMoveTo( 1 );
                setup->setx( x );
                setup->sety( y );
                
                minmax( x, y );
        
                lastx = _x; lasty = _y;
                lastsetupx = _x; lastsetupy = _y;
        }
                        
        edges->append( setup );
        
//      fprintf(stderr,"setup %i/%i\n", x, y );
}

void ShapeMaker::lineToR( double _x, double _y ) {
        int x = roundX(factorx * ( _x ) );
        int y = roundY(factory * ( _y ) );

        diffx += x; diffy += y;
        
        SWF::LineTo *segment = new SWF::LineTo;
        segment->setType(1);
        //segment->setbits( maxBitsNeeded( true, 2, x, y ) );
        segment->setx( x );
        segment->sety( y );
        edges->append( segment );
        minmax( x+(lastx*factorx), y+(lasty*factory) );

        lastx += _x; lasty += _y;
}

void ShapeMaker::curveToR( double _cx, double _cy, double ax, double ay ) {
        int cx = roundX(factorx * ( _cx ) );
        int cy = roundY(factory * ( _cy ) );
        int x = roundX(factorx * ( ax - _cx ) );
        int y = roundY(factory * ( ay - _cy) );

        smoothx = lastx + _cx;
        smoothy = lasty + _cy;

        diffx += cx + x; diffy += cy + y;
        
        CurveTo *segment = new CurveTo;
        segment->setType(2);
        segment->setbits( SWFMaxBitsNeeded( true, 4, x, y, cx, cy ) );
        segment->setx1( cx );
        segment->sety1( cy );
        segment->setx2( x );
        segment->sety2( y );
        edges->append( segment );
        minmax( x+cx+(lastx*factorx), y+cy+(lasty*factory) );

        lastx += ax; lasty += ay;

        smoothx = ax - _cx;
        smoothy = ay - _cy;
}

void ShapeMaker::curveTo( double cx, double cy, double ax, double ay ) {
        curveToR( cx - lastx, cy - lasty, ax - lastx, ay - lasty );
}

void ShapeMaker::smoothCurveToR( double ax, double ay ) {
        curveToR( smoothx, smoothy, ax, ay );

}

void ShapeMaker::smoothCurveTo( double ax, double ay ) {
        curveTo( lastx + smoothx, lasty + smoothy, ax, ay );
}

// cubic to quadratic bezier functions
// thanks to Robert Penner

Point intersectLines( Point p1, Point p2, Point p3, Point p4 ) {
        double x1 = p1.x, y1 = p1.y;
        double x4 = p4.x, y4 = p4.y;
        double dx1 = p2.x-x1;
        double dx2 = p3.x-x4;
        if( dx1 == 0 && dx2 == 0 ) return Point(p1.x + ((p4.x-p1.x)/2), p1.y + ((p4.y-p1.y)/2));
        double m1 = (p2.y-y1)/dx1;
        double m2 = (p3.y-y4)/dx2;
        
        if( !dx1 ) {
                return Point( x1, (m2*(x1-x4))+y4 );
        } else if( !dx2 ) {
                return Point( x4, (m1*(x4-x1))+y1 );
        } else if( fabs( m1-m2 ) < .001 ) {
                return Point( x4, (m1*(x4-x1))+y1 );
        }
        double x = ((-m2 * x4) + y4 + (m1 * x1) - y1) / (m1-m2);
        Point p( x, (m1 * (x-x1)) + y1 );
        
/*      
        if( fabs(p.y) > 100 ) {
                fprintf(stderr,"p1/2: %f/%f %f/%f %f/%f %f/%f\n", p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y );
                fprintf(stderr,"S: %f/%f  m1/2: %f/%f\n", p.x, p.y, m1, m2 );
        }
*/
        return p;
}

Point midLine( const Point& a, const Point& b ) {
        Point p( ((a.x+b.x)/2), ((a.y+b.y)/2) );
        return p;
}

void bezierSplit( const Bezier& b, Bezier* b0, Bezier* b1 ) {
        Point p01 = midLine( b.p0, b.p1 );
        Point p12 = midLine( b.p1, b.p2 );
        Point p23 = midLine( b.p2, b.p3 );
        Point p02 = midLine( p01, p12 );
        Point p13 = midLine( p12, p23 );
        Point p03 = midLine( p02, p13 );
        
        b0->set( b.p0, p01, p02, p03 );
        b1->set( p03, p13, p23, b.p3 );
}

void ShapeMaker::cubicToRec( const Point& a, const Point& b, const Point& c, const Point& d, double k, int iteration ) {
        Point s = intersectLines( a, b, c, d );
        double dx = (a.x+d.x+s.x*4-(b.x+c.x)*3)*.125;
        double dy = (a.y+d.y+s.y*4-(b.y+c.y)*3)*.125;
        Bezier bz( a, b, c, d );
        Bezier b0, b1;
        if( dx*dx + dy*dy > k && iteration<10 ) {
//              fprintf(stderr,"[%03i] split: %f\n", iteration, dx*dx + dy*dy);
                bezierSplit( bz, &b0, &b1 );
                // recurse
                iteration++;
                cubicToRec( a,    b0.p1, b0.p2, b0.p3, k, iteration );
                //lineTo( b0.p3.x, b0.p3.y );
                
                cubicToRec( b1.p0, b1.p1, b1.p2, d,    k, iteration );
                //lineTo( b1.p1.x, b1.p1.y );
                //lineTo( b1.p2.x, b1.p2.y );
                //lineTo( d.x, d.y );
        } else {
//              fprintf(stderr,"#### %i %i %i %i\n", (int)s.x, (int)s.y, (int)d.x, (int)d.y );
                //lineTo( (int)s.x, (int)s.y );
                //lineTo( (int)d.x, (int)d.y );
                curveTo( s.x, s.y, d.x, d.y );
        }
}

void ShapeMaker::cubicTo( double x1, double y1, double x2, double y2, double ax, double ay ) {
        Point a(lastx,lasty);
        Point b(x1,y1);
        Point c(x2,y2);
        Point d(ax,ay);

        cubicToRec( a, b, c, d, .01 );
        
        lastx = ax; lasty = ay;
        smoothx = ax - x2;
        smoothy = ay - y2;
}

void ShapeMaker::cubicToR( double x1, double y1, double x2, double y2, double ax, double ay ) {
        cubicTo(lastx + x1, lasty + y1,
                lastx + x2, lasty + y2,
                lastx + ax, lasty + ay);
}

void ShapeMaker::smoothCubicTo( double x2, double y2, double ax, double ay ) {
        cubicTo( lastx + smoothx, lasty + smoothy, x2, y2, ax, ay );

}

void ShapeMaker::smoothCubicToR( double x2, double y2, double ax, double ay ) {
        cubicToR( smoothx, smoothy, x2, y2, ax, ay );
}

void ShapeMaker::close(bool stroke) {
        // diffx/diffy captures rounding errors. they can accumulate a bit! FIXME
        
        if( diffx || diffy ) {
                /*fprintf(stderr,"WARNING: shape not closed; closing (%f/%f).\n", diffx, diffy);
                fprintf(stderr,"DEBUG: accumulated rounding error (%f/%f).\n", roundx, roundy);*/
                
                if(!stroke) {
                        doSetup( 0, 0, false, -1, -1, 0 );
                }
                
                // closing line
                LineTo *segment = new LineTo;
                segment->setType(1);
                //segment->setbits( maxBitsNeeded( true, 2, x, y ) );
                segment->setx( (int)-diffx );
                segment->sety( (int)-diffy );
                edges->append( segment );

                if(!stroke) {
                        doSetup( 0, 0, false, -1, -1, lineStyle );
                }
                
                diffx = diffy = 0;
                if(stroke) {
                        lastx = lastsetupx; lasty = lastsetupy;
                }
        }
}

void ShapeMaker::finish() {
        // end shape
        ShapeSetup *setup = new ShapeSetup;
        edges->append( setup );
}

void ShapeMaker::setupR( double x, double y ) {
        x += lastx; y += lasty;
        doSetup( x, y, true, fillStyle0, fillStyle1, lineStyle );
}

void ShapeMaker::setup( double x, double y ) {
        x += offsetx; y += offsety;
        doSetup( x, y, true, fillStyle0, fillStyle1, lineStyle );
}

void ShapeMaker::lineTo( double x, double y ) {
        x += offsetx; y += offsety;
        lineToR( x - lastx, y - lasty );
}

void ShapeMaker::rect( double x, double y, double w, double h, double rx, double ry ) {
        if(rx > 0 || ry > 0) {
                setup(x + rx, y);
                lineTo(x + w - rx, y);
                arcTo(rx, ry, 0, false, true, x + w, y + ry);
                lineTo(x + w, y + h - ry);
                arcTo(rx, ry, 0, false, true, x + w - rx, y + h);
                lineTo(x + rx, y + h);
                arcTo(rx, ry, 0, false, true, x, y + h - ry);
                lineTo(x, y + ry);
                arcTo(rx, ry, 0, false, true, x + rx, y);
                close();
        } else {
                setup(x, y);
                lineToR(0, h);
                lineToR(w, 0);
                lineToR(0, -h);
                lineToR(-w, 0);
                close();
        }
}

#define ELLIPSE_SEGMENTS 8
#define ELLIPSE_ANGLE ( M_PI * 2 / ELLIPSE_SEGMENTS )

void ShapeMaker::ellipseSegment( double cx, double cy, double rx, double ry, double phi, double theta, double dTheta) {
        double a1 = theta + dTheta / 2;
        double a2 = theta + dTheta;
        double f = cos(dTheta / 2);

        Point p1(cos(a1) * rx / f, sin(a1) * ry / f);
        Point p2(cos(a2) * rx, sin(a2) * ry);
        p1.rotate(phi);
        p2.rotate(phi);

        curveTo(cx + p1.x, cy + p1.y, cx + p2.x, cy + p2.y);
}

void ShapeMaker::ellipse( double cx, double cy, double rx, double ry ) {
        setup(cx + rx, cy);
        for(int i = 0; i < ELLIPSE_SEGMENTS; i++) {
                ellipseSegment(cx, cy, rx, ry, 0, ELLIPSE_ANGLE * i, ELLIPSE_ANGLE);
        }
        close();
}

void ShapeMaker::arcTo( double rx, double ry, double rotation, bool largeArcFlag, bool sweepFlag, double x, double y ) {
        double a, f, lambda, theta, dTheta;

        a = rotation / 180 * M_PI;

        Point A(lastx, lasty);
        Point B(x, y);
                        
        Point P = (A - B) / 2;
        P.rotate(-a);

        lambda = pow(P.x, 2) / pow(rx, 2) + pow(P.y, 2) / pow(ry, 2);
        if(lambda > 1) {
                rx *= sqrt(lambda);
                ry *= sqrt(lambda);
        }

        f = (pow(rx, 2)*pow(ry, 2)-pow(rx, 2)*pow(P.y, 2)-pow(ry, 2)*pow(P.x, 2))/(pow(rx, 2)*pow(P.y, 2)+pow(ry, 2)*pow(P.x, 2));
        if(f < 0) {
                f = 0;
        } else {
                f = sqrt(f);
        }
        if(largeArcFlag == sweepFlag) f *= -1;

        Point C_(rx / ry * P.y, -ry / rx * P.x);
        C_ = C_ * f;

        Point C = C_;
        C.rotate(a);
        C = C + (A + B) / 2; 
                        
        theta = atan2((P.y-C_.y)/ry, (P.x-C_.x)/rx);
        dTheta = atan2((-P.y-C_.y)/ry, (-P.x-C_.x)/rx) - theta;

        if(sweepFlag && dTheta < 0)
                dTheta += 2 * M_PI;

        if(!sweepFlag && dTheta > 0)
                dTheta -= 2 * M_PI;
        
        double dThetaAbs = (dTheta < 0 ? -dTheta : dTheta);
        int segments = (int)ceil(dThetaAbs / ELLIPSE_ANGLE);

        for(int i = 0; i < segments; i++) {
                ellipseSegment(C.x, C.y, rx, ry, a, dTheta / segments * i + theta, dTheta / segments);
        }
}

void ShapeMaker::arcToR( double rx, double ry, double rotation, bool largeArcFlag, bool sweepFlag, double x, double y ) {
        arcTo(rx, ry, rotation, largeArcFlag, sweepFlag, lastx + x, lasty + y);
}

void ShapeMaker::boundsWriteXML( xmlNodePtr parent, double border ) {
        char tmp[TMP_STRLEN];
        xmlNodePtr node;

        if(border >= 0) {
                node = xmlNewChild(parent, NULL, (const xmlChar *)"bounds", NULL); 
        } else {
                node = xmlNewChild(parent, NULL, (const xmlChar *)"strokeBounds", NULL); 
                border = 0;
        }

        node = xmlNewChild(node, NULL, (const xmlChar *)"Rectangle", NULL);
        snprintf(tmp, TMP_STRLEN, "%f", minx - border * 20);
        xmlSetProp(node, (const xmlChar *)"left", (const xmlChar *)&tmp);
        snprintf(tmp, TMP_STRLEN,"%f", miny - border * 20);
        xmlSetProp(node, (const xmlChar *)"top", (const xmlChar *)&tmp);
        snprintf(tmp,TMP_STRLEN,"%f", maxx + border * 20);
        xmlSetProp(node, (const xmlChar *)"right", (const xmlChar *)&tmp);
        snprintf(tmp,TMP_STRLEN,"%f", maxy + border * 20);
        xmlSetProp(node, (const xmlChar *)"bottom", (const xmlChar *)&tmp);
}

}

/* [<][>][^][v][top][bottom][index][help] */