Metropoli BBS
VIEWER: bsp2poly.c MODE: TEXT (ASCII)
/******************************************************************************
* Bsp2Poly.c - Bezier to polygon/polylines conversion routines.		      *
*******************************************************************************
* Written by Gershon Elber, Mar. 90.					      *
******************************************************************************/

#include "cagd_loc.h"

static CagdPolygonStruct *BspC1Srf2Polygons(CagdSrfStruct *Srf, int FineNess,
			 CagdBType ComputeNormals, CagdBType FourPerFlat);

/*****************************************************************************
* Routine to convert a single bspline surface to set of triangles	     *
* approximating it. FineNess is a finess control on result and the bigger it *
* is more triangles may result. a value of 10 is a good start value.	     *
* NULL is returned in case of an error, otherwise list of CagdPolygonStruct. *
*   This routine looks for C1 discontinuities in the surface and split it    *
* into C1 continuous patches to invoke BspC1Srf2Polygons to gen. polygons.   *
*****************************************************************************/
CagdPolygonStruct *BspSrf2Polygons(CagdSrfStruct *Srf, int FineNess,
			 CagdBType ComputeNormals, CagdBType FourPerFlat)
{
    CagdRType u, v;
    int UOrder = Srf -> UOrder,
	VOrder = Srf -> VOrder,
	ULength = Srf -> ULength,
	VLength = Srf -> VLength;
    CagdBType
	HasUDiscont = BspKnotC1Discont(Srf -> UKnotVector, UOrder, ULength, &u),
	HasVDiscont = BspKnotC1Discont(Srf -> VKnotVector, VOrder, VLength, &v);
    CagdPolygonStruct *Poly;

    if (HasUDiscont || HasVDiscont) {
	CagdSrfStruct
	    *Srf1 = HasUDiscont ? BspSrfSubdivAtParam(Srf, u, CAGD_CONST_U_DIR)
				: BspSrfSubdivAtParam(Srf, v, CAGD_CONST_V_DIR),
	    *Srf2 = Srf1 -> Pnext;
	CagdPolygonStruct
	    *Poly1 = BspSrf2Polygons(Srf1, FineNess, ComputeNormals, FourPerFlat),
	    *Poly2 = BspSrf2Polygons(Srf2, FineNess, ComputeNormals, FourPerFlat);

	CagdSrfFreeList(Srf1);

	/* Chain the two lists together: */
	for (Poly = Poly1; Poly -> Pnext != NULL; Poly = Poly -> Pnext);
	Poly -> Pnext = Poly2;
	Poly = Poly1;
    }
    else
	Poly = BspC1Srf2Polygons(Srf, FineNess, ComputeNormals, FourPerFlat);

    return Poly;
}

