#help_index "Graphics/Sprite;Sprites"

CSprite *SpriteSetSettings(CDC *dc=NULL, CSprite *head, I64 elem_num, I64 x=0, I64 y=0,
                                                   CColorROPU32 *_color=NULL, I64 *_thick=NULL, I64 *_xx=NULL, I64 *_yy=NULL)
{
        CSprite                 *res = head->next;
        I64                              thick = 1, xx = 0, yy = 0;
        CColorROPU32     color = BLACK;

        if (dc)
                DCReset(dc);
        while (elem_num-- > 0 && res != head)
        {
                switch (res->type & SPG_TYPE_MASK)
                {
                        case SPT_COLOR:
                                color = res->c.color;
                                if (dc)
                                        dc->color = color;
                                break;

                        case SPT_DITHER_COLOR:
                                color = res->d.dither_color.u8[0] | res->d.dither_color.u8[1] << COLORROP_BITS | ROPF_DITHER;
                                if (dc)
                                        dc->color = color;
                                break;

                        case SPT_THICK:
                                thick = res->t.thick;
                                if (dc)
                                        dc->thick = thick;
                                break;

                        case SPT_SHIFT:
                                xx += res->p.x1;
                                yy += res->p.y1;
                                x  += res->p.x1;
                                y  += res->p.y1;
                                break;

                        case SPT_PLANAR_SYMMETRY:
                                if (dc)
                                {
                                        if (DCSymmetry3Set(dc,
                                                                           res->pp.x1 + x, res->pp.y1 + y, 0,
                                                                           res->pp.x2 + x, res->pp.y2 + y, 0,
                                                                           res->pp.x2 + x, res->pp.y2 + y, 1))
                                                dc->flags |= DCF_SYMMETRY;
                                        else
                                                dc->flags &= ~DCF_SYMMETRY;
                                }
                                break;
                }
                res = res->next;
        }
        if (_color)
                *_color = color;
        if (_thick)
                *_thick = thick;
        if (_xx)
                *_xx = xx;
        if (_yy)
                *_yy = yy;

        return res;
}

Bool SpritePolyPtPlot(CSprite *head, I64 x, I64 y, I64)
{
        CSprite *tmpg = CAlloc(SpriteElemQueuedBaseSize(SPT_PT));


        tmpg->type = SPT_PT;
        tmpg->p.x1 = x;
        tmpg->p.y1 = y;
        QueueInsert(tmpg, head->last);

        return TRUE;
}

CSprite *Sprite2SpriteQueue(U8 *elems)
{
        I64              s;
        CSprite *res = CAlloc(sizeof(CSprite)), *tmpg = elems - offset(CSprite.start), *tmpg1;

        QueueInit(res);
        while (tmpg->type & SPG_TYPE_MASK)
        {
                tmpg1 = MAlloc(SpriteElemSize(tmpg) + offset(CSprite.start));
                s = SpriteElemSize(tmpg);
                MemCopy(&tmpg1->start, &tmpg->start, s);
                QueueInsert(tmpg1, res->last);
                tmpg(U8 *) += s;
        }

        return res;
}

U8 *SpriteQueue2Sprite(CSprite *head, I64 *_size=NULL)
{
        I64              i, size = sprite_elem_base_sizes[SPT_END];
        CSprite *tmpg = head->next;
        U8              *res, *dst;

        while (tmpg != head)
        {
                size += SpriteElemSize(tmpg);
                tmpg = tmpg->next;
        }
        if (_size)
                *_size = size;
        res = dst = MAlloc(size);
        tmpg = head->next;
        while (tmpg != head)
        {
                i = SpriteElemSize(tmpg);
                MemCopy(dst, &tmpg->start, i);
                dst += i;
                tmpg = tmpg->next;
        }
        *dst = SPT_END;

        return res;
}

U0 SpriteEdUpdate(CDoc *doc, CDocEntry *doc_ce, CSprite *head)
{
        CDocBin *tmpb = doc_ce->bin_data;
        I64              size;
        Bool     unlock = DocLock(doc);

        Free(tmpb->data);
        tmpb->data = SpriteQueue2Sprite(head, &size);
        tmpb->size = size;
        if (unlock)
                DocUnlock(doc);
}

U0 SpriteSetOrigin(CSprite *head, I64 dx, I64 dy, I64 dz)
{
        I64              i;
        I32             *ptr;
        CD3I32  *p;
        CSprite *tmpg = head->next;

        while (tmpg != head)
        {
                if (Bt(&tmpg->type, SPf_SEL))
                        switch (tmpg->type & SPG_TYPE_MASK)
                        {
                                case SPT_ARROW:
                                case SPT_LINE:
                                case SPT_PLANAR_SYMMETRY:
                                case SPT_RECT:
                                case SPT_ROTATED_RECT:
                                        tmpg->pp.x2 += dx;
                                        tmpg->pp.y2 += dy;
                                case SPT_PT:
                                case SPT_FLOOD_FILL:
                                case SPT_FLOOD_FILL_NOT:
                                case SPT_TEXT:
                                case SPT_TEXT_BOX:
                                case SPT_TEXT_DIAMOND:
                                case SPT_CIRCLE:
                                case SPT_BITMAP:
                                case SPT_ELLIPSE:
                                case SPT_POLYGON:
                                        tmpg->p.x1 += dx;
                                        tmpg->p.y1 += dy;
                                        break;

                                case SPT_POLYLINE:
                                        ptr = &tmpg->nu.u;
                                        for (i = 0; i < tmpg->nu.num; i++)
                                        {
                                                ptr[i << 1]     += dx;
                                                ptr[i << 1 + 1] += dy;
                                        }
                                        break;

                                case SPT_POLYPT:
                                        tmpg->npu.x += dx;
                                        tmpg->npu.y += dy;
                                        break;

                                case SPT_BSPLINE2:
                                case SPT_BSPLINE3:
                                case SPT_BSPLINE2_CLOSED:
                                case SPT_BSPLINE3_CLOSED:
                                        p = &tmpg->nu.u;
                                        for (i = 0; i < tmpg->nu.num; i++, p++)
                                        {
                                                p->x += dx;
                                                p->y += dy;
                                                p->z += dz;
                                        }
                                        break;

                                case SPT_MESH:
                                        p = &tmpg->mu.u;
                                        for (i = 0; i < tmpg->mu.vertex_count; i++, p++)
                                        {
                                                p->x += dx;
                                                p->y += dy;
                                                p->z += dz;
                                        }
                                        break;

                                case SPT_SHIFTABLE_MESH:
                                        tmpg->pmu.x += dx;
                                        tmpg->pmu.y += dy;
                                        tmpg->pmu.z += dz;
                                        break;
                        }
                tmpg = tmpg->next;
        }
}

