// Executes a linear mouse movement animation. It can be a simple cursor // move or a drag depending on what is passed to `type`. static void mouse_animate( CGEventType type, CGMouseButton button, CGPoint start_point, CGPoint end_point, double duration ) { CFDateRef current_time = NULL; CGPoint current_point = start_point; double xstep = (end_point.x - start_point.x) / (duration * FPS); double ystep = (end_point.y - start_point.y) / (duration * FPS); CFDateRef start = NOW; double remaining = 0.0; while (!CLOSE_ENOUGH(current_point, end_point)) { remaining = end_point.x - current_point.x; current_point.x += abs(xstep) > abs(remaining) ? remaining : xstep; remaining = end_point.y - current_point.y; current_point.y += abs(ystep) > abs(remaining) ? remaining : ystep; POSTRELEASE(NEW_EVENT(type, current_point, button)); mouse_sleep(1); current_time = NOW; // this is a safety if (CFDateGetTimeIntervalSinceDate(NOW, start) > (duration + 1)) break; RELEASE(current_time); current_time = NULL; current_point = mouse_current_position(); } RELEASE(start); if (current_time) RELEASE(current_time); }
void stepEdges(ActiveEdgeList *ael, const rb_red_blk_tree* activePrims){ static int scanLine; const static Comparator leftToRight = {(CompareF)(&leftToRightF), &scanLine}; LinkN **aelHead = &(ael->activeEdges); scanLine = ++(ael->scanLine); { LinkN *i, *p, *nextP; for(p = NULL, i = ael->activeEdges; i; (p = i),(i = nextP)){ const EdgeListEntry *const entry = i->data; Point **const edge = entry->edge; const float ys = edge[START]->y, ye = edge[END]->y, edgeEnd = max(ys, ye); nextP = i->tail; if(edgeEnd < scanLine){ #ifndef NDEBUG { const char* msg = "Deactivating %s with y-span: %f -> %f with x-span: %f -> %f(true: %f -> %f)\n", *color = fmtColor(entry->owner->color); const float lowEnd = min(ys, ye), mnX = getMinXForLine(entry, scanLine), mxX = getMaxXForLine(entry, scanLine), sX = edge[START]->x, eX = edge[END]->x; dPrintf((msg, color, lowEnd, edgeEnd, mnX, mxX, sX, eX)); } #endif /* Entries don't own the primitives they point to, * so we can get away with a simple free */ freeLink(removeLink(aelHead, i, p), &free); i = p; /* We don't want to advance p into garbage data */ } } } { const rb_red_blk_node *i; for(i = activePrims->first; i != activePrims->sentinel; i = TreeSuccessor(activePrims, i)){ Primitive *const prim = i->key; const size_t jMax = prim->arity; size_t j; for(j = 0; j < jMax; ++j){ Point **const e = prim->boundary + j; const float sy = e[START]->y, ey = e[END]->y, mnY = min(sy, ey), mxY = max(sy, ey); const bool singleton = prim->arity == 1; if((roundOwn(mnY) == scanLine && (!CLOSE_ENOUGH(sy,ey) || (singleton /* newEdge.isSingleton() */))) || (scanLine == 0 && mnY < 0 && mxY > 0)){ LinkN* newEdge = makeLinkEZ(e, prim); #ifndef NDEBUG { const char* msg = "Activating %s with y-span: %f -> %f with x-span: %f -> %f(true: %f -> %f)\n", *color = fmtColor(prim->color); const float mnX = getMinXForLine(newEdge->data, scanLine), mxX = getMaxXForLine(newEdge->data, scanLine), sX = e[START]->x, eX = e[END]->x; dPrintf((msg, color, mnY, mxY, mnX, mxX, sX, eX)); } #endif linkFront(aelHead, newEdge); if (singleton) { dPrintf(("\t->Activating dummy end\n")); linkFront(aelHead, makeLink(e, prim, true)); } } } } } mergeSort(&(ael->activeEdges), &leftToRight); }
void render(PaletteRef *raster, int lineWidth, int numLines, const rb_red_blk_tree *scanLinePrimBuckets){ static OntoProj screenPlaneData = {offsetof(Point, z), 0}; static const Transformation screenPlane = {(TransformationF)(&snapOntoProj), &screenPlaneData}; { int line; rb_red_blk_tree activePrimSet; ActiveEdgeList ael = freshAEL(); rb_red_blk_map_tree inFlags; rb_red_blk_tree deFlags; /* This ensures that both trees are initialized and in a cleared state */ RBTreeMapInit(&inFlags, pointerDiffF, NULL, &RBMapNodeAlloc, NULL); RBTreeInit(&deFlags, pointerDiffF, NULL, &RBNodeAlloc); RBTreeInit(&activePrimSet, pointerDiffF, NULL, &RBNodeAlloc); dPrintf(("Scanning line: 0\n")); for(line = 0; line < numLines; (++line), (raster += lineWidth)) { rb_red_blk_node *primIt, *p = NULL, *nextP; dPrintf(("\tUpdating activePrimSet\n")); for (primIt = activePrimSet.first; primIt != activePrimSet.sentinel; (p = primIt), (primIt = nextP)) { const Primitive* prim = primIt->key; const int top = roundOwn(topMostPoint(prim)); nextP = TreeSuccessor(&activePrimSet, primIt); if(top < line){ #ifndef NDEBUG { const int bottom = roundOwn(bottomMostPoint(prim)); dPrintf(("\t\t%d -> %d ( %s ) is not valid here: %d\n",top,bottom,fmtColor(prim->color), line)); } #endif RBDelete(&activePrimSet, primIt); primIt = p; /* We don't want to advance p into garbage data */ } } { const rb_red_blk_tree *bucket = scanLinePrimBuckets + line; const rb_red_blk_node *node; for(node = bucket->first; node != bucket->sentinel; node = TreeSuccessor(bucket, node)) { Primitive * prim = node->key; #ifndef NDEBUG { const int top = roundOwn(topMostPoint(prim)), bottom = roundOwn(bottomMostPoint(prim)); dPrintf(("\t\t%d -> %d ( %s ) is added here: %d\n",top,bottom,fmtColor(prim->color), line)); } #endif RBTreeInsert(&activePrimSet, prim); } } stepEdges(&ael, &activePrimSet); { int curPixel = 0; const Primitive *curDraw = NULL; EdgeListEntry *nextEdge; LinkN* i = ael.activeEdges; if(i){ nextEdge = i->data; while(nextEdge && curPixel < lineWidth){ EdgeListEntry *const startEdge = nextEdge; Primitive *const startOwner = startEdge->owner; int startX = roundOwn(getSmartXForLine(startEdge, line)), nextX; rb_red_blk_map_node *inFlag = (rb_red_blk_map_node *)RBExactQuery((rb_red_blk_tree*)(&inFlags), startOwner); if(inFlag){ static Point localPoints[6]; /* We don't recurse, so this is fine */ static Edge flatHere = {localPoints, localPoints + 1}, flatIn = {localPoints + 2, localPoints + 3}, vert = {localPoints + 4, localPoints + 5}; const EdgeListEntry *const edgeInEntry = inFlag->info; Point **const edgeHere = startEdge->edge, **edgeIn = edgeInEntry->edge; const Point *const s = edgeHere[START], *const e = edgeHere[END]; Point here; bool sV, eV, v; float dotH, dotIn; transformEdge(&screenPlane, edgeHere, flatHere); transformEdge(&screenPlane, edgeIn, flatIn); INIT_POINT(here, startX, line, 0); sV = contains(edgeIn, s); eV = contains(edgeIn, e); v = (sV || eV) && contains(flatIn, &here) && contains(flatHere, &here) && (startOwner->arity != 1); vert[START] = &here; INIT_POINT(*(vert[END]), startX, line+1, 0); dotH = v ? dotEdge(vert, flatHere) : 0; dotIn = v ? dotEdge(vert, flatIn) : 0; if(!v || dotH * dotIn > 0){ dPrintf(("\tNot *in* old %s at %f\n", fmtColor(startEdge->owner->color), getSmartXForLine(startEdge, line))); RBSetAdd(&deFlags, startOwner); } else { dPrintf(("\tFound horizontal vertex %s at %f. Don't delete it yet\n",fmtColor(startEdge->owner->color), getSmartXForLine(startEdge, line))); } } else { dPrintf(("\tNow *in* new %s at %f\n",fmtColor(startEdge->owner->color), getSmartXForLine(startEdge, line))); /* This might happen if a polygon is parallel to the x-axis */ RBMapPut(&inFlags, startOwner, startEdge); } if(curPixel < startX){ dPrintf(("\tcurPixel has fallen behind, dragging from %d to %d\n",curPixel, startX)); curPixel = startX; } i = i->tail; if(i){ nextEdge = i->data; nextX = roundOwn(getSmartXForLine(nextEdge, line)); dPrintf(("\tNext edges @ x = %d from %s\n",nextX, fmtColor(nextEdge->owner->color))); } else { dPrintf(("\tNo more edges\n")); nextEdge = NULL; nextX = 0; } nextX = min(nextX, lineWidth); while ((!nextEdge && curPixel < lineWidth) || (curPixel < nextX)) { bool zFight = false, solitary = false; float bestZ = HUGE_VAL; const rb_red_blk_node *node; curDraw = NULL; dPrintf(("\tTesting depth:\n")); for(node = inFlags.tree.first; node != inFlags.tree.sentinel; node = TreeSuccessor((rb_red_blk_tree*)(&inFlags), node)) { const Primitive *prim = node->key; /* We need sub-pixel accuracy */ const float testZ = getZForXY(prim, curPixel, line); if(testZ <= bestZ + PT_EPS){ dPrintf(("\t\tHit: %f <= %f for %s\n",testZ, bestZ, fmtColor(prim->color))); if (CLOSE_ENOUGH(testZ, bestZ)) { if (prim->arity == 1) { zFight = curDraw && curDraw->arity == 1; curDraw = prim; solitary = RBSetContains(&deFlags, prim); } else { zFight = curDraw && curDraw->arity != 1; } } else { zFight = false; bestZ = testZ; curDraw = prim; solitary = RBSetContains(&deFlags, prim); } } else { dPrintf(("\t\tMiss: %f > %f for %s\n",testZ, bestZ, fmtColor(prim->color))); } } if(curDraw){ #ifndef NDEBUG if(nextEdge || solitary){ #endif const int drawWidth = (zFight || solitary) ? 1 : ((nextEdge ? nextX : lineWidth) - curPixel), stopPixel = curPixel + min(lineWidth - curPixel, max(0, drawWidth)); const PaletteRef drawColor = /*(uint16_t)roundOwn(63 * bestZ / 100) << 5;*/decodeColor(curDraw->color); dPrintf(("Drawing %d @ (%d, %d)\n",drawWidth,curPixel,line)); dPrintf(("Drawing %d @ (%d, %d)\n",stopPixel - curPixel,curPixel,line)); while(curPixel < stopPixel){ raster[curPixel++] = drawColor; } #ifndef NDEBUG } else { dPrintf(("Warning: we probably shouldn't have to draw if there are no more edges to turn us off. Look for parity errors\n"); RBTreeClear((rb_red_blk_tree*)&inFlags)); } #endif } else if(!inFlags.tree.size && nextEdge){ /* fast forward, we aren't in any polys */ dPrintf(("Not in any polys at the moment, fast-forwarding(1) to %d\n", nextX)); curPixel = nextX; } else { /* Nothing left */ dPrintf(("Nothing to draw at end of line\n")); curPixel = lineWidth; } for(node = deFlags.first; node != deFlags.sentinel; node = TreeSuccessor(&deFlags, node)){ RBMapRemove(&inFlags, node->key); } RBTreeClear(&deFlags); } if (!inFlags.tree.size && nextEdge) { dPrintf(("Not in any polys at the moment, fast-forwarding(2) to %d\n", nextX)); curPixel = nextX; } } } } #ifndef NDEBUG { dPrintf(("Scanning line: %d\n", line+1)); if(inFlags.tree.size){ rb_red_blk_node *node; dPrintf(("\tGarbage left in inFlags:\n")); for (node = inFlags.tree.first; node != inFlags.tree.sentinel; node = TreeSuccessor((rb_red_blk_tree*)&inFlags, node)) { dPrintf(("\t\t%s\n",fmtColor(((const Primitive*)node->key)->color))); } } } #endif RBTreeClear(&deFlags); RBTreeClear((rb_red_blk_tree*)(&inFlags)); } RBTreeDestroy(&activePrimSet, false); RBTreeDestroy(&deFlags, false); RBTreeDestroy((rb_red_blk_tree*)(&inFlags), false); }