/*****************************************************************************
* Routine to convert a single C1 continuouse bspline srf to set of triangles *
* approximating it. FineNess is a finess control on result and the bigger it *
* is more triangles may result. a value of 10 is a good start value.	     *
* NULL is returned in case of an error, otherwise list of CagdPolygonStruct. *
*****************************************************************************/
static CagdPolygonStruct *BspC1Srf2Polygons(CagdSrfStruct *Srf, int FineNess,
			 CagdBType ComputeNormals, CagdBType FourPerFlat)
{
    int i, j, FineNessU1, FineNessV1, FineNessU, FineNessV, BaseIndex;
    CagdRType u, v, UMin, UMax, VMin, VMax, *Pt;
    CagdPointType
	PType = Srf -> PType;
    CagdPtStruct PtCenter, *Pt1, *Pt2, *Pt3, *Pt4, *PtMesh, *PtMeshPtr;
    CagdVecStruct NlCenter, *Nl1, *Nl2, *Nl3, *Nl4, *PtNrml;
    CagdCrvStruct *Crv;
    CagdPolygonStruct *Poly,
	*PolyHead = NULL;

    if (!CAGD_IS_BSPLINE_SRF(Srf)) return NULL;

    /* Simple heuristic to estimate how many samples to compute. */
    FineNessU = Srf -> ULength * FineNess / 10;
    FineNessV = Srf -> VLength * FineNess / 10;


    if (FineNessU < 2) FineNessU = 2;
    if (FineNessV < 2) FineNessV = 2;
    switch (_CagdLin2Poly) {
	case CAGD_REG_POLY_PER_LIN:
	    break;
    	case CAGD_ONE_POLY_PER_LIN:
	    if (Srf -> UOrder == 2) FineNessU = 2;
	    if (Srf -> VOrder == 2) FineNessV = 2;
	    break;
    	case CAGD_ONE_POLY_PER_COLIN:
	    break;
    }
    FineNessU1 = FineNessU - 1;
    FineNessV1 = FineNessV - 1;

    /* Current to surface property such as curvature is used as subdivison   */
    /* criterion and the surface is subdivided, equally spaced in parametric */
    /* space, using FineNess as number of subdivisions per axis.	     */

    /* Allocate a mesh to hold all vertices so common vertices need not be   */
    /* Evaluated twice, and evaluate the surface at these mesh points.	     */
    PtMeshPtr = PtMesh = (CagdPtStruct *) CagdMalloc(FineNessU * FineNessV *
							sizeof(CagdPtStruct));

    BspSrfDomain(Srf, &UMin, &UMax, &VMin, &VMax);

    for (i = 0; i < FineNessU; i++) {
	u = UMin + (UMax - UMin) * i / ((CagdRType) FineNessU1);
	Crv = BspSrfCrvFromSrf(Srf, u, CAGD_CONST_U_DIR);

	for (j = 0; j < FineNessV; j++) {
	    v = VMin + (VMax - VMin) * j / ((CagdRType) FineNessV1);
	    Pt = BspCrvEvalAtParam(Crv, v);
	    CagdCoerceToE3(PtMeshPtr -> Pt, &Pt, -1, PType);
	    PtMeshPtr++;
	}

	CagdCrvFree(Crv);
    }

    if (ComputeNormals) PtNrml = BspSrfMeshNormals(Srf, FineNessU, FineNessV);

    /* Now that we have the mesh, create the polygons. */
    for (i = 0; i < FineNessU1; i++)
	for (j = 0; j < FineNessV1; j++) {
	    BaseIndex = i * FineNessV + j;
	    Pt1 = &PtMesh[BaseIndex];        /* Cache the four flat corners. */
	    Pt2 = &PtMesh[BaseIndex + 1];
	    Pt3 = &PtMesh[BaseIndex + FineNessV + 1];
	    Pt4 = &PtMesh[BaseIndex + FineNessV];
	    if (ComputeNormals) {
		Nl1 = &PtNrml[BaseIndex];
		Nl2 = &PtNrml[BaseIndex + 1];
		Nl3 = &PtNrml[BaseIndex + FineNessV + 1];
		Nl4 = &PtNrml[BaseIndex + FineNessV];
	    }

	    if (FourPerFlat) {  /* Eval middle point and create 4 triangles. */
		CAGD_COPY_POINT(PtCenter, *Pt1);
		CAGD_ADD_POINT(PtCenter, *Pt2);
		CAGD_ADD_POINT(PtCenter, *Pt3);
		CAGD_ADD_POINT(PtCenter, *Pt4);
		CAGD_MULT_POINT(PtCenter, 0.25);

		if (ComputeNormals) {
		    /* Average the four normals to find the middle one. */
		    CAGD_COPY_VECTOR(NlCenter, *Nl1);
		    CAGD_ADD_VECTOR(NlCenter, *Nl2);
		    CAGD_ADD_VECTOR(NlCenter, *Nl3);
		    CAGD_ADD_VECTOR(NlCenter, *Nl4);
		    CAGD_NORMALIZE_VECTOR(NlCenter);
		}

		Poly = _CagdMakePolygon(ComputeNormals, Pt1, Pt2, &PtCenter,
							Nl1, Nl2, &NlCenter);
		CAGD_LIST_PUSH(Poly, PolyHead);
		Poly = _CagdMakePolygon(ComputeNormals, Pt2, Pt3, &PtCenter,
						        Nl2, Nl3, &NlCenter);
		CAGD_LIST_PUSH(Poly, PolyHead);
		Poly = _CagdMakePolygon(ComputeNormals, Pt3, Pt4, &PtCenter,
						        Nl3, Nl4, &NlCenter);
		CAGD_LIST_PUSH(Poly, PolyHead);
		Poly = _CagdMakePolygon(ComputeNormals, Pt4, Pt1, &PtCenter,
						        Nl4, Nl1, &NlCenter);
		CAGD_LIST_PUSH(Poly, PolyHead);
	    }
	    else {			   /* Only two along the diagonal... */
		Poly = _CagdMakePolygon(ComputeNormals, Pt1, Pt2, Pt3,
						        Nl1, Nl2, Nl3);
		CAGD_LIST_PUSH(Poly, PolyHead);
		Poly = _CagdMakePolygon(ComputeNormals, Pt3, Pt4, Pt1,
						        Nl3, Nl4, Nl1);
		CAGD_LIST_PUSH(Poly, PolyHead);
	    }
	}

    CagdFree((VoidPtr) PtMesh);
    if (ComputeNormals) CagdFree((VoidPtr) PtNrml);

    return PolyHead;
}