CSprite *SpriteTransformCircle(I64 *r, CSprite *tmpg)
{
        I64              x, y, z;
        F64              m1, arg1, m2, radius = tmpg->pr.radius << 16;
        CSprite *tmpg1 = CAlloc(SpriteElemQueuedBaseSize(SPT_ELLIPSE));

        tmpg1->type = SPT_ELLIPSE;

        x = tmpg->pr.x1;
        y = tmpg->pr.y1;
        z = 0;
        Mat4x4MulXYZ(r, &x, &y, &z);
        tmpg1->pwha.x1 = x;
        tmpg1->pwha.y1 = y;

        x = radius;
        y = 0;
        z = 0;
        Mat4x4MulXYZ(r, &x, &y, &z);
        R2P(&m1, &arg1, x, y);

        x = 0;
        y = radius;
        z = 0;
        Mat4x4MulXYZ(r, &x, &y, &z);
        m2 = Sqrt(x * x + y * y);

        tmpg1->pwha.width  = ToI64(m1) / 0x10000;
        tmpg1->pwha.height = ToI64(m2) / 0x10000;
        tmpg1->pwha.angle  = -arg1;

        tmpg1->type |= tmpg->type & SPF_SEL;

        return tmpg1;
}

CSprite *SpriteTransformEllipse(I64 *r, CSprite *tmpg)
{
        I64              x, y, z;
        F64              m1, arg1, m2, arg2, s, c, x_radius = tmpg->pwha.width << 16, y_radius = tmpg->pwha.height << 16;
        CSprite *tmpg1 = CAlloc(SpriteElemQueuedBaseSize(tmpg->type & SPG_TYPE_MASK));

        tmpg1->type = tmpg->type;
        if (tmpg->type & SPG_TYPE_MASK == SPT_POLYGON)
                tmpg1->pwhas.sides = tmpg->pwhas.sides;

        x = tmpg->pwha.x1;
        y = tmpg->pwha.y1;
        z = 0;
        Mat4x4MulXYZ(r, &x, &y, &z);
        tmpg1->pwha.x1 = x;
        tmpg1->pwha.y1 = y;

        c = Cos(-tmpg->pwha.angle);
        s = Sin(-tmpg->pwha.angle);

        x = x_radius * c;
        y = x_radius * s;
        z = 0;
        Mat4x4MulXYZ(r, &x, &y, &z);
        R2P(&m1, &arg1, x, y);

        x = -y_radius * s;
        y = y_radius * c;
        z = 0;
        Mat4x4MulXYZ(r, &x, &y, &z);
        R2P(&m2, &arg2, x, y);
        m2 *= Abs(Sin(arg2 - arg1));

        tmpg1->pwha.width = ToI64(m1) / 0x10000;
        if (tmpg1->pwha.width < 1)
                tmpg1->pwha.width = 1;
        tmpg1->pwha.height = ToI64(m2) / 0x10000;
        if (tmpg1->pwha.height < 1)
                tmpg1->pwha.height = 1;
        tmpg1->pwha.angle = -arg1;

        tmpg1->type |= tmpg->type & SPF_SEL;

        return tmpg1;
}

CSprite *SpriteTransformRect(I64 *r, CSprite *tmpg, F64 theta)
{
        I64              x, y, z, w, h;
        F64              m1, arg1, m2, arg2, s, c, 
                         x_radius = (tmpg->pp.x2 - tmpg->pp.x1) << 16, 
                         y_radius = (tmpg->pp.y2 - tmpg->pp.y1) << 16;
        CSprite *tmpg1 = CAlloc(SpriteElemQueuedBaseSize(SPT_ROTATED_RECT));

        tmpg1->type = SPT_ROTATED_RECT;

        x = tmpg->pp.x1;
        y = tmpg->pp.y1;
        z = 0;
        Mat4x4MulXYZ(r, &x, &y, &z);
        tmpg1->ppa.x1 = x;
        tmpg1->ppa.y1 = y;

        c = Cos(-theta);
        s = Sin(-theta);

        x = x_radius * c;
        y = x_radius * s;
        z = 0;
        Mat4x4MulXYZ(r, &x, &y, &z);
        R2P(&m1, &arg1, x, y);

        x = -y_radius * s;
        y = y_radius * c;
        z = 0;
        Mat4x4MulXYZ(r, &x, &y, &z);
        R2P(&m2, &arg2, x, y);
        m2 *= Abs(Sin(arg2 - arg1));

        w = ToI64(m1) / 0x10000;
        if (w < 1)
                w = 1;
        h = ToI64(m2) / 0x10000;
        if (h < 1)
                h = 1;
        tmpg1->ppa.x2 = tmpg1->ppa.x1 + w;
        tmpg1->ppa.y2 = tmpg1->ppa.y1 + h;
        tmpg1->ppa.angle = -arg1;

        tmpg1->type |= tmpg->type & SPF_SEL;

        return tmpg1;
}

