#help_index "Graphics/Sprite;Sprites" /* CSprites are stored as a sequence of variable length operations with a 1-byte type leading each operation. They are stored, one after another, in a chunk of memory terminated by a zero. Sprite3() shows how the CSprite unions are used. SpriteElemSize() will return the size of a single element, while SpriteSize() will return the size of an entire list. Look at sprite_elem_base_sizes. See ::/Apps/GrModels for an example of making CSprite by hand. It uses SPT_MESH, one of the most complicated. */ public U0 Sprite3(CDC *dc=gr.dc, I64 x, I64 y, I64 z, U8 *elems, Bool just_one_elem=FALSE) {//Plot a sprite into a CDC. CSprite *tmpg = elems - offset(CSprite.start); I64 i, j, k, x1, y1, z1, x2, y2, *old_r, *r2, old_flags = dc->flags, old_pen_width = dc->thick; I32 *ptr; CColorROPU32 old_color = dc->color; CDC *img; CD3I32 *p, *p2; CGrSym old_sym; MemCopy(&old_sym, &dc->sym, sizeof(CGrSym)); if (dc->flags & DCF_LOCATE_NEAREST) dc->nearest_dist = I64_MAX; while (tmpg->type & SPG_TYPE_MASK) { switch (tmpg->type & SPG_TYPE_MASK) { case SPT_COLOR: dc->color = dc->color & ~(COLORROP_COLORS_MASK | ROPF_DITHER) | tmpg->c.color; break; case SPT_DITHER_COLOR: dc->color = dc->color & ~COLORROP_COLORS_MASK | tmpg->d.dither_color.u8[0] | tmpg->d.dither_color.u8[1] << COLORROP_BITS | ROPF_DITHER; break; case SPT_THICK: dc->thick = tmpg->t.thick; DCThickScale(dc); break; case SPT_TRANSFORM_ON: if (!(dc->flags & DCF_TRANSFORMATION)) { x -= dc->x; y -= dc->y; z -= dc->z; } dc->flags |= DCF_TRANSFORMATION; break; case SPT_TRANSFORM_OFF: if (dc->flags & DCF_TRANSFORMATION) { x += dc->x; y += dc->y; z += dc->z; } dc->flags &= ~DCF_TRANSFORMATION; break; case SPT_PT: GrPlot3(dc, tmpg->p.x1 + x, tmpg->p.y1 + y, z); break; case SPT_TEXT: GrPrint3(dc, tmpg->ps.x1 + x, tmpg->ps.y1 + y, z, "%s", tmpg->ps.st); break; case SPT_TEXT_BOX: GrTextBox3(dc, tmpg->ps.x1 + x, tmpg->ps.y1 + y, z, tmpg->ps.st); break; case SPT_TEXT_DIAMOND: GrTextDiamond3(dc, tmpg->ps.x1 + x, tmpg->ps.y1 + y, z, tmpg->ps.st); break; case SPT_FLOOD_FILL: GrFloodFill3(dc, tmpg->p.x1 + x, tmpg->p.y1 + y, z, FALSE); break; case SPT_FLOOD_FILL_NOT: i = dc->color; dc->color = dc->color.c0; GrFloodFill3(dc, tmpg->p.x1 + x, tmpg->p.y1 + y, z, TRUE); dc->color = i; break; case SPT_SHIFT: x += tmpg->p.x1; y += tmpg->p.y1; break; case SPT_LINE: GrLine3(dc, tmpg->pp.x1 + x, tmpg->pp.y1 + y, z, tmpg->pp.x2 + x, tmpg->pp.y2 + y, z); break; case SPT_ARROW: GrArrow3(dc, tmpg->pp.x1 + x, tmpg->pp.y1 + y, z, tmpg->pp.x2 + x, tmpg->pp.y2 + y, z); break; case SPT_PLANAR_SYMMETRY: if (DCSymmetry3Set(dc, tmpg->pp.x1 + x, tmpg->pp.y1 + y, z, tmpg->pp.x2 + x, tmpg->pp.y2 + y, z, tmpg->pp.x2 + x, tmpg->pp.y2 + y, z + 1)) dc->flags |= DCF_SYMMETRY; else dc->flags &= ~DCF_SYMMETRY; break; case SPT_BITMAP: 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; GrBlot3(dc, tmpg->pwhu.x1 + x, tmpg->pwhu.y1 + y, z, img); Free(img); break; case SPT_RECT: GrRect3(dc, tmpg->pp.x1 + x, tmpg->pp.y1 + y, z, tmpg->pp.x2 - tmpg->pp.x1, tmpg->pp.y2 - tmpg->pp.y1); break; case SPT_ROTATED_RECT: x1 = tmpg->ppa.x1 + x; y1 = tmpg->ppa.y1 + y; z1 = z; Mat4x4MulXYZ(dc->r, &x1, &y1, &z1); old_r = dc->r; dc->flags |= DCF_TRANSFORMATION; r2 = Mat4x4IdentNew; Mat4x4RotZ(r2, -tmpg->ppa.angle); Mat4x4TranslationEqu(r2, x1, y1, z1); DCMat4x4Set(dc, Mat4x4MulMat4x4New(old_r, r2)); GrRect3(dc, 0, 0, 0, tmpg->ppa.x2 - tmpg->ppa.x1, tmpg->ppa.y2 - tmpg->ppa.y1); Free(dc->r); Free(r2); DCMat4x4Set(dc, old_r); dc->flags = dc->flags & ~DCF_TRANSFORMATION | old_flags; break; case SPT_CIRCLE: GrCircle3(dc, tmpg->pr.x1 + x, tmpg->pr.y1 + y, z, tmpg->pr.radius); break; case SPT_ELLIPSE: GrEllipse3(dc, tmpg->pwha.x1 + x, tmpg->pwha.y1 + y, z, tmpg->pwha.width, tmpg->pwha.height, tmpg->pwha.angle); break; case SPT_POLYGON: GrRegPoly3(dc, tmpg->pwhas.x1 + x, tmpg->pwhas.y1 + y, z, tmpg->pwhas.width, tmpg->pwhas.height, tmpg->pwhas.sides, tmpg->pwhas.angle); break; case SPT_POLYLINE: ptr = &tmpg->nu.u; x1 = ptr[0]; y1 = ptr[1]; for (i = 1; i < tmpg->nu.num; i++) { x2 = ptr[i << 1]; y2 = ptr[i << 1 + 1]; GrLine3(dc, x1 + x, y1 + y, z, x2 + x, y2 + y, z); x1 = x2; y1 = y2; } break; case SPT_POLYPT: x1 = tmpg->npu.x; y1 = tmpg->npu.y; ptr = &tmpg->npu.u; k = tmpg->npu.num * 3; GrPlot3(dc, x1 + x, y1 + y, z); for (i = 0; i < k; i += 3) { j = BFieldExtU32(ptr, i, 3); x1 += gr_x_offsets[j]; y1 += gr_y_offsets[j]; GrPlot3(dc, x1 + x, y1 + y, z); } break; start: p2 = p = MAlloc(tmpg->nu.num * sizeof(CD3I32)); MemCopy(p, &tmpg->nu.u, tmpg->nu.num * sizeof(CD3I32)); for (i = 0; i < tmpg->nu.num; i++, p2++) { p2->x += x; p2->y += y; p2->z += z; } case SPT_BSPLINE2: Gr2BSpline3(dc, p, tmpg->nu.num, FALSE); break; case SPT_BSPLINE3: Gr3BSpline3(dc, p, tmpg->nu.num, FALSE); break; case SPT_BSPLINE2_CLOSED: Gr2BSpline3(dc, p, tmpg->nu.num, TRUE); break; case SPT_BSPLINE3_CLOSED: Gr3BSpline3(dc, p, tmpg->nu.num, TRUE); break; end: Free(p); break; case SPT_MESH: p2 = p = MAlloc(tmpg->mu.vertex_count * sizeof(CD3I32)); MemCopy(p, &tmpg->mu.u, tmpg->mu.vertex_count * sizeof(CD3I32)); for (i = 0; i < tmpg->mu.vertex_count; i++, p2++) { p2->x += x; p2->y += y; p2->z += z; } Gr3Mesh(dc, tmpg->mu.vertex_count, p, tmpg->mu.tri_count, (&tmpg->mu.u)(U8 *) + sizeof(CD3I32) * tmpg->mu.vertex_count); Free(p); break; case SPT_SHIFTABLE_MESH: if (dc->flags & DCF_TRANSFORMATION) { dc->x += tmpg->pmu.x; dc->y += tmpg->pmu.y; dc->z += tmpg->pmu.z; x1 = x; y1 = y; z1 = z; } else { x1 = tmpg->pmu.x + x; y1 = tmpg->pmu.y + y; z1 = tmpg->pmu.z + z; } p2 = p = MAlloc(tmpg->pmu.vertex_count * sizeof(CD3I32)); MemCopy(p, &tmpg->pmu.u, tmpg->pmu.vertex_count * sizeof(CD3I32)); for (i = 0; i < tmpg->pmu.vertex_count; i++, p2++) { p2->x += x1; p2->y += y1; p2->z += z1; } Gr3Mesh(dc, tmpg->pmu.vertex_count, p, tmpg->pmu.tri_count, (&tmpg->pmu.u)(U8 *) + sizeof(CD3I32) * tmpg->pmu.vertex_count); Free(p); if (dc->flags & DCF_TRANSFORMATION) { dc->x -= tmpg->pmu.x; dc->y -= tmpg->pmu.y; dc->z -= tmpg->pmu.z; } break; } if (just_one_elem) break; tmpg(U8 *) += SpriteElemSize(tmpg); } MemCopy(&dc->sym, &old_sym, sizeof(CGrSym)); dc->color = old_color; dc->thick = old_pen_width; dc->flags = dc->flags & ~(DCF_SYMMETRY | DCF_TRANSFORMATION) | old_flags & (DCF_SYMMETRY | DCF_TRANSFORMATION); } public U0 Sprite3B(CDC *dc=gr.dc, I64 x, I64 y, I64 z, U8 *elems) {//Plot a sprite into a CDC, post transform xyz translation. I64 old_x = dc->x, old_y = dc->y, old_z = dc->z, old_flags = dc->flags & DCF_TRANSFORMATION; dc->x = x; dc->y = y; dc->z = z; dc->flags |= DCF_TRANSFORMATION; Sprite3(dc, 0, 0, 0, elems); dc->x = old_x; dc->y = old_y; dc->z = old_z; dc->flags = dc->flags & ~DCF_TRANSFORMATION | old_flags; } public U0 Sprite3Mat4x4B(CDC *dc=gr.dc, I64 x, I64 y, I64 z, U8 *elems, I64 *m) {//Plot rotated by matrix. I64 r[16], *old_r = dc->r, new_m[16], old_flags = dc->flags & DCF_TRANSFORMATION; MemCopy(new_m, m, 16 * sizeof(I64)); dc->flags |= DCF_TRANSFORMATION; Mat4x4TranslationAdd(new_m, x, y, z); dc->r=Mat4x4MulMat4x4Equ(r, old_r, new_m); Sprite3(dc, 0, 0, 0, elems); dc->r = old_r; dc->flags = dc->flags & ~DCF_TRANSFORMATION | old_flags; } public U0 Sprite3XB(CDC *dc=gr.dc, I64 x, I64 y, I64 z, U8 *elems, F64 phi=0) {//Plot rotated around X axis. I64 r[16]; Mat4x4IdentEqu(r); Mat4x4RotX(r, phi); Sprite3Mat4x4B(dc, x, y, z, elems, r); } public U0 Sprite3YB(CDC *dc=gr.dc, I64 x, I64 y, I64 z, U8 *elems, F64 omega=0) {//Plot rotated around Y axis. I64 r[16]; Mat4x4IdentEqu(r); Mat4x4RotY(r, omega); Sprite3Mat4x4B(dc, x, y, z, elems, r); } public U0 Sprite3ZB(CDC *dc=gr.dc, I64 x, I64 y, I64 z, U8 *elems, F64 theta=0) {//Plot rotated around Z axis. I64 r[16]; Mat4x4IdentEqu(r); Mat4x4RotZ(r, theta); Sprite3Mat4x4B(dc, x, y, z, elems, r); } public U0 SpriteExtents(U8 *elems, I64 *min_x=NULL, I64 *max_x=NULL, I64 *min_y=NULL, I64 *max_y=NULL) {//Ignores flood fills. CDC *dc = DCNew(I32_MAX, I32_MAX, Fs, TRUE); DCExtentsInit(dc); Sprite3(dc, I32_MAX / 2, I32_MAX / 2, I32_MAX / 2, elems); if (dc->min_x <= dc->max_x) { dc->min_x -= I32_MAX / 2; dc->max_x -= I32_MAX / 2; } if (dc->min_y <= dc->max_y) { dc->min_y -= I32_MAX / 2; dc->max_y -= I32_MAX / 2; } if (min_x) *min_x = dc->min_x; if (max_x) *max_x = dc->max_x; if (min_y) *min_y = dc->min_y; if (max_y) *max_y = dc->max_y; DCDel(dc); } public CDC *Sprite2DC(U8 *elems) {//Convert sprite to device context. CDC *res; I64 min_x, max_x, min_y, max_y; SpriteExtents(elems, &min_x, &max_x, &min_y, &max_y); res = DCNew(max_x - min_x + 1, max_y - min_y + 1); Sprite3(res, -min_x, -min_y, 0, elems); return res; } public U8 *SpriteInterpolate(F64 t, U8 *elems0, U8 *elems1) {//The two CSprite should be ident except for points shifted around. //t ranges from 0.0 to 1.0. I64 i, t1 = GR_SCALE * t, t0 = GR_SCALE - t1; I32 *ptr0, *ptr1, *ptrr; CD3I32 *p0, *p1, *pr; U8 *res; CSprite *tmpg0 = elems0 - offset(CSprite.start), *tmpg1 = elems1 - offset(CSprite.start), *tmpgr; if (t < 0.5) { i = SpriteSize(elems0), res = MAlloc(i); MemCopy(res, elems0, i); } else { i = SpriteSize(elems1), res = MAlloc(i); MemCopy(res, elems1, i); } tmpgr = res - offset(CSprite.start); while (tmpg0->type & SPG_TYPE_MASK) { if (tmpg0->type & SPG_TYPE_MASK != tmpg1->type & SPG_TYPE_MASK) throw('Graphics'); switch (tmpg0->type & SPG_TYPE_MASK) { case SPT_ROTATED_RECT: tmpgr->ppa.angle = (tmpg0->ppa.angle * t0 + tmpg1->ppa.angle * t1) / GR_SCALE; case SPT_RECT: case SPT_LINE: case SPT_ARROW: case SPT_PLANAR_SYMMETRY: tmpgr->pp.x2 = (tmpg0->pp.x2 * t0 + tmpg1->pp.x2 * t1) >> 32; tmpgr->pp.y2 = (tmpg0->pp.y2 * t0 + tmpg1->pp.y2 * t1) >> 32; case SPT_TEXT: case SPT_TEXT_BOX: case SPT_TEXT_DIAMOND: case SPT_PT: case SPT_FLOOD_FILL: case SPT_FLOOD_FILL_NOT: case SPT_SHIFT: tmpgr->p.x1 = (tmpg0->p.x1 * t0 + tmpg1->p.x1 * t1) >> 32; tmpgr->p.y1 = (tmpg0->p.y1 * t0 + tmpg1->p.y1 * t1) >> 32; break; case SPT_CIRCLE: tmpgr->pr.radius = (tmpg0->pr.radius * t0 + tmpg1->pr.radius * t1) >> 32; tmpgr->pr.x1 = (tmpg0->pr.x1 * t0 + tmpg1->pr.x1 * t1) >> 32; tmpgr->pr.y1 = (tmpg0->pr.y1 * t0 + tmpg1->pr.y1 * t1) >> 32; break; case SPT_ELLIPSE: case SPT_POLYGON: tmpgr->pwha.x1 = (tmpg0->pwha.x1 * t0 + tmpg1->pwha.x1 * t1) >> 32; tmpgr->pwha.y1 = (tmpg0->pwha.y1 * t0 + tmpg1->pwha.y1 * t1) >> 32; tmpgr->pwha.width = (tmpg0->pwha.width * t0 + tmpg1->pwha.width * t1) >> 32; tmpgr->pwha.height = (tmpg0->pwha.height * t0 + tmpg1->pwha.height * t1) >> 32; break; case SPT_BITMAP: tmpgr->pwhu.x1 = (tmpg0->pwhu.x1 * t0 + tmpg1->pwhu.x1 * t1) >> 32; tmpgr->pwhu.y1 = (tmpg0->pwhu.y1 * t0 + tmpg1->pwhu.y1 * t1) >> 32; break; case SPT_POLYLINE: ptr0 = &tmpg0->nu.u; ptr1 = &tmpg1->nu.u; ptrr = &tmpgr->nu.u; for (i = 0; i < tmpg0->nu.num; i++) { ptrr[i << 1] = (ptr0[i << 1] * t0 + ptr1[i << 1] * t1) >> 32; ptrr[i << 1 + 1] = (ptr0[i << 1 + 1] * t0 + ptr1[i << 1 + 1] * t1) >> 32; } break; case SPT_POLYPT: tmpgr->npu.x = (tmpg0->npu.x * t0 + tmpg1->npu.x * t1) >> 32; tmpgr->npu.y = (tmpg0->npu.y * t0 + tmpg1->npu.y * t1) >> 32; break; case SPT_BSPLINE2: case SPT_BSPLINE3: case SPT_BSPLINE2_CLOSED: case SPT_BSPLINE3_CLOSED: p0 = &tmpg0->nu.u; p1 = &tmpg1->nu.u; pr = &tmpgr->nu.u; for (i = 0; i < tmpg0->nu.num; i++) { pr[i].x = (p0[i].x * t0 + p1[i].x * t1) >> 32; pr[i].y = (p0[i].y * t0 + p1[i].y * t1) >> 32; pr[i].z = (p0[i].z * t0 + p1[i].z * t1) >> 32; } break; case SPT_MESH: p0 = &tmpg0->mu.u; p1 = &tmpg1->mu.u; pr = &tmpgr->mu.u; for (i = 0; i < tmpg0->mu.vertex_count; i++) { pr[i].x = (p0[i].x * t0 + p1[i].x * t1) >> 32; pr[i].y = (p0[i].y * t0 + p1[i].y * t1) >> 32; pr[i].z = (p0[i].z * t0 + p1[i].z * t1) >> 32; } break; case SPT_SHIFTABLE_MESH: p0 = &tmpg0->pmu.u; p1 = &tmpg1->pmu.u; pr = &tmpgr->pmu.u; for (i = 0; i < tmpg0->pmu.vertex_count; i++) { pr[i].x = (p0[i].x * t0 + p1[i].x * t1) >> 32; pr[i].y = (p0[i].y * t0 + p1[i].y * t1) >> 32; pr[i].z = (p0[i].z * t0 + p1[i].z * t1) >> 32; } break; } tmpg0(U8 *) += SpriteElemSize(tmpg0); tmpg1(U8 *) += SpriteElemSize(tmpg1); tmpgr(U8 *) += SpriteElemSize(tmpgr); } return res; } #help_index "Graphics/Sprite;DolDoc/Output;StdOut/DolDoc" public CDocEntry *DocSprite(CDoc *doc=NULL, U8 *elems, U8 *format=NULL) {//Put a sprite into a document. You can, optionally, supply a format string //for DolDoc cmd with a %d for the bin_num. I64 size; U8 *st; Bool unlock; CDocEntry *doc_e; CDocBin *tmpb; if (!doc && !(doc = DocPut)) return NULL; unlock = DocLock(doc); size = SpriteSize(elems); tmpb = CAlloc(sizeof(CDocBin), doc->mem_task); tmpb->size = size; tmpb->data = MAlloc(size, doc->mem_task); MemCopy(tmpb->data, elems, size); tmpb->num = doc->cur_bin_num; tmpb->use_count = 1; QueueInsert(tmpb, doc->bin_head.last); if (format) st = MStrPrint(format, doc->cur_bin_num++); else st = MStrPrint("$SP,\"\",BI=%d$", doc->cur_bin_num++); doc_e = DocPrint(doc, "%s", st); Free(st); doc_e->bin_data = tmpb; if (doc_e && doc_e->de_flags & DOCEF_TAG && doc_e->tag && *doc_e->tag) tmpb->tag = StrNew(doc_e->tag, doc->mem_task); if (unlock) DocUnlock(doc); return doc_e; } public CDocEntry *Sprite(U8 *elems, U8 *format=NULL) {//Put sprite to the command-line, DocPut. //If you set format, then include dollars ("$SP ...$") and leave %d for num. CDoc *doc; if (doc = DocPut) return DocSprite(doc, elems, format); return NULL; }