コード例 #1
0
ファイル: cube.c プロジェクト: JetChen07/sgtpuzzles
static game_state *execute_move(const game_state *from, const char *move)
{
    game_state *ret;
    float angle;
    struct solid *poly;
    int pkey[2];
    int skey[2], dkey[2];
    int i, j, dest;
    int direction;

    switch (*move) {
      case 'L': direction = LEFT; break;
      case 'R': direction = RIGHT; break;
      case 'U': direction = UP; break;
      case 'D': direction = DOWN; break;
      default: return NULL;
    }

    dest = find_move_dest(from, direction, skey, dkey);
    if (dest < 0)
        return NULL;

    ret = dup_game(from);
    ret->current = dest;

    /*
     * So we know what grid square we're aiming for, and we also
     * know the two key points (as indices in both the source and
     * destination grid squares) which are invariant between source
     * and destination.
     * 
     * Next we must roll the polyhedron on to that square. So we
     * find the indices of the key points within the polyhedron's
     * vertex array, then use those in a call to transform_poly,
     * and align the result on the new grid square.
     */
    {
        int all_pkey[4];
        align_poly(from->solid, &from->grid->squares[from->current], all_pkey);
        pkey[0] = all_pkey[skey[0]];
        pkey[1] = all_pkey[skey[1]];
        /*
         * Now pkey[0] corresponds to skey[0] and dkey[0], and
         * likewise [1].
         */
    }

    /*
     * Now find the angle through which to rotate the polyhedron.
     * Do this by finding the two faces that share the two vertices
     * we've found, and taking the dot product of their normals.
     */
    {
        int f[2], nf = 0;
        float dp;

        for (i = 0; i < from->solid->nfaces; i++) {
            int match = 0;
            for (j = 0; j < from->solid->order; j++)
                if (from->solid->faces[i*from->solid->order + j] == pkey[0] ||
                    from->solid->faces[i*from->solid->order + j] == pkey[1])
                    match++;
            if (match == 2) {
                assert(nf < 2);
                f[nf++] = i;
            }
        }

        assert(nf == 2);

        dp = 0;
        for (i = 0; i < 3; i++)
            dp += (from->solid->normals[f[0]*3+i] *
                   from->solid->normals[f[1]*3+i]);
        angle = (float)acos(dp);
    }

    /*
     * Now transform the polyhedron. We aren't entirely sure
     * whether we need to rotate through angle or -angle, and the
     * simplest way round this is to try both and see which one
     * aligns successfully!
     * 
     * Unfortunately, _both_ will align successfully if this is a
     * cube, which won't tell us anything much. So for that
     * particular case, I resort to gross hackery: I simply negate
     * the angle before trying the alignment, depending on the
     * direction. Which directions work which way is determined by
     * pure trial and error. I said it was gross :-/
     */
    {
        int all_pkey[4];
        int success;

        if (from->solid->order == 4 && direction == UP)
            angle = -angle;            /* HACK */

        poly = transform_poly(from->solid,
                              from->grid->squares[from->current].flip,
                              pkey[0], pkey[1], angle);
        flip_poly(poly, from->grid->squares[ret->current].flip);
        success = align_poly(poly, &from->grid->squares[ret->current], all_pkey);

        if (!success) {
            sfree(poly);
            angle = -angle;
            poly = transform_poly(from->solid,
                                  from->grid->squares[from->current].flip,
                                  pkey[0], pkey[1], angle);
            flip_poly(poly, from->grid->squares[ret->current].flip);
            success = align_poly(poly, &from->grid->squares[ret->current], all_pkey);
        }

        assert(success);
    }

    /*
     * Now we have our rotated polyhedron, which we expect to be
     * exactly congruent to the one we started with - but with the
     * faces permuted. So we map that congruence and thereby figure
     * out how to permute the faces as a result of the polyhedron
     * having rolled.
     */
    {
        int *newcolours = snewn(from->solid->nfaces, int);

        for (i = 0; i < from->solid->nfaces; i++)
            newcolours[i] = -1;

        for (i = 0; i < from->solid->nfaces; i++) {
            int nmatch = 0;

            /*
             * Now go through the transformed polyhedron's faces
             * and figure out which one's normal is approximately
             * equal to this one.
             */
            for (j = 0; j < poly->nfaces; j++) {
                float dist;
                int k;

                dist = 0;

                for (k = 0; k < 3; k++)
                    dist += SQ(poly->normals[j*3+k] -
                               from->solid->normals[i*3+k]);

                if (APPROXEQ(dist, 0)) {
                    nmatch++;
                    newcolours[i] = ret->facecolours[j];
                }
            }

            assert(nmatch == 1);
        }

        for (i = 0; i < from->solid->nfaces; i++)
            assert(newcolours[i] != -1);

        sfree(ret->facecolours);
        ret->facecolours = newcolours;
    }

    ret->movecount++;

    /*
     * And finally, swap the colour between the bottom face of the
     * polyhedron and the face we've just landed on.
     * 
     * We don't do this if the game is already complete, since we
     * allow the user to roll the fully blue polyhedron around the
     * grid as a feeble reward.
     */
    if (!ret->completed) {
        i = lowest_face(from->solid);
        j = ret->facecolours[i];
        ret->facecolours[i] = GET_SQUARE(ret, ret->current);
        SET_SQUARE(ret, ret->current, j);

        /*
         * Detect game completion.
         */
        j = 0;
        for (i = 0; i < ret->solid->nfaces; i++)
            if (ret->facecolours[i])
                j++;
        if (j == ret->solid->nfaces) {
            ret->completed = ret->movecount;
        }

    }

    sfree(poly);

    /*
     * Align the normal polyhedron with its grid square, to get key
     * points for non-animated display.
     */
    {
        int pkey[4];
        int success;

        success = align_poly(ret->solid, &ret->grid->squares[ret->current], pkey);
        assert(success);

        ret->dpkey[0] = pkey[0];
        ret->dpkey[1] = pkey[1];
        ret->dgkey[0] = 0;
        ret->dgkey[1] = 1;
    }


    ret->spkey[0] = pkey[0];
    ret->spkey[1] = pkey[1];
    ret->sgkey[0] = skey[0];
    ret->sgkey[1] = skey[1];
    ret->previous = from->current;
    ret->angle = angle;

    return ret;
}
コード例 #2
0
ファイル: cube.c プロジェクト: JetChen07/sgtpuzzles
static void game_redraw(drawing *dr, game_drawstate *ds,
                        const game_state *oldstate, const game_state *state,
                        int dir, const game_ui *ui,
                        float animtime, float flashtime)
{
    int i, j;
    struct bbox bb = find_bbox(&state->params);
    struct solid *poly;
    const int *pkey, *gkey;
    float t[3];
    float angle;
    int square;

    draw_rect(dr, 0, 0, XSIZE(GRID_SCALE, bb, state->solid),
	      YSIZE(GRID_SCALE, bb, state->solid), COL_BACKGROUND);

    if (dir < 0) {
        const game_state *t;

        /*
         * This is an Undo. So reverse the order of the states, and
         * run the roll timer backwards.
         */
	assert(oldstate);

        t = oldstate;
        oldstate = state;
        state = t;

        animtime = ROLLTIME - animtime;
    }

    if (!oldstate) {
        oldstate = state;
        angle = 0.0;
        square = state->current;
        pkey = state->dpkey;
        gkey = state->dgkey;
    } else {
        angle = state->angle * animtime / ROLLTIME;
        square = state->previous;
        pkey = state->spkey;
        gkey = state->sgkey;
    }
    state = oldstate;

    for (i = 0; i < state->grid->nsquares; i++) {
        int coords[8];

        for (j = 0; j < state->grid->squares[i].npoints; j++) {
            coords[2*j] = ((int)(state->grid->squares[i].points[2*j] * GRID_SCALE)
			   + ds->ox);
            coords[2*j+1] = ((int)(state->grid->squares[i].points[2*j+1]*GRID_SCALE)
			     + ds->oy);
        }

        draw_polygon(dr, coords, state->grid->squares[i].npoints,
                     GET_SQUARE(state, i) ? COL_BLUE : COL_BACKGROUND,
		     COL_BORDER);
    }

    /*
     * Now compute and draw the polyhedron.
     */
    poly = transform_poly(state->solid, state->grid->squares[square].flip,
                          pkey[0], pkey[1], angle);

    /*
     * Compute the translation required to align the two key points
     * on the polyhedron with the same key points on the current
     * face.
     */
    for (i = 0; i < 3; i++) {
        float tc = 0.0;

        for (j = 0; j < 2; j++) {
            float grid_coord;

            if (i < 2) {
                grid_coord =
                    state->grid->squares[square].points[gkey[j]*2+i];
            } else {
                grid_coord = 0.0;
            }

            tc += (grid_coord - poly->vertices[pkey[j]*3+i]);
        }

        t[i] = tc / 2;
    }
    for (i = 0; i < poly->nvertices; i++)
        for (j = 0; j < 3; j++)
            poly->vertices[i*3+j] += t[j];

    /*
     * Now actually draw each face.
     */
    for (i = 0; i < poly->nfaces; i++) {
        float points[8];
        int coords[8];

        for (j = 0; j < poly->order; j++) {
            int f = poly->faces[i*poly->order + j];
            points[j*2] = (poly->vertices[f*3+0] -
                           poly->vertices[f*3+2] * poly->shear);
            points[j*2+1] = (poly->vertices[f*3+1] -
                             poly->vertices[f*3+2] * poly->shear);
        }

        for (j = 0; j < poly->order; j++) {
            coords[j*2] = (int)floor(points[j*2] * GRID_SCALE) + ds->ox;
            coords[j*2+1] = (int)floor(points[j*2+1] * GRID_SCALE) + ds->oy;
        }

        /*
         * Find out whether these points are in a clockwise or
         * anticlockwise arrangement. If the latter, discard the
         * face because it's facing away from the viewer.
         *
         * This would involve fiddly winding-number stuff for a
         * general polygon, but for the simple parallelograms we'll
         * be seeing here, all we have to do is check whether the
         * corners turn right or left. So we'll take the vector
         * from point 0 to point 1, turn it right 90 degrees,
         * and check the sign of the dot product with that and the
         * next vector (point 1 to point 2).
         */
        {
            float v1x = points[2]-points[0];
            float v1y = points[3]-points[1];
            float v2x = points[4]-points[2];
            float v2y = points[5]-points[3];
            float dp = v1x * v2y - v1y * v2x;

            if (dp <= 0)
                continue;
        }

        draw_polygon(dr, coords, poly->order,
                     state->facecolours[i] ? COL_BLUE : COL_BACKGROUND,
		     COL_BORDER);
    }
    sfree(poly);

    draw_update(dr, 0, 0, XSIZE(GRID_SCALE, bb, state->solid),
		YSIZE(GRID_SCALE, bb, state->solid));

    /*
     * Update the status bar.
     */
    {
	char statusbuf[256];

	if (state->completed) {
		strcpy(statusbuf, _("COMPLETED!"));
		strcpy(statusbuf+strlen(statusbuf), " ");
	} else statusbuf[0] = '\0';
	sprintf(statusbuf+strlen(statusbuf), _("Moves: %d"),
		(state->completed ? state->completed : state->movecount));

	status_bar(dr, statusbuf);
    }
}
コード例 #3
0
ファイル: bsptree.c プロジェクト: NautiluX/geomview
/* convert a PolyList into a linked list of PolyListNodes, subdivide
 * non-flat or concave polgons
 */