CSprite *SpriteTransformBitMap(I64 *r, CSprite *tmpg)
{
        CDC             *img, *dc3;
        U8              *elems;
        I64              x, y, z, minx, maxx, miny, maxy, minz, maxz;
        CSprite *tmpg1;

        x = tmpg->pwhu.x1;
        y = tmpg->pwhu.y1;
        z = 0;
        Mat4x4MulXYZ(r, &x, &y, &z);
        minx = maxx = x;
        miny = maxy = y;
        minz = maxz = z;

        x = tmpg->pwhu.x1;
        y = tmpg->pwhu.y1 + tmpg->pwhu.height;
        z = 0;
        Mat4x4MulXYZ(r, &x, &y, &z);
        if (x < minx) minx = x;
        if (x > maxx) maxx = x;
        if (y < miny) miny = y;
        if (y > maxy) maxy = y;
        if (z < minz) minz = z;
        if (z > maxz) maxz = z;

        x = tmpg->pwhu.x1 + tmpg->pwhu.width;
        y = tmpg->pwhu.y1;
        z = 0;
        Mat4x4MulXYZ(r, &x, &y, &z);
        if (x < minx) minx = x;
        if (x > maxx) maxx = x;
        if (y < miny) miny = y;
        if (y > maxy) maxy = y;
        if (z < minz) minz = z;
        if (z > maxz) maxz = z;

        x = tmpg->pwhu.x1 + tmpg->pwhu.width;
        y = tmpg->pwhu.y1 + tmpg->pwhu.height;
        z = 0;
        Mat4x4MulXYZ(r, &x, &y, &z);
        if (x < minx) minx = x;
        if (x > maxx) maxx = x;
        if (y < miny) miny = y;
        if (y > maxy) maxy = y;
        if (z < minz) minz = z;
        if (z > maxz) maxz = z;

        dc3 = DCNew(maxx - minx + 1, maxy - miny + 1);

        img = CAlloc(sizeof(CDC));
        img->width                      = tmpg->pwhu.width;
        img->width_internal     = (tmpg->pwhu.width + 7) & ~7;
        img->height                     = tmpg->pwhu.height;
        img->body                       = &tmpg->pwhu.u;
        img->dc_signature       = DCS_SIGNATURE_VAL;

        dc3->color = TRANSPARENT;
        GrRect(dc3, 0, 0, maxx - minx + 1, maxy - miny + 1);

        Free(dc3->r);
        DCMat4x4Set(dc3, r);
        dc3->flags |= DCF_TRANSFORMATION;

        dc3->x = tmpg->pwhu.x1 - minx;
        dc3->y = tmpg->pwhu.y1 - miny;
        dc3->z = -minz;
        GrBlot3(dc3, 0, 0, 0, img);
        Free(img);

        elems = DC2Sprite(dc3);
        dc3->r = NULL;
        DCDel(dc3);
        tmpg1 = CAlloc(offset(CSprite.start) + MSize(elems));
        MemCopy(tmpg1(U8 *) + offset(CSprite.start), elems, MSize(elems));
        tmpg1->type = tmpg->type;

        x = tmpg->pwhu.x1;
        y = tmpg->pwhu.y1;
        z = 0;
        Mat4x4MulXYZ(r, &x, &y, &z);
        tmpg1->pwhu.x1 = x;
        tmpg1->pwhu.y1 = y;

        return tmpg1;
}

U0 SpriteTransformQueue(CSprite *head, I64 *r)
{
        I64              i, j, k, num, x, y, z, x1, y1, z1, x2, y2, z2, x3, y3, z3;
        I32             *ptr;
        CD3I32  *p;
        CSprite *tmpg = head->next, head2, *tmpg1, *tmpg2, *tmpg3;

        while (tmpg != head)
        {
                if (Bt(&tmpg->type, SPf_SEL))
                        switch (tmpg->type & SPG_TYPE_MASK)
                        {
                                case SPT_THICK:
                                        tmpg->t.thick *= Sqrt(Mat4x4NormSqr65536(r)) / 65536;
                                        if (tmpg->t.thick < 0)
                                                tmpg->t.thick = 0;
                                        break;

                                case SPT_PLANAR_SYMMETRY:
                                case SPT_ARROW:
                                case SPT_LINE:
                                        x = tmpg->pp.x2;
                                        y = tmpg->pp.y2;
                                        z = 0;
                                        Mat4x4MulXYZ(r, &x, &y, &z);
                                        tmpg->pp.x2 = x;
                                        tmpg->pp.y2 = y;
                                case SPT_PT:
                                case SPT_FLOOD_FILL:
                                case SPT_FLOOD_FILL_NOT:
                                case SPT_TEXT:
                                case SPT_TEXT_BOX:
                                case SPT_TEXT_DIAMOND:
                                        x = tmpg->p.x1;
                                        y = tmpg->p.y1;
                                        z = 0;
                                        Mat4x4MulXYZ(r, &x, &y, &z);
                                        tmpg->p.x1 = x;
                                        tmpg->p.y1 = y;
                                        break;

                                case SPT_BITMAP:
                                        tmpg1 = SpriteTransformBitMap(r, tmpg);
                                        QueueInsert(tmpg1, tmpg);
                                        QueueRemove(tmpg);
                                        Free(tmpg);
                                        tmpg = tmpg1;
                                        break;

                                case SPT_ROTATED_RECT:
                                        tmpg1 = SpriteTransformRect(r, tmpg, tmpg->ppa.angle);
                                        QueueInsert(tmpg1, tmpg);
                                        QueueRemove(tmpg);
                                        Free(tmpg);
                                        tmpg = tmpg1;
                                        break;

                                case SPT_RECT:
                                        tmpg1 = SpriteTransformRect(r, tmpg, 0);
                                        QueueInsert(tmpg1, tmpg);
                                        QueueRemove(tmpg);
                                        Free(tmpg);
                                        tmpg = tmpg1;
                                        break;

                                case SPT_CIRCLE:
                                        tmpg1 = SpriteTransformCircle(r, tmpg);
                                        QueueInsert(tmpg1, tmpg);
                                        QueueRemove(tmpg);
                                        Free(tmpg);
                                        tmpg = tmpg1;
                                        break;

                                case SPT_ELLIPSE:
                                case SPT_POLYGON:
                                        tmpg1 = SpriteTransformEllipse(r, tmpg);
                                        QueueInsert(tmpg1, tmpg);
                                        QueueRemove(tmpg);
                                        Free(tmpg);
                                        tmpg = tmpg1;
                                        break;

                                case SPT_POLYLINE:
                                        ptr = &tmpg->nu.u;
                                        for (i = 0; i < tmpg->nu.num; i++)
                                        {
                                                x = ptr[i << 1];
                                                y = ptr[i << 1 + 1];
                                                z = 0;
                                                Mat4x4MulXYZ(r, &x, &y, &z);
                                                ptr[i << 1]             = x;
                                                ptr[i << 1 + 1] = y;
                                        }
                                        break;

                                case SPT_POLYPT:
                                        QueueInit(&head2);
                                        x = tmpg->npu.x;
                                        y = tmpg->npu.y;
                                        z = 0;
                                        x1 = x;  //
                                        y1 = y;  //
                                        z1 = z;  //unrotated cur coordinates
                                        Mat4x4MulXYZ(r, &x, &y, &z);
                                        ptr = &tmpg->npu.u;
                                        k = tmpg->npu.num * 3;
                                        x2 = x;  //
                                        y2 = y;  //
                                        z2 = z;  //rotated start coordinates

                                        x3 = x;  //
                                        y3 = y;  //
                                        z3 = z;  //lag 1 rotated coordinates
                                        for (i = 0; i < k; i += 3)
                                        {
                                                j = BFieldExtU32(ptr, i, 3);
                                                x1 += gr_x_offsets[j];
                                                y1 += gr_y_offsets[j];
                                                x = x1;
                                                y = y1;
                                                z = z1;
                                                Mat4x4MulXYZ(r, &x, &y, &z);
                                                Line(&head2, x3 - x2, y3 - y2, 0, x - x2, y - y2, 0, &SpritePolyPtPlot);
                                                x3 = x;
                                                y3 = y;
                                                z3 = z;
                                        }

                                        num = 0;
                                        tmpg1 = head2.next;
                                        x3 = 0;
                                        y3 = 0;
                                        z3 = 0;
                                        while (tmpg1 != &head2)
                                        {
                                                tmpg2 = tmpg1->next;
                                                if (tmpg1->p.x1 == x3 && tmpg1->p.y1 == y3)
                                                {
                                                        QueueRemove(tmpg1);
                                                        Free(tmpg1);
                                                }
                                                else
                                                {
                                                        num++;
                                                        x3 = tmpg1->p.x1;
                                                        y3 = tmpg1->p.y1;
                                                }
                                                tmpg1 = tmpg2;
                                        }

                                        tmpg3 = CAlloc(SpriteElemQueuedBaseSize(SPT_POLYPT) + (num * 3 + 7) >> 3);
                                        tmpg3->npu.x = x2;
                                        tmpg3->npu.y = y2;
                                        ptr = &tmpg3->npu.u;
                                        x3 = 0;
                                        y3 = 0;
                                        z3 = 0;
                                        i = 0;
                                        tmpg1 = head2.next;
                                        while (tmpg1 != &head2)
                                        {
                                                tmpg2 = tmpg1->next;
                                                BFieldOrU32(ptr, i, polypt_map[SignI64(tmpg1->p.x1 - x3) + 1 + 3 * (SignI64(tmpg1->p.y1 - y3) + 1)]);
                                                i += 3;
                                                x3 = tmpg1->p.x1;
                                                y3 = tmpg1->p.y1;
                                                QueueRemove(tmpg1);
                                                Free(tmpg1);
                                                tmpg1 = tmpg2;
                                        }
                                        tmpg3->type = SPT_POLYPT | tmpg->type & SPF_SEL;
                                        tmpg3->npu.num = num;
                                        QueueInsert(tmpg3, tmpg);
                                        QueueRemove(tmpg);
                                        Free(tmpg);
                                        tmpg = tmpg3;
                                        break;

                                case SPT_BSPLINE2:
                                case SPT_BSPLINE3:
                                case SPT_BSPLINE2_CLOSED:
                                case SPT_BSPLINE3_CLOSED:
                                        p = &tmpg->nu.u;
                                        for (i = 0; i < tmpg->nu.num; i++, p++)
                                        {
                                                x = p->x;
                                                y = p->y;
                                                z = p->z;
                                                Mat4x4MulXYZ(r, &x, &y, &z);
                                                p->x = x;
                                                p->y = y;
                                                p->z = z;
                                        }
                                        break;

                                case SPT_SHIFTABLE_MESH:
                                        x = tmpg->pmu.x;
                                        y = tmpg->pmu.y;
                                        z = tmpg->pmu.z;
                                        Mat4x4MulXYZ(r, &x, &y, &z);
                                        tmpg->pmu.x = x;
                                        tmpg->pmu.y = y;
                                        tmpg->pmu.z = z;
                                        p = &tmpg->pmu.u;
                                        for (i = 0; i < tmpg->pmu.vertex_count; i++, p++)
                                        {
                                                x = p->x;
                                                y = p->y;
                                                z = p->z;
                                                Mat4x4MulXYZ(r, &x, &y, &z);
                                                p->x = x;
                                                p->y = y;
                                                p->z = z;
                                        }
                                        break;

                                case SPT_MESH:
                                        p = &tmpg->mu.u;
                                        for (i = 0; i < tmpg->mu.vertex_count; i++, p++)
                                        {
                                                x = p->x;
                                                y = p->y;
                                                z = p->z;
                                                Mat4x4MulXYZ(r, &x, &y, &z);
                                                p->x = x;
                                                p->y = y;
                                                p->z = z;
                                        }
                                        break;
                        }
                tmpg = tmpg->next;
        }
}

