/****************************************************************************** * CagdSwep.c - Sweep srf operator out of given cross section and an axis. * ******************************************************************************* * Written by Gershon Elber, May. 91. * ******************************************************************************/ #ifdef __MSDOS__ #include #include #include #endif /* __MSDOS__ */ #include "cagd_loc.h" static void TransformCrossSection(CagdRType **SPoints, int Index, CagdCrvStruct *CrossSection, CagdRType *Position, CagdRType Scale, CagdVecStruct *Tangent, CagdVecStruct *Normal); static void GenTransformMatrix(CagdMatStruct *Mat, CagdRType *Trans, CagdVecStruct *Normal, CagdVecStruct *Dir, CagdRType Scale); /****************************************************************************** * Constructs a sweep surface using the following curves: * * 1. CrossSection - defines the basic cross section of the sweep. Must be in * * the XY plane. * * 2. Axis - a 3D curve the CrossSection will be swept along such that the * * Axis normal aligns with the Y axis of the cross section. If Axis is * * linear (i.e. no normal), the normal is picked randomly or to fit the non * * linear part of the Axis (if any). * * 3. Scale - a scaling curve for the sweep, If NULL a scale of Scale is used. * ******************************************************************************/ CagdSrfStruct *CagdSweepSrf(CagdCrvStruct *CrossSection, CagdCrvStruct *Axis, CagdCrvStruct *ScalingCrv, CagdRType Scale) { CagdSrfStruct *Srf; CagdVecStruct Normal, *Vec, TVec; CagdPointType SrfPType = CAGD_PT_E3_TYPE; CagdGeomType SrfGType = CAGD_SBSPLINE_TYPE; int i, j, k, ULength = CrossSection -> Length, VLength = Axis -> Length, UOrder = CrossSection -> Order, VOrder = Axis -> Order; CagdRType **Points, *AxisNodes, *AxisNodePtr, *AxisKV, *AxisWeights = NULL; switch (Axis -> GType) { case CAGD_CBEZIER_TYPE: SrfGType = CAGD_SBEZIER_TYPE; AxisKV = BspKnotUniformOpen(VLength, VOrder, NULL); AxisNodes = AxisNodePtr = BspKnotNodes(AxisKV, VLength + VOrder, VOrder); break; case CAGD_CBSPLINE_TYPE: SrfGType = CAGD_SBSPLINE_TYPE; AxisKV = Axis -> KnotVector; AxisNodePtr = AxisNodes = BspKnotNodes(Axis -> KnotVector, VLength + VOrder, VOrder); break; case CAGD_CPOWER_TYPE: FATAL_ERROR(CAGD_ERR_POWER_NO_SUPPORT); break; default: FATAL_ERROR(CAGD_ERR_UNDEF_CRV); break; } if (CAGD_IS_RATIONAL_CRV(Axis)) AxisWeights = Axis -> Points[W]; switch (CrossSection -> GType) { case CAGD_CBEZIER_TYPE: break; case CAGD_CBSPLINE_TYPE: SrfGType = CAGD_SBSPLINE_TYPE; break; case CAGD_CPOWER_TYPE: FATAL_ERROR(CAGD_ERR_POWER_NO_SUPPORT); break; default: FATAL_ERROR(CAGD_ERR_UNDEF_CRV); break; } if (ScalingCrv) { int ScaleKVLen, AxisKVLen = Axis -> Order + Axis -> Length; switch (ScalingCrv -> GType) { case CAGD_CBEZIER_TYPE: ScalingCrv = CnvrtBezier2BsplineCrv(ScalingCrv); break; case CAGD_CBSPLINE_TYPE: ScalingCrv = CagdCrvCopy(ScalingCrv); break; case CAGD_CPOWER_TYPE: FATAL_ERROR(CAGD_ERR_POWER_NO_SUPPORT); break; default: FATAL_ERROR(CAGD_ERR_UNDEF_CRV); break; } ScaleKVLen = ScalingCrv -> Order + ScalingCrv -> Length; /* Affine trans. ScalingCrv KV to match Axis. */ BspKnotAffineTrans(ScalingCrv -> KnotVector, ScaleKVLen, AxisKV[0] - ScalingCrv -> KnotVector[0], (AxisKV[AxisKVLen - 1] - AxisKV[0]) / (ScalingCrv -> KnotVector[ScaleKVLen - 1] - ScalingCrv -> KnotVector[0])); } if (CAGD_IS_RATIONAL_CRV(Axis) || CAGD_IS_RATIONAL_CRV(CrossSection)) SrfPType = CAGD_PT_P3_TYPE; switch (SrfGType) { case CAGD_SBEZIER_TYPE: Srf = BzrSrfNew(ULength, VLength, SrfPType); break; case CAGD_SBSPLINE_TYPE: Srf = BspSrfNew(ULength, VLength, UOrder, VOrder, SrfPType); if (CrossSection -> GType == CAGD_CBSPLINE_TYPE) GEN_COPY(Srf -> UKnotVector, CrossSection -> KnotVector, sizeof(CagdRType) * (ULength + UOrder)); else BspKnotUniformOpen(ULength, UOrder, Srf -> UKnotVector); if (Axis -> GType == CAGD_CBSPLINE_TYPE) GEN_COPY(Srf -> VKnotVector, Axis -> KnotVector, sizeof(CagdRType) * (VLength + VOrder)); else BspKnotUniformOpen(VLength, VOrder, Srf -> VKnotVector); break; default: FATAL_ERROR(CAGD_ERR_UNDEF_SRF); break; } Points = Srf -> Points; /* Compute the normal to the axis curve and use it to align the +Y axis */ /* of the cross section with that vector. If the Axis curve has no */ /* normal (i.e. it is a linear segment), an arbitrary normal is picked. */ Vec = CagdCrvNormal(Axis, *AxisNodePtr); if (Vec != NULL) { CAGD_COPY_VECTOR(Normal, *Vec); } else { Vec = CagdCrvTangent(Axis, *AxisNodePtr); Normal.Vec[0] = Normal.Vec[1] = Normal.Vec[2] = 0.0; if (ABS(Vec -> Vec[0]) <= ABS(Vec -> Vec[1]) && ABS(Vec -> Vec[0]) <= ABS(Vec -> Vec[2])) Normal.Vec[0] = 1.0; else if (ABS(Vec -> Vec[1]) <= ABS(Vec -> Vec[0]) && ABS(Vec -> Vec[1]) <= ABS(Vec -> Vec[2])) Normal.Vec[1] = 1.0; else Normal.Vec[2] = 1.0; CROSS_PROD(TVec.Vec, Vec -> Vec, Normal.Vec); CAGD_NORMALIZE_VECTOR(TVec); CROSS_PROD(Normal.Vec, TVec.Vec, Vec -> Vec); } /* For each ctl points of the axis, transform the cross section */ /* according to ctl point position, tangent to axis at the point and in */ /* such a way to minimize Normal change. */ for (i = 0; i < VLength; i++) { CagdRType PosE3[3], ScaleE2[2], *Scaling = ScalingCrv ? CagdCrvEval(ScalingCrv, *AxisNodePtr) : NULL; CagdVecStruct *Tangent = CagdCrvTangent(Axis, *AxisNodePtr++); if (Scaling) CagdCoerceToE2(ScaleE2, &Scaling, -1, ScalingCrv -> PType); else ScaleE2[1] = Scale; if (AxisWeights) ScaleE2[1] /= AxisWeights[i]; CagdCoerceToE3(PosE3, Axis -> Points, i, Axis -> PType); TransformCrossSection(Points, i * ULength, CrossSection, PosE3, ScaleE2[1], Tangent, &Normal); } /* Do fixups if axis is a rational curve (note surface is P3). */ if (AxisWeights) { if (CAGD_IS_RATIONAL_CRV(CrossSection)) { /* Need only scale by the Axis curve weights: */ for (j = 0, k = 0; j < VLength; j++) for (i = 0; i < ULength; i++, k++) { Points[X][k] *= AxisWeights[j]; Points[Y][k] *= AxisWeights[j]; Points[Z][k] *= AxisWeights[j]; Points[W][k] *= AxisWeights[j]; } } else { /* Weights do not exists at the moment - need to copy them. */ for (j = 0, k = 0; j < VLength; j++) for (i = 0; i < ULength; i++, k++) { Points[X][k] *= AxisWeights[j]; Points[Y][k] *= AxisWeights[j]; Points[Z][k] *= AxisWeights[j]; Points[W][k] = AxisWeights[j]; } } } if (Axis -> GType == CAGD_CBEZIER_TYPE) CagdFree((VoidPtr) AxisKV); if (ScalingCrv) CagdCrvFree(ScalingCrv); CagdFree((VoidPtr) AxisNodes); return Srf; } /****************************************************************************** * Transform The CrossSection Points, to Position such that Tangent is * * perpendicular to the cross section (which is assumed to be on the XY * * plane). The +Y axis of the cross section is aligned with Normal direction * * to minimize twist along the sweep and been updated to new normal. * * Transformed cross section is place into srf Points, SPoints starting from * * index SIndex. * * All agrument vectors are assumed to be normalized to a unit length. * ******************************************************************************/ static void TransformCrossSection(CagdRType **SPoints, int SIndex, CagdCrvStruct *CrossSection, CagdRType *Position, CagdRType Scale, CagdVecStruct *Tangent, CagdVecStruct *Normal) { CagdPointType PType = CrossSection -> PType; CagdBType IsNotRational = !CAGD_IS_RATIONAL_PT(PType); CagdVecStruct TVec; CagdMatStruct Mat; CagdCrvStruct *CSCopy = CagdCrvCopy(CrossSection); int i, j, MaxCoord = CAGD_NUM_OF_PT_COORD(PType), Len = CSCopy -> Length; CagdRType R, **CSPoints = CSCopy -> Points; /* Fix the Normal to be perpendicular to the Tangent vector is a minimal */ /* rotation. This ensures minimal twist in the resulting surface. */ R = DOT_PROD(Normal -> Vec, Tangent -> Vec); TVec = *Tangent; CAGD_MULT_VECTOR(TVec, R); CAGD_SUB_VECTOR(*Normal, TVec); CAGD_NORMALIZE_VECTOR(*Normal); GenTransformMatrix(&Mat, Position, Normal, Tangent, Scale); CagdCrvMatTransform(CSCopy, &Mat); for (i = 0; i < Len; i++) for (j = IsNotRational; j <= MaxCoord; j++) SPoints[j][SIndex + i] = CSPoints[j][i]; CagdCrvFree(CSCopy); } /****************************************************************************** * Routine to preper transformation martix to do the following (in this * * order): scale by Scale, rotate such that the Z axis is in direction Dir * * and Y is colinear with the Normal and then translate by Trans. * * Algorithm: given the Trans vector, it forms the 4th line of Mat. Dir is * * used to form the second line (the first 3 lines set the rotation), and * * finally Scale is used to scale first 3 lines/columns to the needed scale: * * | Tx Ty Tz 0 | A transformation which takes the coord * * | Bx By Bz 0 | system into T, N & B as required and * * [X Y Z 1] * | Nx Ny Nz 0 | then translate it to C. T, N, B are * * | Cx Cy Cz 1 | scaled by Scale. * * N is exactly Dir (unit vec). T is set to be the Normal and B their cross * * product. * * All agrument vectors are assumed to be normalized to a unit length. * ******************************************************************************/ static void GenTransformMatrix(CagdMatStruct *Mat, CagdRType *Trans, CagdVecStruct *Normal, CagdVecStruct *Dir, CagdRType Scale) { int i; CagdVecStruct B; CROSS_PROD(B.Vec, Dir -> Vec, Normal -> Vec); for (i = 0; i < 3; i++) { Mat -> Mat[0][i] = Normal -> Vec[i] * Scale; Mat -> Mat[1][i] = B.Vec[i] * Scale; Mat -> Mat[2][i] = Dir -> Vec[i] * Scale; Mat -> Mat[3][i] = Trans[i]; Mat -> Mat[i][3] = 0.0; } Mat -> Mat[3][3] = 1.0; }