Ejemplo n.º 1
0
/* Compute the plane equation for this polygon; copy the normal for
 * originally flat polygons, really compute it for polygons created by
 * sub-division from non-flat polygons.
 */
static inline void PolyPlane(PolyListNode *plnode, HPoint3 *plane)
{
  if (plnode->pn) {
    *(Point3 *)plane = *plnode->pn;
  } else if (plnode->poly->flags & POLY_NONFLAT) {
    /* The polygon actually _IS_ flat -- BSPTreeCreate() is
     * responsible to make that sure, but we have to compute the
     * normal ourselves. We do not need to remember the normal because
     * when this function is called, then plnode has found its "home"
     * tree-node.
     */
    PolyNormal(plnode->poly, (Point3 *)(void *)plane,
	       true /* fourd */, false /* evert */, NULL, NULL);
  } else {
    *(Point3 *)plane = plnode->poly->pn;
  }
  plane->w = HPt3DotPt3(&plnode->poly->v[0]->pt, (Point3 *)(void *)plane);
}
Ejemplo n.º 2
0
void UKismetMathLibrary::MinimumAreaRectangle(class UObject* WorldContextObject, const TArray<FVector>& InVerts, const FVector& SampleSurfaceNormal, FVector& OutRectCenter, FRotator& OutRectRotation, float& OutSideLengthX, float& OutSideLengthY, bool bDebugDraw)
{
	float MinArea = -1.f;
	float CurrentArea = -1.f;
	FVector SupportVectorA, SupportVectorB;
	FVector RectSideA, RectSideB;
	float MinDotResultA, MinDotResultB, MaxDotResultA, MaxDotResultB;
	FVector TestEdge;
	float TestEdgeDot = 0.f;
	FVector PolyNormal(0.f, 0.f, 1.f);
	TArray<int32> PolyVertIndices;

	// Bail if we receive an empty InVerts array
	if( InVerts.Num() == 0 )
	{
		return;
	}

	// Compute the approximate normal of the poly, using the direction of SampleSurfaceNormal for guidance
	PolyNormal = (InVerts[InVerts.Num() / 3] - InVerts[0]) ^ (InVerts[InVerts.Num() * 2 / 3] - InVerts[InVerts.Num() / 3]);
	if( (PolyNormal | SampleSurfaceNormal) < 0.f )
	{
		PolyNormal = -PolyNormal;
	}

	// Transform the sample points to 2D
	FMatrix SurfaceNormalMatrix = FRotationMatrix::MakeFromZX(PolyNormal, FVector(1.f, 0.f, 0.f));
	TArray<FVector> TransformedVerts;
	OutRectCenter = FVector(0.f);
	for( int32 Idx = 0; Idx < InVerts.Num(); ++Idx )
	{
		OutRectCenter += InVerts[Idx];
		TransformedVerts.Add(SurfaceNormalMatrix.InverseTransformVector(InVerts[Idx]));
	}
	OutRectCenter /= InVerts.Num();

	// Compute the convex hull of the sample points
	ConvexHull2D::ComputeConvexHull(TransformedVerts, PolyVertIndices);

	// Minimum area rectangle as computed by http://www.geometrictools.com/Documentation/MinimumAreaRectangle.pdf
	for( int32 Idx = 1; Idx < PolyVertIndices.Num() - 1; ++Idx )
	{
		SupportVectorA = (TransformedVerts[PolyVertIndices[Idx]] - TransformedVerts[PolyVertIndices[Idx-1]]).SafeNormal();
		SupportVectorA.Z = 0.f;
		SupportVectorB.X = -SupportVectorA.Y;
		SupportVectorB.Y = SupportVectorA.X;
		SupportVectorB.Z = 0.f;
		MinDotResultA = MinDotResultB = MaxDotResultA = MaxDotResultB = 0.f;

		for (int TestVertIdx = 1; TestVertIdx < PolyVertIndices.Num(); ++TestVertIdx )
		{
			TestEdge = TransformedVerts[PolyVertIndices[TestVertIdx]] - TransformedVerts[PolyVertIndices[0]];
			TestEdgeDot = SupportVectorA | TestEdge;
			if( TestEdgeDot < MinDotResultA )
			{
				MinDotResultA = TestEdgeDot;
			}
			else if(TestEdgeDot > MaxDotResultA )
			{
				MaxDotResultA = TestEdgeDot;
			}

			TestEdgeDot = SupportVectorB | TestEdge;
			if( TestEdgeDot < MinDotResultB )
			{
				MinDotResultB = TestEdgeDot;
			}
			else if( TestEdgeDot > MaxDotResultB )
			{
				MaxDotResultB = TestEdgeDot;
			}
		}

		CurrentArea = (MaxDotResultA - MinDotResultA) * (MaxDotResultB - MinDotResultB);
		if( MinArea < 0.f || CurrentArea < MinArea )
		{
			MinArea = CurrentArea;
			RectSideA = SupportVectorA * (MaxDotResultA - MinDotResultA);
			RectSideB = SupportVectorB * (MaxDotResultB - MinDotResultB);
		}
	}

	RectSideA = SurfaceNormalMatrix.TransformVector(RectSideA);
	RectSideB = SurfaceNormalMatrix.TransformVector(RectSideB);
	OutRectRotation = FRotationMatrix::MakeFromZX(PolyNormal, RectSideA).Rotator();
	OutSideLengthX = RectSideA.Size();
	OutSideLengthY = RectSideB.Size();

	if( bDebugDraw )
	{
		DrawDebugSphere(GEngine->GetWorldFromContextObject(WorldContextObject), OutRectCenter, 10.f, 12, FColor::Yellow, true);
		DrawDebugCoordinateSystem(GEngine->GetWorldFromContextObject(WorldContextObject), OutRectCenter, SurfaceNormalMatrix.Rotator(), 100.f, true);
		DrawDebugLine(GEngine->GetWorldFromContextObject(WorldContextObject), OutRectCenter - RectSideA * 0.5f + FVector(0,0,10.f), OutRectCenter + RectSideA * 0.5f + FVector(0,0,10.f), FColor::Green, true,-1, 0, 5.f);
		DrawDebugLine(GEngine->GetWorldFromContextObject(WorldContextObject), OutRectCenter - RectSideB * 0.5f + FVector(0,0,10.f), OutRectCenter + RectSideB * 0.5f + FVector(0,0,10.f), FColor::Blue, true,-1, 0, 5.f);
	}
}
Ejemplo n.º 3
0
/* 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;
}
Ejemplo n.º 4
0
/* Convert a Mesh into linked list of PolyListNodes, subdivide
 * non-flat or concave quadrilaterals.
 */