I64 SpriteQueueSelCount(CSprite *head, Bool val=TRUE)
{
        I64              res = 0;
        CSprite *tmpg = head->next;

        val = ToBool(val);
        while (tmpg != head)
        {
                if (Bt(&tmpg->type, SPf_SEL) == val)
                        res++;
                tmpg = tmpg->next;
        }

        return res;
}

I64 SpriteQueueSelAll(CSprite *head, Bool val=TRUE)
{
        I64              res = 0;
        CSprite *tmpg = head->next;

        while (tmpg != head)
        {
                BEqual(&tmpg->type, SPf_SEL, val);
                res++;
                tmpg = tmpg->next;
        }

        return res;
}

Bool SpriteEdText(CSprite **_head, I64 *_cur_elem_num)
{
        Bool     res;
        CSprite *head = *_head;
        U8              *elems = SpriteQueue2Sprite(head);
        CDoc    *doc = DocNew, *doc2, *old_put = DocPut;

        StrPrint(doc->filename.name, "AI:0x%X", doc);
        DocPrint(doc,
                                "//$PURPLE$$TX+CX,\"Sprite Edit as Text\"$$FG$\n"
                                "//$LK+PU+CX,\"Click for Help\","
                                "A=\"FI:::/Doc/SpriteEdText.DD\"$\n\n");
        Sprite2Code(doc, elems);
        Free(elems);
        while (TRUE)
        {
                if (res = PopUpPrint("DocEd(0x%X,0x%X);", doc, 0))
                {
                        Fs->put_doc = doc2 = DocNew;
                        "$WW,1$";
                        if (elems = Code2Sprite(doc))
                        {
                                DocDel(doc2);
                                Fs->put_doc = old_put;
                                QueueDel(head);
                                Free(head);
                                head = Sprite2SpriteQueue(elems);
                                Free(elems);
                                *_cur_elem_num = QueueCount(head); //TODO: Might want to improve this.
                                break;
                        }
                        else
                        {
                                PopUpPrint("DocEd(0x%X,0x%X);", doc2, 0);
                                DocDel(doc2);
                                Fs->put_doc = old_put;
                        }
                }
                else
                        break;
        }
        DocDel(doc);
        if (_head)
                *_head = head;

        return res;
}

#define SPED_SEL_UNSEL_ALL              0
#define SPED_SEL                                2
#define SPED_SEL_RECTS                  3
#define SPED_UNSEL                              4
#define SPED_UNSEL_RECTS                5
#define SPED_SHIFT_PTS                  6
#define SPED_SHIFT_RECTS                7
#define SPED_SHIFT_SEL                  8
#define SPED_TRANSFORM_SEL              9
#define SPED_SET_ORIGIN                 10
#define SPED_SHIFT_SUB_ORIGIN   11
#define SPED_TEXT_ED                    12
#define SPED_INS_CLIP                   13
#define SPED_MAIN_MENU                  14
#define SPED_EXIT                               15