static PolyListNode *
PolyListToLinkedPoyList(Transform T, Transform Tdual, Transform TxT,
			const void **tagged_app,
			PolyListNode **plistp,
			PolyList *pl, struct obstack *scratch)
{
  PolyListNode *plist = NULL;
  int pnr;
  
  if (!plistp) {
    plistp = &plist;
  }

  PolyListComputeNormals(pl, PL_HASVN|PL_HASPN|PL_HASPFL);
  for (pnr = 0; pnr < pl->n_polys; pnr++) {
    PolyListNode *new_pn;
    Poly *poly;

    if (pl->p[pnr].flags & POLY_NOPOLY) {
      /* degenerated, just skip it */
      continue;
    }

    poly = &pl->p[pnr];
    poly->flags |= pl->geomflags;

    if (T && T != TM_IDENTITY) {
      poly = transform_poly(T, Tdual, TxT, poly, scratch);
    }

    switch (pl->p[pnr].n_vertices) {
    case 3: /* ok */
      new_pn = new_poly_list_node(tagged_app, scratch);
      new_pn->poly = poly;
      ListPush(*plistp, new_pn);
      break;
#if !HAVE_LIBGLU
    case 4: /* supported */
      if (pl->p[pnr].flags & (POLY_NONFLAT|POLY_CONCAVE)) {
	/* split this polygon along a diagonal, if the polygon is
	 * concave: split across the unique concave vertex.
	 */
	int concave;

	if (pl->p[pnr].flags & POLY_CONCAVE) {
	  Point3 nu;

	  /* We need to determine the concave vertex */
	  PolyNormal(poly, &nu, pl->geomflags & VERT_4D, false, NULL,
		     &concave);
	} else {
	  concave= 0;
	}
	split_quad_poly(concave, poly, plistp, tagged_app, scratch);
      } else {
	new_pn = new_poly_list_node(tagged_app, scratch);
	new_pn->poly = poly;
	ListPush(*plistp, new_pn);
      }
      break;
    default:
      if (pl->p[pnr].flags & (POLY_NONFLAT|POLY_CONCAVE)) {
	static int was_here;
	
	if (!was_here ) {
	  GeomError(1, "Non-flat or concave polygons not supported yet.\n");
	  was_here = 1;
	}
      }
      new_pn = new_poly_list_node(tagged_app, scratch);
      new_pn->poly = poly;
      ListPush(*plistp, new_pn);
      break;
#else
    case 4:
      /* if we want to be able to render polygons with
       * self-intersections "correctly", then we always have to use
       * the GLU tesselater for polygons with more than 4 vertices and
       * for non-convex quadrilaterals. We can handle non-flat
       * quadrilaterals ourselves.
       */
      if ((pl->p[pnr].flags & (POLY_NONFLAT|POLY_CONCAVE)) == POLY_NONFLAT) {
	/* Split this polygon along a diagonal. Leave concave
	 * quadrilaterals to the GLU tesselator; they could have
	 * self-intersections.
	 */
	split_quad_poly(0, poly, plistp, tagged_app, scratch);
      } else if ((pl->p[pnr].flags & POLY_CONCAVE) == 0) {
	new_pn = new_poly_list_node(tagged_app, scratch);
	new_pn->poly = poly;
	ListPush(*plistp, new_pn);
      }
      break;
      /* otherwise fall into the > 4 vertices case and leave
       * everything to the GLU tesselator.
       */
    default: {
      /* We use the GLU tesselator here, if available. It is not
       * necessary to reinvent the wheel; also, the OpenGL MG backend
       * also uses the tesselator (so we will get comparable shapes
       * w/o translucency).
       */
      static GLUtesselator *glutess;
      struct tess_data tessdata[1];
      VARARRAY2(dv, GLdouble, poly->n_vertices, 3);
      Vertex **vp;
      int i;
      
      if (glutess == NULL) {
	glutess = gluNewTess();
	gluTessProperty(glutess,
			GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO);
	gluTessCallback(glutess, GLU_TESS_BEGIN_DATA,
			(GLvoid (*)())tess_begin_data);
	gluTessCallback(glutess, GLU_TESS_VERTEX_DATA,
			(GLvoid (*)())tess_vertex_data);
	gluTessCallback(glutess, GLU_TESS_COMBINE_DATA,
			(GLvoid (*)())tess_combine_data);
      }

      tessdata->trickyp    = poly;
      tessdata->polyflags  = poly->flags;
      tessdata->pn         = &poly->pn;
      tessdata->scratch    = scratch;
      tessdata->plistp     = plistp;
      tessdata->tagged_app = tagged_app;

      /* tell GLU what we think is a good approximation for the normal */
      gluTessNormal(glutess, poly->pn.x, poly->pn.y, poly->pn.z);

      /* rest is done in the callback functions */
      gluTessBeginPolygon(glutess, tessdata);
      gluTessBeginContour(glutess);
      for (i = 0, vp = poly->v; i < poly->n_vertices; i++, vp++) {
	HPt3Coord w = (*vp)->pt.w ? (*vp)->pt.w : 1e20;

	if (w == 1.0) {
	  dv[i][0] = (*vp)->pt.x;
	  dv[i][1] = (*vp)->pt.y;
	  dv[i][2] = (*vp)->pt.z;
	} else {
	  dv[i][0] = (*vp)->pt.x / w;
	  dv[i][1] = (*vp)->pt.y / w;
	  dv[i][2] = (*vp)->pt.z / w;
	}
	gluTessVertex(glutess, dv[i], *vp);
      }
      gluTessEndContour(glutess);
      gluTessEndPolygon(glutess);
      break; /* out of switch */
    } /* default */
#endif
    } /* switch */
  } /* for */
  return *plistp;
}