static PolyListNode *
MeshToLinkedPolyList(Transform T, Transform Tdual, Transform TxT,
		     const void **tagged_app, PolyListNode **plistp,
		     Mesh *mesh, struct obstack *scratch)
{
  PolyListNode *plist = NULL;
  Poly *qpoly;
  int v0 = 1, prev0v = 0;
  int u0 = 1, prev0u = 0;
  int u, v, prevu, prevv;
  int concave;
  int i;

  if (!plistp) {
    plistp = &plist;
  }

  MeshComputeNormals(mesh, MESH_N);

  if(mesh->geomflags & MESH_UWRAP) {
    v0 = 0, prev0v = mesh->nv-1;
  }
  if(mesh->geomflags & MESH_VWRAP) {
    u0 = 0, prev0u = mesh->nu-1;
  }

#define MESHIDX(u, v, mesh) ((v)*(mesh)->nu + (u))
  qpoly = NULL;
  for(prevv = prev0v, v = v0; v < mesh->nv; prevv = v, v++) {
    for(prevu = prev0u, u = u0; u < mesh->nu; prevu = u, u++) {
      /* First try to create a single polygon, if that mesh-cell is
       * non-flat or concave, then sub-divide it.
       */
      if (!qpoly) {
	qpoly = new_poly(4, NULL, scratch);
	for (i = 0; i < 4; i++) {
	  qpoly->v[i] = obstack_alloc(scratch, sizeof(Vertex));
	}
      }
      if (T && T != TM_IDENTITY) {
	meshv_to_polyv_trans(T, Tdual, TxT, qpoly->v[0], mesh,
			     MESHIDX(prevu, prevv, mesh));
	meshv_to_polyv_trans(T, Tdual, TxT, qpoly->v[1], mesh,
			     MESHIDX(u, prevv, mesh));
	meshv_to_polyv_trans(T, Tdual, TxT, qpoly->v[2], mesh,
			     MESHIDX(u, v, mesh));
	meshv_to_polyv_trans(T, Tdual, TxT, qpoly->v[3], mesh,
			     MESHIDX(prevu, v, mesh));
      } else {
	meshv_to_polyv(qpoly->v[0], mesh, MESHIDX(prevu, prevv, mesh));
	meshv_to_polyv(qpoly->v[1], mesh, MESHIDX(u, prevv, mesh));
	meshv_to_polyv(qpoly->v[2], mesh, MESHIDX(u, v, mesh));
	meshv_to_polyv(qpoly->v[3], mesh, MESHIDX(prevu, v, mesh));
      }

      if (mesh->geomflags & MESH_C) {
	qpoly->flags |= PL_HASVCOL;
      }
      if (mesh->geomflags & COLOR_ALPHA) {
	qpoly->flags |= COLOR_ALPHA;
      }
      if (mesh->geomflags & MESH_N) {
	qpoly->flags |= PL_HASVN;
      }
      if (mesh->geomflags & MESH_U) {
	qpoly->flags |= PL_HASST;
      }

      PolyNormal(qpoly, &qpoly->pn, mesh->geomflags & VERT_4D, false,
		 &qpoly->flags, &concave);
      qpoly->flags |= PL_HASPN;
      if (qpoly->flags & POLY_NOPOLY) {
	/* polygon is degenerated, we just do NOT draw it, edges are
	 * drawn by other methods. Just do nothing, qpoly will be
	 * re-used for the next quad.
	 */
	qpoly->flags = 0;
      } else if (qpoly->flags & (POLY_CONCAVE|POLY_NONFLAT)) {
	/* we need to split it */
	split_quad_poly(concave, qpoly, plistp, tagged_app, scratch);
	qpoly = NULL;
      } else {
	PolyListNode *new_pn;
	
	new_pn = new_poly_list_node(tagged_app, scratch);
	new_pn->poly = qpoly;
	ListPush(*plistp, new_pn);
	qpoly = NULL;
      }
    }
  }

#undef MESHIDX
  
  return *plistp;
}
Ejemplo n.º 5
0
static PolyListNode *
QuadToLinkedPolyList(Transform T, Transform Tdual, Transform TxT,
		     const void **tagged_app, PolyListNode **plistp,
		     Quad *quad, struct obstack *scratch)
{
  PolyListNode *plist = NULL;
  Poly *qpoly;
  int i, j, concave;

  (void)Tdual;
  (void)TxT;

  if (!plistp) {
    plistp = &plist;
  }

  if(!(quad->geomflags & QUAD_N)) {
    QuadComputeNormals(quad);
  }
  
  qpoly = NULL;
  for (i = 0; i < quad->maxquad; i++) {
    /* First try to create a single polygon, if that mesh-cell is
     * non-flat or concave, then sub-divide it.
     */
    if (!qpoly) {
      qpoly = new_poly(4, NULL, scratch);
      for (j = 0; j < 4; j++) {
	qpoly->v[j] = obstack_alloc(scratch, sizeof(Vertex));
      }
    }
    if (T && T != TM_IDENTITY) {
      for (j = 0; j < 4; j++) {
	memset(qpoly->v[j], 0, sizeof(Vertex));
	HPt3Transform(T, &quad->p[i][j], &qpoly->v[j]->pt);
	NormalTransform(T, &quad->n[i][j], &qpoly->v[j]->vn);
      }
    } else {
      for (j = 0; j < 4; j++) {
	memset(qpoly->v[j], 0, sizeof(Vertex));
	qpoly->v[j]->pt   = quad->p[i][j];
	qpoly->v[j]->vn   = quad->n[i][j];
      }
    }
    qpoly->flags |= PL_HASVN;
    if (quad->geomflags & QUAD_C) {
      qpoly->flags |= PL_HASVCOL;
      for (j = 0; j < 4; j++) {
	qpoly->v[j]->vcol = quad->c[i][j];
      }
    }
    if (quad->geomflags & COLOR_ALPHA) {
      qpoly->flags |= COLOR_ALPHA;
    }
    PolyNormal(qpoly, &qpoly->pn,
	       quad->geomflags & VERT_4D, false, &qpoly->flags, &concave);
    qpoly->flags |= PL_HASPN;
    if (qpoly->flags & POLY_NOPOLY) {
      /* degenerated, skip it, but reuse the memory region.  */
      qpoly->flags = 0;
    } else if (qpoly->flags & (POLY_CONCAVE|POLY_NONFLAT)) {
      /* we need to split it */
      split_quad_poly(concave, qpoly, plistp, tagged_app, scratch);
      qpoly = NULL;
    } else {
      PolyListNode *new_pn;
      
      new_pn = new_poly_list_node(tagged_app, scratch);
      new_pn->poly = qpoly;
      ListPush(*plistp, new_pn);
      qpoly = NULL;
    }
  }
  
  return *plistp;
}
Ejemplo n.º 6
0
/* Split plnode along plane. We know that plane really intersects
 * plnode->poly. We also know that plnode->poly is planar and convex.
 *
 * Given this assumptions we know that plnode->poly has to be split
 * into exactly two pieces. 
 */