U0 GrInit3()
{
        DefineListLoad("ST_SPRITE_ED_MENU",

                                                "Select/Unselect All\0"
                                                " \0"
                                                "Select\0"
                                                "Select Rects\0"
                                                "Unselect\0"
                                                "Unselect Rects\0"
                                                "Shift Points\0"
                                                "Shift Rects\0"
                                                "Shift Selected\0"
                                                "Transform Selected\0"
                                                "Set Origin\0"
                                                "Insert Shift SubOrigin\0"
                                                "Edit as Text\0"
                                                "Insert Clip\0"
                                                "Main Menu\0");
}
GrInit3;

I64 PopUpSpriteEd(CSprite **_head, I64 *_cur_elem_num)
{
        U8              *st;
        CTask   *pu_task;
        I64              res;
        CDoc    *doc = DocNew;

        DocPrint(doc,
                                "$PURPLE$$TX+CX,\"Sprite Edit Menu\"$\n"
                                "$LK+PU+CX,\"Click for Help\",A=\"FI:::/Doc/SpriteEd.DD\"$\n\n"
                                "$LTBLUE$$MU-UL,\"Select/Unselect All\",LE=SPED_SEL_UNSEL_ALL$\n"
                                "$MU-UL,\"Select Elems\",LE=SPED_SEL$\n"
                                "$MU-UL,\"Select Elems with Rects\",LE=SPED_SEL_RECTS$\n"
                                "$MU-UL,\"Unsel Elems\",LE=SPED_UNSEL$\n"
                                "$MU-UL,\"Unsel Elems with Rects\",LE=SPED_UNSEL_RECTS$\n\n"
                                "$MU-UL,\"Shift Points\",LE=SPED_SHIFT_PTS$\n"
                                "$MU-UL,\"Shift Points with Rects\",LE=SPED_SHIFT_RECTS$\n"
                                "$MU-UL,\"Shift Selected Elems\",LE=SPED_SHIFT_SEL$\n"
                                "$MU-UL,\"Transform Selected Elems\",LE=SPED_TRANSFORM_SEL$\n\n"
                                "$MU-UL,\"Set Origin\",LE=SPED_SET_ORIGIN$\n"
                                "$MU-UL,\"Insert Shift SubOrigin\",LE=SPED_SHIFT_SUB_ORIGIN$\n\n"
                                "$MU-UL,\"Edit as Text\",LE=SPED_TEXT_ED$\n"
                                "$MU-UL,\"Insert Clip Sprite's\",LE=SPED_INS_CLIP$\n\n"
                                "$PURPLE$$MU-UL,\"+] Sprite Main Menu\",LE=SPED_MAIN_MENU$$LTBLUE$\n"
                                "$MU-UL,\"Exit  Sprite\",LE=SPED_EXIT$\n"
                                "$MU-UL,\"Abort Sprite\",LE=DOCM_CANCEL$");

        st = MStrPrint("SpriteSideBarTask(0x%X,0x%X,0x%X);", Fs, _head, _cur_elem_num);
        PopUp(st, NULL, &pu_task);
        Free(st);
        res = PopUpMenu(doc);
        if (TaskValidate(pu_task))
        {
                *_head = SpriteSideBar2SpriteQueue(DocPut(pu_task), *_head, _cur_elem_num);
                Kill(pu_task);
        }
        DocDel(doc);

        return res;
}

#define SPEDT_SIMPLE_PT         0
#define SPEDT_WIDTH_HEIGHT      1

#define SPEDF_SEL                       1

class CEdSprite
{
        CEdSprite       *next, *last;
        CSprite         *g;
        I32                      type, num, flags, xx, yy, zz;
        I32                     *x, *y, *z, *w, *h;
};

CEdSprite *EdSpriteNew(I64 type, CSprite *tmpg)
{
        CEdSprite *res = CAlloc(sizeof(CEdSprite));

        res->g = tmpg;
        if (tmpg->type & SPF_SEL)
                res->flags |= SPEDF_SEL;
        res->type = type;

        return res;
}

U0 SpritePtQueueNew(U8 *elems, I64 x, I64 y, CEdSprite *head)
{
        I64                      i, num = 0;
        I32                     *ptr;
        CD3I32          *p;
        CEdSprite       *tmpes;
        CSprite         *tmpg = elems - offset(CSprite.start);

        QueueInit(head);
        while (tmpg->type & SPG_TYPE_MASK)
        {
                switch (tmpg->type & SPG_TYPE_MASK)
                {
                        case SPT_ELLIPSE:
                        case SPT_POLYGON:
                                tmpes = EdSpriteNew(SPEDT_WIDTH_HEIGHT, tmpg);
                                tmpes->xx       = x;
                                tmpes->yy       = y;
                                tmpes->x        = &tmpg->pwha.x1;
                                tmpes->y        = &tmpg->pwha.y1;
                                tmpes->w        = &tmpg->pwha.width;
                                tmpes->h        = &tmpg->pwha.height;
                                tmpes->num      = num;
                                QueueInsert(tmpes, head->last);
                                goto pq_x1_y1;

                        case SPT_RECT:
                        case SPT_ROTATED_RECT:
                        case SPT_LINE:
                        case SPT_ARROW:
                        case SPT_PLANAR_SYMMETRY:
                                tmpes = EdSpriteNew(SPEDT_SIMPLE_PT, tmpg);
                                tmpes->xx       = x;
                                tmpes->yy       = y;
                                tmpes->x        = &tmpg->pp.x2;
                                tmpes->y        = &tmpg->pp.y2;
                                tmpes->num      = num;
                                QueueInsert(tmpes, head->last);
                        case SPT_TEXT:
                        case SPT_TEXT_BOX:
                        case SPT_TEXT_DIAMOND:
                        case SPT_PT:
                        case SPT_BITMAP:
                        case SPT_FLOOD_FILL:
                        case SPT_FLOOD_FILL_NOT:
                        case SPT_CIRCLE:
pq_x1_y1:
                                tmpes = EdSpriteNew(SPEDT_SIMPLE_PT, tmpg);
                                tmpes->xx       = x;
                                tmpes->yy       = y;
                                tmpes->x        = &tmpg->p.x1;
                                tmpes->y        = &tmpg->p.y1;
                                tmpes->num      = num;
                                QueueInsert(tmpes, head->last);
                                break;

                        case SPT_SHIFT:
                                x += tmpg->p.x1;
                                y += tmpg->p.y1;
                                break;

                        case SPT_POLYLINE:
                                ptr = &tmpg->nu.u;
                                for (i = 0; i < tmpg->nu.num; i++)
                                {
                                        tmpes = EdSpriteNew(SPEDT_SIMPLE_PT, tmpg);
                                        tmpes->xx       = x;
                                        tmpes->yy       = y;
                                        tmpes->x        = &ptr[i << 1];
                                        tmpes->y        = &ptr[i << 1 + 1];
                                        tmpes->num      = num;
                                        QueueInsert(tmpes, head->last);
                                }
                                break;

                        case SPT_POLYPT:
                                tmpes = EdSpriteNew(SPEDT_SIMPLE_PT, tmpg);
                                tmpes->xx       = x;
                                tmpes->yy       = y;
                                tmpes->x        = &tmpg->npu.x;
                                tmpes->y        = &tmpg->npu.y;
                                tmpes->num      = num;
                                QueueInsert(tmpes, head->last);
                                break;

                        case SPT_BSPLINE2:
                        case SPT_BSPLINE3:
                        case SPT_BSPLINE2_CLOSED:
                        case SPT_BSPLINE3_CLOSED:
                                p = &tmpg->nu.u;
                                for (i = 0; i < tmpg->nu.num; i++)
                                {
                                        tmpes = EdSpriteNew(SPEDT_SIMPLE_PT, tmpg);
                                        tmpes->xx       = x;
                                        tmpes->yy       = y;
                                        tmpes->x        = &p[i].x;
                                        tmpes->y        = &p[i].y;
                                        tmpes->z        = &p[i].z;
                                        tmpes->num      = num;
                                        QueueInsert(tmpes, head->last);
                                }
                                break;

                        case SPT_MESH:
                                break;

                        case SPT_SHIFTABLE_MESH:
                                tmpes = EdSpriteNew(SPEDT_SIMPLE_PT, tmpg);
                                tmpes->xx       = x;
                                tmpes->yy       = y;
                                tmpes->x        = &tmpg->pmu.x;
                                tmpes->y        = &tmpg->pmu.y;
                                tmpes->z        = &tmpg->pmu.z;
                                tmpes->num      = num;
                                QueueInsert(tmpes, head->last);
                                break;
                }
                tmpg(U8 *) += SpriteElemSize(tmpg);
                num++;
        }
}