/*****************************************************************************
* Routine to convert a single bspline surface to NumOfIsolines polylines list*
* in each param. direction with SamplesPerCurve in each isoparametric curve. *
* Polyline are always E3 of CagdPolylineStruct type.			     *
* Iso parametric curves are sampled equally spaced in parametric space.	     *
* NULL is returned in case of an error, otherwise list of CagdPolylineStruct.*
* Attempt is made to extract isolines along C1 discontinuities first.	     *
*****************************************************************************/
CagdPolylineStruct *BspSrf2Polylines(CagdSrfStruct *Srf, int NumOfIsocurves,
							 int SamplesPerCurve)
{
    int i, NumC1Disconts;
    CagdRType u, v, UMin, UMax, VMin, VMax, *C1Disconts, *IsoParams;
    CagdCrvStruct *Crv;
    CagdPolylineStruct *Poly,
	*PolyList = NULL;

    if (!CAGD_IS_BSPLINE_SRF(Srf)) return NULL;

    /* Make sure requested format is something reasonable. */
    if (SamplesPerCurve < 1) SamplesPerCurve = 1;
    if (SamplesPerCurve > CAGD_MAX_BEZIER_CACHE_ORDER)
	SamplesPerCurve = CAGD_MAX_BEZIER_CACHE_ORDER;
    if (NumOfIsocurves < 2) NumOfIsocurves = 2;

    BspSrfDomain(Srf, &UMin, &UMax, &VMin, &VMax);

    /* Compute discontinuities along the u axis and use that to determine    */
    /* where to extract isolines along u.				     */
    C1Disconts = BspKnotAllC1Discont(Srf -> UKnotVector, Srf -> UOrder,
				     Srf -> ULength, &NumC1Disconts);
    IsoParams = BspKnotParamValues(UMin, UMax, NumOfIsocurves, C1Disconts,
								NumC1Disconts);
    for (i = 0; i < NumOfIsocurves; i++) {
	u = IsoParams[i];

	Crv = BspSrfCrvFromSrf(Srf, u, CAGD_CONST_U_DIR);
	Poly = BspCrv2Polyline(Crv, SamplesPerCurve);
	Poly -> Pnext = PolyList;
	PolyList = Poly;
	CagdCrvFree(Crv);
    }
    CagdFree((VoidPtr) IsoParams);

    /* Compute discontinuities along the v axis and use that to determine    */
    /* where to extract isolines along v.				     */
    C1Disconts = BspKnotAllC1Discont(Srf -> VKnotVector, Srf -> VOrder,
				     Srf -> VLength, &NumC1Disconts);
    IsoParams = BspKnotParamValues(VMin, VMax, NumOfIsocurves, C1Disconts,
								NumC1Disconts);
    for (i = 0; i < NumOfIsocurves; i++) {
	v = IsoParams[i];

	Crv = BspSrfCrvFromSrf(Srf, v, CAGD_CONST_V_DIR);
	Poly = BspCrv2Polyline(Crv, SamplesPerCurve);
	Poly -> Pnext = PolyList;
	PolyList = Poly;
	CagdCrvFree(Crv);
    }
    CagdFree((VoidPtr) IsoParams);

    return PolyList;
}

/*****************************************************************************
* Routine to approx. a single bspline curve as a polyline with		     *
* 2^SamplesPerCurve samples. Polyline is always E3 CagdPolylineStruct type.  *
* Curve is sampled equally spaced in parametric space, unless the curve is   *
* linear in which the control polygon is simply being copied.		     *
* NULL is returned in case of an error, otherwise CagdPolylineStruct.	     *
*****************************************************************************/
CagdPolylineStruct *BspCrv2Polyline(CagdCrvStruct *Crv, int SamplesPerCurve)
{
    int i, j, n,
	IsNotRational = !CAGD_IS_RATIONAL_CRV(Crv),
	MaxCoord = CAGD_NUM_OF_PT_COORD(Crv -> PType);
    CagdRType *Polyline[CAGD_MAX_PT_SIZE], Scaler;
    CagdPtStruct *NewPolyline;
    CagdPolylineStruct *P;

    if (!CAGD_IS_BSPLINE_CRV(Crv)) return NULL;

    /* Make sure requested format is something reasonable. */
    if (SamplesPerCurve < 1) SamplesPerCurve = 1;
    if (SamplesPerCurve > CAGD_MAX_BEZIER_CACHE_ORDER)
	SamplesPerCurve = CAGD_MAX_BEZIER_CACHE_ORDER;
    if (Crv -> Order == 2 && (1 << SamplesPerCurve) < Crv -> Length) {
    	/* Make sure 2^SamplesPerCurve can hold the entire control polygon. */
    	for (i = 1, SamplesPerCurve = 0;
	     i < Crv -> Length;
	     i <<= 1, SamplesPerCurve++);
    }

    n = 1 << SamplesPerCurve;

    P = CagdPolylineNew(n);
    NewPolyline = P -> Polyline;

    /* Allocate temporary memory to hold evaluated curve. */
    for (i = 0; i < CAGD_MAX_PT_SIZE; i++)
	Polyline[i] = (CagdRType *) CagdMalloc(sizeof(CagdRType) * n);

    if (MaxCoord > 3) MaxCoord = 3;

    n = P -> Length = BspCrvEvalToPolyline(Crv, SamplesPerCurve, Polyline);

    for (i = n - 1; i >= 0; i--) {	          /* Convert to E3 polyline. */
	if (IsNotRational)
	    Scaler = 1.0;
	else
	    Scaler = Polyline[0][i];

	for (j = 0; j < MaxCoord; j++)
	   NewPolyline[i].Pt[j] = Polyline[j+1][i] / Scaler;
	for (j = MaxCoord; j < 3; j++)
	   NewPolyline[i].Pt[j] = 0.0;
    }
    for (i = 0; i < CAGD_MAX_PT_SIZE; i++) CagdFree((VoidPtr) Polyline[i]);

    return P;
}
[ RETURN TO DIRECTORY ]