static inline void SplitPolyNode(PolyListNode *plnode,
				 PolyListNode **front, PolyListNode **back,
				 EdgeIntersection edges[2],
				 struct obstack *scratch)
{
  const void **tagged_app = plnode->tagged_app;
  Poly *poly = plnode->poly, savedp;
  VARARRAY(savedv, Vertex *, poly->n_vertices);
  Vertex *v0, *v1, **vpos;
  int istart[2], iend[2], i, nv[2];
  Vertex *vstart[2], *vend[2];

#if BSPTREE_STATS
  ++n_tree_polys;
#endif

  vstart[0] = vstart[1] = vend[0] = vend[1] = NULL;
  istart[0] = istart[1] = iend[0] = iend[1] = -1;

  /* first point of intersection */
  if (fzero(edges[0].scp[0])) {
    v0 = poly->v[edges[0].v[0]];
    if (fpos(edges[0].scp[1])) {
      istart[0] = edges[0].v[0];
      iend[1]   = edges[0].v[0];
    } else {
      istart[1] = edges[0].v[0];
      iend[0]   = edges[0].v[0]; 
    }
  } else if (fzero(edges[0].scp[1])) {
    v0 = poly->v[edges[0].v[1]];
    if (fpos(edges[0].scp[0])) {
      istart[1] = edges[0].v[1];
      iend[0]   = edges[0].v[1];
    } else {
      istart[0] = edges[0].v[1];
      iend[1]   = edges[0].v[1]; 
    }
  } else {
    HPt3Coord mu0, mu1;
    Vertex *V0 = poly->v[edges[0].v[0]];
    Vertex *V1 = poly->v[edges[0].v[1]];
    
    v0 = obstack_alloc(scratch, sizeof(Vertex));
    mu0 = edges[0].scp[1]/(edges[0].scp[1]-edges[0].scp[0]);
#if 0
    mu1 = edges[0].scp[0]/(edges[0].scp[0]-edges[0].scp[1]);
#else
    mu1 = 1.0 - mu0;
#endif
    /* Use denormalized variant; otherwise textures may come out wrong
     * because the homogeneous divisor is used for perspective
     * corrections.
     */
    if (poly->flags & VERT_ST) {
      v0->st.s = mu0 * V0->st.s + mu1 * V1->st.s;
      v0->st.t = mu0 * V0->st.t + mu1 * V1->st.t;
      HPt3LinSumDenorm(mu0, &V0->pt, mu1, &V1->pt, &v0->pt);
    } else {
      HPt3LinSum(mu0, &V0->pt, mu1, &V1->pt, &v0->pt);
    } 
    if (!finite(v0->pt.x + v0->pt.y + v0->pt.z)){
      abort();
    }
    if (poly->flags & VERT_C) {
      CoLinSum(mu0, &V0->vcol, mu1, &V1->vcol, &v0->vcol);
    }
    if (true || (poly->flags & VERT_N)) {
      /* The averaged vertex normals do not have an orientation, so
       * try to orient them w.r.t. the polygon normal before computing
       * the linear combination.
       */
      if (Pt3Dot(&V0->vn, &poly->pn)*Pt3Dot(&V1->vn, &poly->pn) < 0) {
	Pt3Comb(-mu0, &V0->vn, mu1, &V1->vn, &v0->vn);
      } else {
	Pt3Comb(mu0, &V0->vn, mu1, &V1->vn, &v0->vn);
      }
      Pt3Unit(&v0->vn);
    }

    if (fpos(edges[0].scp[0])) {
      vstart[1] = vend[0] = v0;
      istart[1] = edges[0].v[1];
      iend[0]   = edges[0].v[0];
    } else {
      vstart[0] = vend[1] = v0;
      istart[0] = edges[0].v[1];
      iend[1]   = edges[0].v[0];
    }
  }