U0 SpriteCtrlPtsDraw(CDC *dc, CEdSprite *head)
{
        I64                      x, y;
        CEdSprite       *tmpes;

        Refresh;
        DCFill(dc);
        if (Blink(20))
        {
                tmpes = head->next;
                while (tmpes != head)
                {
                        switch (tmpes->type)
                        {
                                case SPEDT_SIMPLE_PT:
                                        x = *tmpes->x + tmpes->xx;
                                        y = *tmpes->y + tmpes->yy;
                                        break;
                                case SPEDT_WIDTH_HEIGHT:
                                        x = *tmpes->w + *tmpes->x + tmpes->xx;
                                        y = *tmpes->h + *tmpes->y + tmpes->yy;
                                        break;
                        }
                        if (tmpes->flags & SPEDF_SEL)
                                dc->color = RED;
                        else
                                dc->color = BLACK;
                        GrRect(dc, x - 2, y - 2, 4, 4);
                        dc->color = WHITE;
                        GrRect(dc, x - 1, y - 1, 2, 2);
                        tmpes = tmpes->next;
                }
        }
}

U0 SpriteCtrlPtsMove(CEdSprite *head, I64 dx, I64 dy)
{
        CEdSprite *tmpes;

        tmpes = head->next;
        while (tmpes != head)
        {
                if (tmpes->flags & SPEDF_SEL)
                        switch (tmpes->type)
                        {
                                case SPEDT_SIMPLE_PT:
                                        if (tmpes->x)
                                                *tmpes->x += dx;
                                        if (tmpes->y)
                                                *tmpes->y += dy;
                                        break;
                                case SPEDT_WIDTH_HEIGHT:
                                        if (tmpes->w)
                                                *tmpes->w += dx;
                                        if (tmpes->h)
                                                *tmpes->h += dy;
                                        break;
                        }
                tmpes = tmpes->next;
        }
}

Bool SpriteSelUnselShiftPts(U8 *elems, I64 x, I64 y, I64 *_cur_elem_num, I64 mode)
{
        I64                      message_code, arg1, arg2, xx, yy, xx2, yy2, dd, best_dd, cur_elem_num;
        Bool             res = TRUE;
        CDC                     *dc = DCAlias;
        CEdSprite        head, *tmpes, *best_es;

        SpritePtQueueNew(elems, x, y, &head);
        cur_elem_num = 0;
        if (head.next != &head)
        {
                while (TRUE)
                {
                        SpriteCtrlPtsDraw(dc, &head); //has Refresh
                        switch (message_code = MessageScan(&arg1, &arg2, 
                                                                                           1 << MESSAGE_MS_R_UP | 1 << MESSAGE_MS_L_DOWN | 1 << MESSAGE_KEY_DOWN))
                        {
                                case MESSAGE_MS_L_DOWN:
                                        switch (mode)
                                        {
                                                case SPED_SEL:
                                                case SPED_UNSEL:
                                                case SPED_SHIFT_PTS:
                                                        xx = arg1;
                                                        yy = arg2;
                                                        best_dd = I64_MAX;
                                                        tmpes = head.next;
                                                        while (tmpes != &head)
                                                        {
                                                                switch (tmpes->type)
                                                                {
                                                                        case SPEDT_SIMPLE_PT:
                                                                                dd = SqrI64(*tmpes->x + tmpes->xx - xx) + SqrI64(*tmpes->y + tmpes->yy - yy);
                                                                                break;
                                                                        case SPEDT_WIDTH_HEIGHT:
                                                                                dd = SqrI64(*tmpes->x + *tmpes->w + tmpes->xx - xx) +
                                                                                         SqrI64(*tmpes->y + *tmpes->h + tmpes->yy - yy);
                                                                                break;
                                                                }
                                                                if (dd < best_dd)
                                                                {
                                                                        best_dd = dd;
                                                                        best_es = tmpes;
                                                                }
                                                                tmpes = tmpes->next;
                                                        }
                                                        cur_elem_num = best_es->num;
                                                        if (mode != SPED_UNSEL)
                                                        {
                                                                best_es->flags |= SPEDF_SEL;
                                                                best_es->g->type |= SPF_SEL;
                                                        }
                                                        else
                                                        {
                                                                best_es->flags &= ~SPEDF_SEL;
                                                                best_es->g->type &= ~SPF_SEL;
                                                        }
                                                        break;
                                                start:
                                                        xx2 = xx = arg1;
                                                        yy2 = yy = arg2;
                                                        while (TRUE)
                                                        {
                                                                SpriteCtrlPtsDraw(dc, &head);
                                                                dc->color = ROPF_DITHER + WHITE << 16 + RED;
                                                                GrBorder(dc, xx, yy, xx2, yy2);
                                                                if (message_code = MessageScan(&arg1, &arg2, 1 << MESSAGE_MS_MOVE | 1 << MESSAGE_MS_L_UP))
                                                                {
                                                                        if (message_code == MESSAGE_MS_MOVE)
                                                                        {
                                                                                xx2 = arg1;
                                                                                yy2 = arg2;
                                                                        }
                                                                        else
                                                                                break;
                                                                }
                                                        }
                                                        if (xx2 < xx)
                                                                SwapI64(&xx, &xx2);
                                                        if (yy2 < yy)
                                                                SwapI64(&yy, &yy2);
                                                        tmpes = head.next;
                                                        while (tmpes != &head)
                                                        {
                                                                switch (tmpes->type)
                                                                {
                                                                        case SPEDT_SIMPLE_PT:
                                                                                if (xx <= *tmpes->x + tmpes->xx <= xx2 && yy <= *tmpes->y + tmpes->yy <= yy2)
                                                                                {
                                                                                        if (mode != SPED_UNSEL_RECTS)
                                                                                        {
                                                                                                tmpes->flags |= SPEDF_SEL;
                                                                                                tmpes->g->type |= SPF_SEL;
                                                                                        }
                                                                                        else
                                                                                        {
                                                                                                tmpes->flags &= ~SPEDF_SEL;
                                                                                                tmpes->g->type &= ~SPF_SEL;
                                                                                        }
                                                                                }
                                                                                break;
                                                                        case SPEDT_WIDTH_HEIGHT:
                                                                                if (xx <= *tmpes->x + *tmpes->w + tmpes->xx <= xx2 &&
                                                                                        yy <= *tmpes->y + *tmpes->h + tmpes->yy <= yy2)
                                                                                {
                                                                                        if (mode != SPED_UNSEL_RECTS)
                                                                                        {
                                                                                                tmpes->flags |= SPEDF_SEL;
                                                                                                tmpes->g->type |= SPF_SEL;
                                                                                        }
                                                                                        else
                                                                                        {
                                                                                                tmpes->flags &= ~SPEDF_SEL;
                                                                                                tmpes->g->type &= ~SPF_SEL;
                                                                                        }
                                                                                }
                                                                                break;
                                                                }
                                                                tmpes = tmpes->next;
                                                        }
                                                        case SPED_SEL_RECTS:
                                                        case SPED_UNSEL_RECTS:
                                                                break;
                                                        case SPED_SHIFT_RECTS:
                                                                do
                                                                {
                                                                        SpriteCtrlPtsDraw(dc, &head);
                                                                        message_code = MessageScan(&arg1, &arg2, 1 << MESSAGE_KEY_DOWN | 1 << MESSAGE_MS_L_DOWN);
                                                                        if (message_code == MESSAGE_KEY_DOWN)
                                                                                goto gs_key;
                                                                }
                                                                while (message_code != MESSAGE_MS_L_DOWN);

                                                                xx = arg1;
                                                                yy = arg2;
                                                                break;
                                                end:
                                        }
                                        switch (mode)
                                        {
                                                case SPED_SHIFT_PTS:
                                                case SPED_SHIFT_RECTS:
                                                        do
                                                        {
                                                                SpriteCtrlPtsDraw(dc, &head);
                                                                if (message_code = MessageScan(&arg1, &arg2, 1 << MESSAGE_MS_MOVE | 1 << MESSAGE_MS_L_UP))
                                                                {
                                                                        SpriteCtrlPtsMove(&head, arg1 - xx, arg2 - yy);
                                                                        xx = arg1;
                                                                        yy = arg2;
                                                                }
                                                        }
                                                        while (message_code != MESSAGE_MS_L_UP);

                                                        tmpes = head.next;
                                                        while (tmpes != &head)
                                                        {
                                                                tmpes->flags &= ~SPEDF_SEL;
                                                                tmpes->g->type &= ~SPF_SEL;
                                                                tmpes = tmpes->next;
                                                        }
                                                        break;
                                        }
                                        break;
                                case MESSAGE_KEY_DOWN:
gs_key:
                                        switch (arg1.u8[0])
                                        {
                                                case CH_SHIFT_ESC:
                                                        res = FALSE;
                                                case CH_ESC:
                                                        MessageGet(&arg1, &arg2, 1 << MESSAGE_KEY_UP);
                                                        goto gs_done;

                                                case 'p':
                                                case 'P':
                                                        mode &= ~1;
                                                        break;

                                                case 'r':
                                                case 'R':
                                                        mode |= 1;
                                                        break;
                                        }
                                        break;
                                case MESSAGE_MS_R_UP:
                                        goto gs_done;
                        }
                }
gs_done:
                QueueDel(&head, TRUE);
        }
        DCFill(dc);
        DCDel(dc);
        if (_cur_elem_num && res)
                *_cur_elem_num = cur_elem_num;

        return res;
}