  /* second point of intersection */
  if (fzero(edges[1].scp[0])) {
    v1 = poly->v[edges[1].v[0]];
    if (fpos(edges[1].scp[1])) {
      istart[0] = edges[1].v[0];
      iend[1]   = edges[1].v[0];
    } else {
      istart[1] = edges[1].v[0];
      iend[0]   = edges[1].v[0]; 
    }
  } else if (fzero(edges[1].scp[1])) {
    v1 = poly->v[edges[1].v[1]];
    if (fpos(edges[1].scp[0])) {
      istart[1] = edges[1].v[1];
      iend[0]   = edges[1].v[1];
    } else {
      istart[0] = edges[1].v[1];
      iend[1]   = edges[1].v[1]; 
    }
  } else {
    HPt3Coord mu0, mu1;
    Vertex *V0 = poly->v[edges[1].v[0]];
    Vertex *V1 = poly->v[edges[1].v[1]];
    
    v1 = obstack_alloc(scratch, sizeof(Vertex));
    mu0 = edges[1].scp[1]/(edges[1].scp[1]-edges[1].scp[0]);
#if 0
    mu1 = edges[1].scp[0]/(edges[1].scp[0]-edges[1].scp[1]);
#else
    mu1 = 1.0 - mu0;
#endif
    if (poly->flags & VERT_ST) {
      v1->st.s = mu0 * V0->st.s + mu1 * V1->st.s;
      v1->st.t = mu0 * V0->st.t + mu1 * V1->st.t;
      HPt3LinSumDenorm(mu0, &V0->pt, mu1, &V1->pt, &v1->pt);
    } else {
      HPt3LinSum(mu0, &V0->pt, mu1, &V1->pt, &v1->pt);
    }
    if (!finite(v1->pt.x + v1->pt.y + v1->pt.z))
      abort();
    if (poly->flags & VERT_C) {
      CoLinSum(mu0, &V0->vcol, mu1, &V1->vcol, &v1->vcol);
    }
    if (true || (poly->flags & VERT_N)) {
      if (Pt3Dot(&V0->vn, &poly->pn)*Pt3Dot(&V1->vn, &poly->pn) < 0) {
	Pt3Comb(-mu0, &V0->vn, mu1, &V1->vn, &v1->vn);
      } else {
	Pt3Comb(mu0, &V0->vn, mu1, &V1->vn, &v1->vn);
      }
      Pt3Unit(&v1->vn);
    }

    if (fpos(edges[1].scp[0])) {
      vstart[1] = vend[0] = v1;
      istart[1] = edges[1].v[1];
      iend[0]   = edges[1].v[0];
    } else {
      vstart[0] = vend[1] = v1;
      istart[0] = edges[1].v[1];
      iend[1]   = edges[1].v[0];
    }
  }