I64 SpriteEd(CDoc *doc, CDocEntry *doc_ce, I64 x, I64 y, CSprite **_head, I64 *_cur_elem_num)
{
        CDocEntry       *doc_e2;
        CDocBin         *tmpb;
        Bool             unlock;
        I64                      i, r[16], message_code, arg1, arg2, xx, yy, old_de_flags;
        CSprite         *head2, *next, *last, *tmpg, *insert_pt;

        old_de_flags = doc_ce->de_flags;
        tmpb = doc_ce->bin_data;
        DocUnlock(doc);
        SpriteQueueSelAll(*_head, FALSE);
        do
        {
                if (winmgr.fps < 10)
                        doc_ce->de_flags |= DOCEF_DONT_DRAW;
                StrCopy(Fs->task_title, "Sprite Edit Menu");
                i=PopUpSpriteEd(_head, _cur_elem_num);
                SpriteEdUpdate(doc, doc_ce, *_head);
                if (0 <= i < SPED_EXIT)
                {
                        StrCopy(Fs->task_title, DefineSub(i, "ST_SPRITE_ED_MENU"));
                        switch (i)
                        {
                                case SPED_SEL_UNSEL_ALL:
                                        if (!SpriteQueueSelCount(*_head))
                                                SpriteQueueSelAll(*_head);
                                        else
                                                SpriteQueueSelAll(*_head, FALSE);
                                        break;

                                case SPED_SET_ORIGIN:
                                        SpriteQueueSelAll(*_head);
                                        doc_ce->de_flags = old_de_flags;
                                        MessageGet(&arg1, &arg2, 1 << MESSAGE_MS_L_UP);
                                        SpriteSetOrigin(*_head, x - arg1, y - arg2, 0);
                                        SpriteEdUpdate(doc, doc_ce, *_head);
                                        SpriteQueueSelAll(*_head, FALSE);
                                        break;

                                case SPED_SHIFT_SEL:
                                        if (!SpriteQueueSelCount(*_head))
                                                SpriteQueueSelAll(*_head);
                                        doc_ce->de_flags = old_de_flags;
                                        MessageGet(&arg1, &arg2, 1 << MESSAGE_MS_L_DOWN);
                                        xx = arg1;
                                        yy = arg2;
                                        do
                                        {
                                                message_code = MessageGet(&arg1, &arg2, 1 << MESSAGE_MS_L_UP + 1 << MESSAGE_MS_MOVE);
                                                SpriteSetOrigin(*_head, arg1 - xx, arg2 - yy, 0);
                                                xx = arg1;
                                                yy = arg2;
                                                SpriteEdUpdate(doc, doc_ce, *_head);
                                        }
                                        while (message_code != MESSAGE_MS_L_UP);

                                        if (!SpriteQueueSelCount(*_head, FALSE))
                                                SpriteQueueSelAll(*_head, FALSE);
                                        break;

                                case SPED_SEL:
                                case SPED_SEL_RECTS:
                                case SPED_UNSEL:
                                case SPED_UNSEL_RECTS:
                                case SPED_SHIFT_PTS:
                                case SPED_SHIFT_RECTS:
                                        RegOneTimePopUp(ARf_CSPRITE_PTS_RECTANGLES, 
                                                                "You can switch between points\n"
                                                                "and rectangles with '$GREEN$p$FG$' and '$GREEN$r$FG$'.\n"
                                                                "Press '$GREEN$r$FG$' after one rectangle\n"
                                                                "to OR another rectangle.\n");
                                        doc_ce->de_flags = old_de_flags;
                                        if (SpriteSelUnselShiftPts(tmpb->data, x, y, _cur_elem_num, i))
                                        {
                                                QueueDel(*_head);
                                                Free(*_head);
                                                *_head = Sprite2SpriteQueue(tmpb->data);
                                        }
                                        else
                                                SpriteEdUpdate(doc, doc_ce, *_head);
                                        break;

                                case SPED_TRANSFORM_SEL:
                                        if (!SpriteQueueSelCount(*_head))
                                                SpriteQueueSelAll(*_head);
                                        if (PopUpTransform(r))
                                        {
                                                SpriteTransformQueue(*_head, r);
                                                SpriteEdUpdate(doc, doc_ce, *_head);
                                        }
                                        if (!SpriteQueueSelCount(*_head, FALSE))
                                                SpriteQueueSelAll(*_head, FALSE);
                                        break;

                                case SPED_SHIFT_SUB_ORIGIN:
                                        doc_ce->de_flags = old_de_flags;
                                        insert_pt = SpriteSetSettings(, *_head, *_cur_elem_num);
                                        tmpg = CAlloc(SpriteElemQueuedBaseSize(SPT_SHIFT));
                                        tmpg->type = SPT_SHIFT;
                                        tmpg->p.x1 = 0;
                                        tmpg->p.y1 = 0;
                                        QueueInsert(tmpg, insert_pt->last);
                                        MessageGet(&arg1, &arg2, 1 << MESSAGE_MS_L_DOWN);
                                        xx = arg1;
                                        yy = arg2;
                                        do
                                        {
                                                message_code=MessageGet(&arg1, &arg2, 1 << MESSAGE_MS_L_UP + 1 << MESSAGE_MS_MOVE);
                                                tmpg->p.x1 = arg1 - xx;
                                                tmpg->p.y1 = arg2 - yy;
                                                SpriteEdUpdate(doc, doc_ce, *_head);
                                        }
                                        while (message_code != MESSAGE_MS_L_UP);

                                        *_cur_elem_num += 1;
                                        break;

                                case SPED_INS_CLIP:
                                        RegOneTimePopUp(ARf_CSPRITE_INS_CLIP, 
                                                                "You will probably want to shift around\n"
                                                                "the location of element groups.  Use\n"
                                                                "'Insert shift sub-origin' after picking the\n"
                                                                "element to insert before.  Or,\n"
                                                                "use 'shift points'.\n");
                                        insert_pt = SpriteSetSettings(, *_head, *_cur_elem_num);
                                        unlock = DocLock(sys_clip_doc);
                                        doc_e2 = sys_clip_doc->head.next;
                                        while (doc_e2 != sys_clip_doc)
                                        {
                                                if (doc_e2->type_u8 == DOCT_SPRITE)
                                                {
                                                        head2 = Sprite2SpriteQueue(doc_e2->bin_data->data);
                                                        if (head2->next != head2)
                                                        {
                                                                tmpg = head2->next;
                                                                while (tmpg != head2)
                                                                {
                                                                        *_cur_elem_num += 1;
                                                                        tmpg = tmpg->next;
                                                                }
                                                                next = head2->next;
                                                                last = head2->last;
                                                                insert_pt->last->next = next;
                                                                next->last = insert_pt->last;
                                                                insert_pt->last = last;
                                                                last->next = insert_pt;
                                                        }
                                                        Free(head2);
                                                }
                                                doc_e2 = doc_e2->next;
                                        }
                                        if (unlock)
                                                DocUnlock(sys_clip_doc);
                                        SpriteEdUpdate(doc, doc_ce, *_head);
                                        break;

                                case SPED_TEXT_ED:
                                        if (SpriteEdText(_head, _cur_elem_num))
                                                SpriteEdUpdate(doc, doc_ce, *_head);
                                        break;
                        }
                }
        }
        while (i != DOCM_CANCEL && i != SPED_EXIT && i != SPED_MAIN_MENU);

        doc_ce->de_flags = old_de_flags;

        switch (i)
        {
                case DOCM_CANCEL:
                        return SPE_ABORT;
                case SPED_EXIT:
                        return SPE_EXIT;
                case SPED_MAIN_MENU:
                        return SPE_CONT;
        }
}

#help_index "Graphics/Sprite;Sprites;Graphics/Math/3D Transformation"
public U8 *SpriteTransform(U8 *elems, I64 *r)
{//Rotate Sprite using 4x4 matrix. Uses fixed-point.
        U8              *res;
        CSprite *head = Sprite2SpriteQueue(elems);

        SpriteQueueSelAll(head);
        SpriteTransformQueue(head, r);
        res = SpriteQueue2Sprite(head);
        QueueDel(head);
        Free(head);

        return res;
}