  ListPush(*front, plnode);
  ListPush(*back, new_poly_list_node(tagged_app, scratch));

  if ((poly->flags & POLY_NONFLAT)) {
    if (!(*front)->pn) {
      /* Compute the normal on the parent element to avoid numerical
       * instabilities on increasingly degenerated polygons.
       */
      (*front)->pn = obstack_alloc(scratch, sizeof(Point3));
      PolyNormal(poly, (*front)->pn,
		 true /* 4d */, false /* evert */, NULL, NULL);
    }
    (*back)->pn = (*front)->pn;
  }

  for (i = 0; i < 2; i++) {
    nv[i] = iend[i] - istart[i] + 1;
    if (nv[i] < 0) {
      nv[i] += poly->n_vertices;
    }
    nv[i] += (vstart[i] != NULL) + (vend[i] != NULL);
  }

  if (poly->flags & POLY_SCRATCH) {

    savedp = *poly;
    memcpy(savedv, poly->v, poly->n_vertices*sizeof(Vertex *));

    if (nv[0] <= poly->n_vertices) {
      poly->n_vertices = nv[0];
      (*front)->poly = poly;
      (*back)->poly  = new_poly(nv[1], NULL, scratch);
    } else {
      if (nv[1] > poly->n_vertices) {
	abort();
      }
      poly->n_vertices = nv[1];
      (*back)->poly = poly;
      (*front)->poly  = new_poly(nv[0], NULL, scratch);
    }
    
    /* Attention: gcc had problems with this code snippet with
     * -fstrict-aliasing, the "#if 1" stuff seems to work. In the
     * "#else" version gcc somehow lost the "savedp.v = savedv"
     * assignment. I think this is a compiler bug.
     */
    poly = &savedp;
#if 1
    poly->v = savedv;
#else
    savedp.v = savedv;
#endif
  } else {
    (*front)->poly  = new_poly(nv[0], NULL, scratch);
    (*back)->poly  = new_poly(nv[1], NULL, scratch);
  }

  for (i = 0; i < 2; i++) {
    PolyListNode *half = (i == 0) ? *front : *back;
    int j;

    vpos = half->poly->v;
    if (vstart[i] != NULL) {
      *vpos++ = vstart[i];
    }
    if (istart[i] <= iend[i]) {
      for (j = istart[i]; j <= iend[i] && j < poly->n_vertices; j++) {
	*vpos++ = poly->v[j];
      }
    } else {
      for (j = istart[i]; j < poly->n_vertices; j++) {
	*vpos++ = poly->v[j];
      }
      for (j = 0; j <= iend[i]; j++) {
	*vpos++ = poly->v[j];
      }
    }
    if (vend[i] != NULL) {
      *vpos++ = vend[i];
    }
    half->poly->pcol  = poly->pcol;
    half->poly->pn    = poly->pn;
    half->poly->flags = poly->flags|POLY_SCRATCH;

    check_poly(half->poly);
  }
}