void PropagatedDeformableModel::computeNewBand(SpinalCord* mesh, CVector3 initialPoint, CVector3 nextPoint, int resolution) { CMatrix4x4 transformationFromOrigin; double angle; CMatrix3x3 trZ; CVector3 point, normale; CVector3 directionCourante = nextPoint-initialPoint, lastNormal = (nextPoint-initialPoint).Normalize(), directionCourantePerpendiculaire; if (lastNormal[2] == 0.0) directionCourantePerpendiculaire = CVector3(0.0,0.0,1.0); else directionCourantePerpendiculaire = CVector3(1.0,2.0,-(lastNormal[0]+2*lastNormal[1])/lastNormal[2]).Normalize(); CVector3 stretchingFactorWorld = image3D_->TransformContinuousIndexToPhysicalPoint(CVector3((1-1.0/stretchingFactor_), 0.0, 0.0))-image3D_->getOrigine(); int offsetTriangles = mesh->getNbrOfPoints()-resolutionRadiale_; for (int len=1; len<=resolution; len++) { CVector3 pointIntermediaire = initialPoint + len*directionCourante/(double)resolutionAxiale_; Referential refCourant = Referential(lastNormal^directionCourantePerpendiculaire, directionCourantePerpendiculaire, lastNormal, pointIntermediaire); transformationFromOrigin = refCourant.getTransformationInverse(); for (int k=0; k<resolutionRadiale_; k++) { angle = 2*M_PI*k/(double)resolutionRadiale_; trZ[0] = cos(angle), trZ[1] = sin(angle), trZ[3] = -sin(angle), trZ[4] = cos(angle); point = transformationFromOrigin*(trZ*CVector3(rayon_,0.0,0.0)); CVector3 vecPoint = initialPoint - point; point[0] += stretchingFactorWorld[0]*vecPoint[0]; point[1] += stretchingFactorWorld[1]*vecPoint[1]; point[2] += stretchingFactorWorld[2]*vecPoint[2]; mesh->addPoint(new Vertex(point,(point-pointIntermediaire).Normalize())); } // Ajout des triangles - attention � la structure en cercle for (int k=0; k<resolutionRadiale_-1; k++) { mesh->addTriangle(offsetTriangles+(len-1)*resolutionRadiale_+k,offsetTriangles+(len-1)*resolutionRadiale_+k+1,offsetTriangles+len*resolutionRadiale_+k); mesh->addTriangle(offsetTriangles+(len-1)*resolutionRadiale_+k+1,offsetTriangles+len*resolutionRadiale_+k+1,offsetTriangles+len*resolutionRadiale_+k); } // Ajout des deux derniers triangles pour fermer le tube mesh->addTriangle(offsetTriangles+(len-1)*resolutionRadiale_+resolutionRadiale_-1,offsetTriangles+(len-1)*resolutionRadiale_,offsetTriangles+len*resolutionRadiale_+resolutionRadiale_-1); mesh->addTriangle(offsetTriangles+(len-1)*resolutionRadiale_,offsetTriangles+len*resolutionRadiale_,offsetTriangles+len*resolutionRadiale_+resolutionRadiale_-1); } }
float PropagatedDeformableModel::computeContrast(Referential& refInitial) { // Calcul du profil de la moelle et du LCR perpendiculairement au tube CVector3 pointS, indexS; vector<float> contrast(resolutionRadiale_); float angle; CMatrix3x3 trZ; CMatrix4x4 transformationFromOrigin = refInitial.getTransformationInverse(); float factor = image3D_->getTypeImageFactor(); for (int k=0; k<resolutionRadiale_; k++) { vector<float> profilIntensite; angle = 2*M_PI*k/(double)resolutionRadiale_; trZ[0] = cos(angle), trZ[1] = sin(angle), trZ[3] = -sin(angle), trZ[4] = cos(angle); for (int l=0; l<2.5*rayon_; l++) { pointS = transformationFromOrigin*(trZ*CVector3(l,0.0,0.0)); if (image3D_->TransformPhysicalPointToIndex(pointS,indexS)) profilIntensite.push_back(factor*image3D_->GetPixelOriginal(indexS)); } float min = 0.0, max = 0.0, maxVal = 0.0, valCourante; unsigned int m = 0; for (unsigned int i=1; i<profilIntensite.size(); i++) { valCourante = profilIntensite[i]-profilIntensite[i-1]; if (maxVal <= valCourante) { maxVal = valCourante; m = i; } } if (profilIntensite.size() > 0) { min = profilIntensite[m]; for (unsigned int j=0; j<m; j++) { valCourante = profilIntensite[j]; if (min > valCourante) min = valCourante; } max = profilIntensite[m]; for (unsigned int j=m+1; j<profilIntensite.size(); j++) { valCourante = profilIntensite[j]; if (max < valCourante) max = valCourante; } } contrast[k] = abs(max-min); } float result = 0.0; for (unsigned int i=0; i<contrast.size(); i++) result += contrast[i]; result /= contrast.size(); return result; }
// read data in packet starting at byte offset and return number of bytes parsed int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { // lazily allocate memory for HeadData in case we're not an Avatar instance if (!_headData) { _headData = new HeadData(this); } // lazily allocate memory for HandData in case we're not an Avatar instance if (!_handData) { _handData = new HandData(this); } const unsigned char* startPosition = reinterpret_cast<const unsigned char*>(buffer.data()); const unsigned char* sourceBuffer = startPosition; quint64 now = usecTimestampNow(); // The absolute minimum size of the update data is as follows: // 50 bytes of "plain old data" { // position = 12 bytes // bodyYaw = 2 (compressed float) // bodyPitch = 2 (compressed float) // bodyRoll = 2 (compressed float) // targetScale = 2 (compressed float) // headPitch = 2 (compressed float) // headYaw = 2 (compressed float) // headRoll = 2 (compressed float) // leanForward = 2 (compressed float) // leanSideways = 2 (compressed float) // torsoTwist = 2 (compressed float) // lookAt = 12 // audioLoudness = 4 // } // + 1 byte for pupilSize // + 1 byte for numJoints (0) // = 51 bytes int minPossibleSize = 51; int maxAvailableSize = buffer.size(); if (minPossibleSize > maxAvailableSize) { if (shouldLogError(now)) { qCDebug(avatars) << "Malformed AvatarData packet at the start; " << " displayName = '" << _displayName << "'" << " minPossibleSize = " << minPossibleSize << " maxAvailableSize = " << maxAvailableSize; } // this packet is malformed so we report all bytes as consumed return maxAvailableSize; } { // Body world position, rotation, and scale // position glm::vec3 position; memcpy(&position, sourceBuffer, sizeof(position)); sourceBuffer += sizeof(position); if (glm::isnan(position.x) || glm::isnan(position.y) || glm::isnan(position.z)) { if (shouldLogError(now)) { qCDebug(avatars) << "Discard nan AvatarData::position; displayName = '" << _displayName << "'"; } return maxAvailableSize; } setPosition(position); // rotation (NOTE: This needs to become a quaternion to save two bytes) float yaw, pitch, roll; sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &yaw); sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &pitch); sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &roll); if (glm::isnan(yaw) || glm::isnan(pitch) || glm::isnan(roll)) { if (shouldLogError(now)) { qCDebug(avatars) << "Discard nan AvatarData::yaw,pitch,roll; displayName = '" << _displayName << "'"; } return maxAvailableSize; } if (_bodyYaw != yaw || _bodyPitch != pitch || _bodyRoll != roll) { _hasNewJointRotations = true; _bodyYaw = yaw; _bodyPitch = pitch; _bodyRoll = roll; } // scale float scale; sourceBuffer += unpackFloatRatioFromTwoByte(sourceBuffer, scale); if (glm::isnan(scale)) { if (shouldLogError(now)) { qCDebug(avatars) << "Discard nan AvatarData::scale; displayName = '" << _displayName << "'"; } return maxAvailableSize; } _targetScale = scale; } // 20 bytes { // Head rotation //(NOTE: This needs to become a quaternion to save two bytes) float headYaw, headPitch, headRoll; sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &headPitch); sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &headYaw); sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &headRoll); if (glm::isnan(headYaw) || glm::isnan(headPitch) || glm::isnan(headRoll)) { if (shouldLogError(now)) { qCDebug(avatars) << "Discard nan AvatarData::headYaw,headPitch,headRoll; displayName = '" << _displayName << "'"; } return maxAvailableSize; } _headData->setBasePitch(headPitch); _headData->setBaseYaw(headYaw); _headData->setBaseRoll(headRoll); } // 6 bytes { // Head lean (relative to pelvis) float leanForward, leanSideways, torsoTwist; sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*)sourceBuffer, &leanForward); sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*)sourceBuffer, &leanSideways); sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*)sourceBuffer, &torsoTwist); if (glm::isnan(leanForward) || glm::isnan(leanSideways)) { if (shouldLogError(now)) { qCDebug(avatars) << "Discard nan AvatarData::leanForward,leanSideways,torsoTwise; displayName = '" << _displayName << "'"; } return maxAvailableSize; } _headData->_leanForward = leanForward; _headData->_leanSideways = leanSideways; _headData->_torsoTwist = torsoTwist; } // 6 bytes { // Lookat Position glm::vec3 lookAt; memcpy(&lookAt, sourceBuffer, sizeof(lookAt)); sourceBuffer += sizeof(lookAt); if (glm::isnan(lookAt.x) || glm::isnan(lookAt.y) || glm::isnan(lookAt.z)) { if (shouldLogError(now)) { qCDebug(avatars) << "Discard nan AvatarData::lookAt; displayName = '" << _displayName << "'"; } return maxAvailableSize; } _headData->_lookAtPosition = lookAt; } // 12 bytes { // AudioLoudness // Instantaneous audio loudness (used to drive facial animation) float audioLoudness; memcpy(&audioLoudness, sourceBuffer, sizeof(float)); sourceBuffer += sizeof(float); if (glm::isnan(audioLoudness)) { if (shouldLogError(now)) { qCDebug(avatars) << "Discard nan AvatarData::audioLoudness; displayName = '" << _displayName << "'"; } return maxAvailableSize; } _headData->_audioLoudness = audioLoudness; } // 4 bytes { // bitFlags and face data unsigned char bitItems = *sourceBuffer++; // key state, stored as a semi-nibble in the bitItems _keyState = (KeyState)getSemiNibbleAt(bitItems,KEY_STATE_START_BIT); // hand state, stored as a semi-nibble plus a bit in the bitItems // we store the hand state as well as other items in a shared bitset. The hand state is an octal, but is split // into two sections to maintain backward compatibility. The bits are ordered as such (0-7 left to right). // +---+-----+-----+--+ // |x,x|H0,H1|x,x,x|H2| // +---+-----+-----+--+ // Hand state - H0,H1,H2 is found in the 3rd, 4th, and 8th bits _handState = getSemiNibbleAt(bitItems, HAND_STATE_START_BIT) + (oneAtBit(bitItems, HAND_STATE_FINGER_POINTING_BIT) ? IS_FINGER_POINTING_FLAG : 0); _headData->_isFaceTrackerConnected = oneAtBit(bitItems, IS_FACESHIFT_CONNECTED); bool hasReferential = oneAtBit(bitItems, HAS_REFERENTIAL); // Referential if (hasReferential) { Referential* ref = new Referential(sourceBuffer, this); if (_referential == NULL || ref->version() != _referential->version()) { changeReferential(ref); } else { delete ref; } _referential->update(); } else if (_referential != NULL) { changeReferential(NULL); } if (_headData->_isFaceTrackerConnected) { float leftEyeBlink, rightEyeBlink, averageLoudness, browAudioLift; minPossibleSize += sizeof(leftEyeBlink) + sizeof(rightEyeBlink) + sizeof(averageLoudness) + sizeof(browAudioLift); minPossibleSize++; // one byte for blendDataSize if (minPossibleSize > maxAvailableSize) { if (shouldLogError(now)) { qCDebug(avatars) << "Malformed AvatarData packet after BitItems;" << " displayName = '" << _displayName << "'" << " minPossibleSize = " << minPossibleSize << " maxAvailableSize = " << maxAvailableSize; } return maxAvailableSize; } // unpack face data memcpy(&leftEyeBlink, sourceBuffer, sizeof(float)); sourceBuffer += sizeof(float); memcpy(&rightEyeBlink, sourceBuffer, sizeof(float)); sourceBuffer += sizeof(float); memcpy(&averageLoudness, sourceBuffer, sizeof(float)); sourceBuffer += sizeof(float); memcpy(&browAudioLift, sourceBuffer, sizeof(float)); sourceBuffer += sizeof(float); if (glm::isnan(leftEyeBlink) || glm::isnan(rightEyeBlink) || glm::isnan(averageLoudness) || glm::isnan(browAudioLift)) { if (shouldLogError(now)) { qCDebug(avatars) << "Discard nan AvatarData::faceData; displayName = '" << _displayName << "'"; } return maxAvailableSize; } _headData->_leftEyeBlink = leftEyeBlink; _headData->_rightEyeBlink = rightEyeBlink; _headData->_averageLoudness = averageLoudness; _headData->_browAudioLift = browAudioLift; int numCoefficients = (int)(*sourceBuffer++); int blendDataSize = numCoefficients * sizeof(float); minPossibleSize += blendDataSize; if (minPossibleSize > maxAvailableSize) { if (shouldLogError(now)) { qCDebug(avatars) << "Malformed AvatarData packet after Blendshapes;" << " displayName = '" << _displayName << "'" << " minPossibleSize = " << minPossibleSize << " maxAvailableSize = " << maxAvailableSize; } return maxAvailableSize; } _headData->_blendshapeCoefficients.resize(numCoefficients); memcpy(_headData->_blendshapeCoefficients.data(), sourceBuffer, blendDataSize); sourceBuffer += numCoefficients * sizeof(float); //bitItemsDataSize = 4 * sizeof(float) + 1 + blendDataSize; } } // 1 + bitItemsDataSize bytes { // pupil dilation sourceBuffer += unpackFloatFromByte(sourceBuffer, _headData->_pupilDilation, 1.0f); } // 1 byte // joint data int numJoints = *sourceBuffer++; int bytesOfValidity = (int)ceil((float)numJoints / (float)BITS_IN_BYTE); minPossibleSize += bytesOfValidity; if (minPossibleSize > maxAvailableSize) { if (shouldLogError(now)) { qCDebug(avatars) << "Malformed AvatarData packet after JointValidityBits;" << " displayName = '" << _displayName << "'" << " minPossibleSize = " << minPossibleSize << " maxAvailableSize = " << maxAvailableSize; } return maxAvailableSize; } int numValidJoints = 0; _jointData.resize(numJoints); { // validity bits unsigned char validity = 0; int validityBit = 0; for (int i = 0; i < numJoints; i++) { if (validityBit == 0) { validity = *sourceBuffer++; } bool valid = (bool)(validity & (1 << validityBit)); if (valid) { ++numValidJoints; } _jointData[i].valid = valid; validityBit = (validityBit + 1) % BITS_IN_BYTE; } } // 1 + bytesOfValidity bytes // each joint rotation component is stored in two bytes (sizeof(uint16_t)) int COMPONENTS_PER_QUATERNION = 4; minPossibleSize += numValidJoints * COMPONENTS_PER_QUATERNION * sizeof(uint16_t); if (minPossibleSize > maxAvailableSize) { if (shouldLogError(now)) { qCDebug(avatars) << "Malformed AvatarData packet after JointData;" << " displayName = '" << _displayName << "'" << " minPossibleSize = " << minPossibleSize << " maxAvailableSize = " << maxAvailableSize; } return maxAvailableSize; } { // joint data for (int i = 0; i < numJoints; i++) { JointData& data = _jointData[i]; if (data.valid) { _hasNewJointRotations = true; sourceBuffer += unpackOrientationQuatFromBytes(sourceBuffer, data.rotation); } } } // numJoints * 8 bytes int numBytesRead = sourceBuffer - startPosition; _averageBytesReceived.updateAverage(numBytesRead); return numBytesRead; }
// read data in packet starting at byte offset and return number of bytes parsed int AvatarData::parseDataAtOffset(const QByteArray& packet, int offset) { // reset the last heard timer since we have new data for this AvatarData _lastUpdateTimer.restart(); // lazily allocate memory for HeadData in case we're not an Avatar instance if (!_headData) { _headData = new HeadData(this); } // lazily allocate memory for HandData in case we're not an Avatar instance if (!_handData) { _handData = new HandData(this); } const unsigned char* startPosition = reinterpret_cast<const unsigned char*>(packet.data()) + offset; const unsigned char* sourceBuffer = startPosition; quint64 now = usecTimestampNow(); // The absolute minimum size of the update data is as follows: // 50 bytes of "plain old data" { // position = 12 bytes // bodyYaw = 2 (compressed float) // bodyPitch = 2 (compressed float) // bodyRoll = 2 (compressed float) // targetScale = 2 (compressed float) // headYaw = 2 (compressed float) // headPitch = 2 (compressed float) // headRoll = 2 (compressed float) // leanSideways = 4 // leanForward = 4 // lookAt = 12 // audioLoudness = 4 // } // + 1 byte for messageSize (0) // + 1 byte for pupilSize // + 1 byte for numJoints (0) // = 53 bytes int minPossibleSize = 53; int maxAvailableSize = packet.size() - offset; if (minPossibleSize > maxAvailableSize) { if (shouldLogError(now)) { qDebug() << "Malformed AvatarData packet at the start; " << " displayName = '" << _displayName << "'" << " minPossibleSize = " << minPossibleSize << " maxAvailableSize = " << maxAvailableSize; } // this packet is malformed so we report all bytes as consumed return maxAvailableSize; } { // Body world position, rotation, and scale // position glm::vec3 position; memcpy(&position, sourceBuffer, sizeof(position)); sourceBuffer += sizeof(position); if (glm::isnan(position.x) || glm::isnan(position.y) || glm::isnan(position.z)) { if (shouldLogError(now)) { qDebug() << "Discard nan AvatarData::position; displayName = '" << _displayName << "'"; } return maxAvailableSize; } setPosition(position); // rotation (NOTE: This needs to become a quaternion to save two bytes) float yaw, pitch, roll; sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &yaw); sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &pitch); sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &roll); if (glm::isnan(yaw) || glm::isnan(pitch) || glm::isnan(roll)) { if (shouldLogError(now)) { qDebug() << "Discard nan AvatarData::yaw,pitch,roll; displayName = '" << _displayName << "'"; } return maxAvailableSize; } _bodyYaw = yaw; _bodyPitch = pitch; _bodyRoll = roll; // scale float scale; sourceBuffer += unpackFloatRatioFromTwoByte(sourceBuffer, scale); if (glm::isnan(scale)) { if (shouldLogError(now)) { qDebug() << "Discard nan AvatarData::scale; displayName = '" << _displayName << "'"; } return maxAvailableSize; } _targetScale = scale; } // 20 bytes { // Head rotation //(NOTE: This needs to become a quaternion to save two bytes) float headYaw, headPitch, headRoll; sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &headYaw); sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &headPitch); sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &headRoll); if (glm::isnan(headYaw) || glm::isnan(headPitch) || glm::isnan(headRoll)) { if (shouldLogError(now)) { qDebug() << "Discard nan AvatarData::headYaw,headPitch,headRoll; displayName = '" << _displayName << "'"; } return maxAvailableSize; } _headData->setBaseYaw(headYaw); _headData->setBasePitch(headPitch); _headData->setBaseRoll(headRoll); } // 6 bytes // Head lean (relative to pelvis) { float leanSideways, leanForward; memcpy(&leanSideways, sourceBuffer, sizeof(float)); sourceBuffer += sizeof(float); memcpy(&leanForward, sourceBuffer, sizeof(float)); sourceBuffer += sizeof(float); if (glm::isnan(leanSideways) || glm::isnan(leanForward)) { if (shouldLogError(now)) { qDebug() << "Discard nan AvatarData::leanSideways,leanForward; displayName = '" << _displayName << "'"; } return maxAvailableSize; } _headData->_leanSideways = leanSideways; _headData->_leanForward = leanForward; } // 8 bytes { // Lookat Position glm::vec3 lookAt; memcpy(&lookAt, sourceBuffer, sizeof(lookAt)); sourceBuffer += sizeof(lookAt); if (glm::isnan(lookAt.x) || glm::isnan(lookAt.y) || glm::isnan(lookAt.z)) { if (shouldLogError(now)) { qDebug() << "Discard nan AvatarData::lookAt; displayName = '" << _displayName << "'"; } return maxAvailableSize; } _headData->_lookAtPosition = lookAt; } // 12 bytes { // AudioLoudness // Instantaneous audio loudness (used to drive facial animation) float audioLoudness; memcpy(&audioLoudness, sourceBuffer, sizeof(float)); sourceBuffer += sizeof(float); if (glm::isnan(audioLoudness)) { if (shouldLogError(now)) { qDebug() << "Discard nan AvatarData::audioLoudness; displayName = '" << _displayName << "'"; } return maxAvailableSize; } _headData->_audioLoudness = audioLoudness; } // 4 bytes // chat int chatMessageSize = *sourceBuffer++; minPossibleSize += chatMessageSize; if (minPossibleSize > maxAvailableSize) { if (shouldLogError(now)) { qDebug() << "Malformed AvatarData packet before ChatMessage;" << " displayName = '" << _displayName << "'" << " minPossibleSize = " << minPossibleSize << " maxAvailableSize = " << maxAvailableSize; } return maxAvailableSize; } { // chat payload _chatMessage = string((char*)sourceBuffer, chatMessageSize); sourceBuffer += chatMessageSize * sizeof(char); } // 1 + chatMessageSize bytes { // bitFlags and face data unsigned char bitItems = *sourceBuffer++; // key state, stored as a semi-nibble in the bitItems _keyState = (KeyState)getSemiNibbleAt(bitItems,KEY_STATE_START_BIT); // hand state, stored as a semi-nibble in the bitItems _handState = getSemiNibbleAt(bitItems,HAND_STATE_START_BIT); _headData->_isFaceshiftConnected = oneAtBit(bitItems, IS_FACESHIFT_CONNECTED); _isChatCirclingEnabled = oneAtBit(bitItems, IS_CHAT_CIRCLING_ENABLED); bool hasReferential = oneAtBit(bitItems, HAS_REFERENTIAL); // Referential if (hasReferential) { Referential* ref = new Referential(sourceBuffer, this); if (_referential == NULL || ref->version() != _referential->version()) { changeReferential(ref); } else { delete ref; } _referential->update(); } else if (_referential != NULL) { changeReferential(NULL); } if (_headData->_isFaceshiftConnected) { float leftEyeBlink, rightEyeBlink, averageLoudness, browAudioLift; minPossibleSize += sizeof(leftEyeBlink) + sizeof(rightEyeBlink) + sizeof(averageLoudness) + sizeof(browAudioLift); minPossibleSize++; // one byte for blendDataSize if (minPossibleSize > maxAvailableSize) { if (shouldLogError(now)) { qDebug() << "Malformed AvatarData packet after BitItems;" << " displayName = '" << _displayName << "'" << " minPossibleSize = " << minPossibleSize << " maxAvailableSize = " << maxAvailableSize; } return maxAvailableSize; } // unpack face data memcpy(&leftEyeBlink, sourceBuffer, sizeof(float)); sourceBuffer += sizeof(float); memcpy(&rightEyeBlink, sourceBuffer, sizeof(float)); sourceBuffer += sizeof(float); memcpy(&averageLoudness, sourceBuffer, sizeof(float)); sourceBuffer += sizeof(float); memcpy(&browAudioLift, sourceBuffer, sizeof(float)); sourceBuffer += sizeof(float); if (glm::isnan(leftEyeBlink) || glm::isnan(rightEyeBlink) || glm::isnan(averageLoudness) || glm::isnan(browAudioLift)) { if (shouldLogError(now)) { qDebug() << "Discard nan AvatarData::faceData; displayName = '" << _displayName << "'"; } return maxAvailableSize; } _headData->_leftEyeBlink = leftEyeBlink; _headData->_rightEyeBlink = rightEyeBlink; _headData->_averageLoudness = averageLoudness; _headData->_browAudioLift = browAudioLift; int numCoefficients = (int)(*sourceBuffer++); int blendDataSize = numCoefficients * sizeof(float); minPossibleSize += blendDataSize; if (minPossibleSize > maxAvailableSize) { if (shouldLogError(now)) { qDebug() << "Malformed AvatarData packet after Blendshapes;" << " displayName = '" << _displayName << "'" << " minPossibleSize = " << minPossibleSize << " maxAvailableSize = " << maxAvailableSize; } return maxAvailableSize; } _headData->_blendshapeCoefficients.resize(numCoefficients); memcpy(_headData->_blendshapeCoefficients.data(), sourceBuffer, blendDataSize); sourceBuffer += numCoefficients * sizeof(float); //bitItemsDataSize = 4 * sizeof(float) + 1 + blendDataSize; } } // 1 + bitItemsDataSize bytes { // pupil dilation sourceBuffer += unpackFloatFromByte(sourceBuffer, _headData->_pupilDilation, 1.0f); } // 1 byte // joint data int numJoints = *sourceBuffer++; int bytesOfValidity = (int)ceil((float)numJoints / (float)BITS_IN_BYTE); minPossibleSize += bytesOfValidity; if (minPossibleSize > maxAvailableSize) { if (shouldLogError(now)) { qDebug() << "Malformed AvatarData packet after JointValidityBits;" << " displayName = '" << _displayName << "'" << " minPossibleSize = " << minPossibleSize << " maxAvailableSize = " << maxAvailableSize; } return maxAvailableSize; } int numValidJoints = 0; _jointData.resize(numJoints); { // validity bits unsigned char validity = 0; int validityBit = 0; for (int i = 0; i < numJoints; i++) { if (validityBit == 0) { validity = *sourceBuffer++; } bool valid = (bool)(validity & (1 << validityBit)); if (valid) { ++numValidJoints; } _jointData[i].valid = valid; validityBit = (validityBit + 1) % BITS_IN_BYTE; } } // 1 + bytesOfValidity bytes // each joint rotation component is stored in two bytes (sizeof(uint16_t)) int COMPONENTS_PER_QUATERNION = 4; minPossibleSize += numValidJoints * COMPONENTS_PER_QUATERNION * sizeof(uint16_t); if (minPossibleSize > maxAvailableSize) { if (shouldLogError(now)) { qDebug() << "Malformed AvatarData packet after JointData;" << " displayName = '" << _displayName << "'" << " minPossibleSize = " << minPossibleSize << " maxAvailableSize = " << maxAvailableSize; } return maxAvailableSize; } { // joint data for (int i = 0; i < numJoints; i++) { JointData& data = _jointData[i]; if (data.valid) { _hasNewJointRotations = true; sourceBuffer += unpackOrientationQuatFromBytes(sourceBuffer, data.rotation); } } } // numJoints * 8 bytes return sourceBuffer - startPosition; }
SpinalCord* PropagatedDeformableModel::propagationMesh(int numberOfMesh) { double const_contrast = 200.0; if (image3D_->getTypeImageFactor() == 1.0) const_contrast = 445.0; // if T2 /****************************************************************************************** * Initialization of the spinal cord mesh * Depending of the choice of propagation, the contrast vector is reversed to take the correct values *****************************************************************************************/ SpinalCord* initialMesh = new SpinalCord(); if (numberOfMesh == 1) initialMesh = initialTube1; else if (numberOfMesh == 2) { reverse(contrast.begin(),contrast.end()); initialMesh = initialTube2; } vector< vector<CVector3> > lastDisks; /****************************************************************************************** * Deformation of the initial mesh. * This deformation must be accurate to have a correct initialization. If not, errors can be propagated. * The number of iteration and the stop condition of the deformation is 0.05 mm by default *****************************************************************************************/ if (verbose_) cout << endl << endl << "Initial deformation : " << initialMesh->getNbrOfPoints() << " points and " << initialMesh->getNbrOfTriangles() << " triangles" << endl; DeformableModelBasicAdaptator *deformableAdaptator = new DeformableModelBasicAdaptator(image3D_,initialMesh,numberOfDeformIteration_,const_contrast,false); deformableAdaptator->setVerbose(verbose_); deformableAdaptator->setNumberOfIteration(8); //8 deformableAdaptator->setStopCondition(0.05); if (tradeoff_d_bool) deformableAdaptator->setTradeOff(tradeoff_d_); //deformableAdaptator->setProgressiveLineSearchLength(true);// tested but not optimal deformableAdaptator->addCorrectionPoints(points_mask_correction_); deformableAdaptator->adaptation(); // launch the deformation meshOutput = deformableAdaptator->getSpinalCordOutput(); // get the spinal cord segmentation mesh delete deformableAdaptator; // release memory meshOutput->setRadialResolution(resolutionRadiale_); // the output of DeformableModelBasicAdaptator is a mesh and we need to provide the radial resolution for further computation // we remove the last disk to prevent edges issues in the deformation process. Indeed, edges have less neighbors and the last disk retract itself. meshOutput->removeLastPoints(resolutionRadiale_); meshOutput->removeLastTriangles(2*resolutionRadiale_); /****************************************************************************************** * Initialization of variables * numberOfDisks is a constant of propagation - don't change it * uniqueMesh is the part of the mesh that will be deformed at each iteration * newStartPoint is the center of the last disk of the mesh. It is the new start point of the propagation. The mesh is duplicated and translated on this point. *****************************************************************************************/ unsigned int numberOfDisks = resolutionAxiale_+1; CVector3 nextPoint, newStartPoint, lastPoint, firstPoint = meshOutput->computeGravityCenterFirstDisk(numberOfDisks); // computation of first point = center of mass of the fisrt disk centerline.push_back(firstPoint); // add point to centerline - first point necessary SpinalCord *uniqueMesh = meshOutput->extractPartOfMesh(numberOfDisks,true,true); // extraction of a part of the mesh uniqueMesh->computeConnectivity(); uniqueMesh->computeTrianglesBarycentre(); newStartPoint = meshOutput->computeGravityCenterFirstDisk(numberOfDisks); // compute first point of the mesh /****************************************************************************************** * Computation of the initial rotation value. * It is used as a mesh refreshing condition. If the difference between initial and updated rotation value if too high, a new part of mesh is used as the template to be duplicated. Rotation value is the sum of intensity at vertices positions. *****************************************************************************************/ GlobalAdaptation* gAdaptI = new GlobalAdaptation(image3D_,uniqueMesh,newStartPoint,"rotation"); CVector3 normal_mesh = CVector3(initialNormal2_[0],initialNormal2_[1],initialNormal2_[2]); gAdaptI->setNormalMesh(normal_mesh); gAdaptI->setVerbose(verbose_); double initialRotationValue = gAdaptI->getInitialValue(), rotationValue = 0.0; delete gAdaptI; CVector3 position; /****************************************************************************************** * initialization of stop condition variables *****************************************************************************************/ bool done = false; area[0] = 0.0; area[1] = 0.0; area[2] = 0.0; double meanVoxels[3]; meanVoxels[0] = 0.0; meanVoxels[1] = 0.0; meanVoxels[2] = 0.0; //double meanVoxelsInit = meshOutput->computeStandardDeviationFromPixelsInside(image3D_->getImageOriginale()); // homogeneity stop condition - not optimal int numberOfBadOrientation = 0, numberOfBadOrientationTotal = 0, maxBadOrientation = 150; meanContrast = 0.0; /****************************************************************************************** * Iterative deformation by adding a portion of mesh at each iteration *****************************************************************************************/ int i; for (i=1; i<=numberOfPropagationIteration_ && !done; i++) { if (verbose_) cout << endl << "Propagation step " << i << "/" << numberOfPropagationIteration_ << endl; centerline = meshOutput->computeCenterline(); double segmentationLength = 0.0; for (unsigned int c=1; c<centerline.size(); c++) segmentationLength += (centerline[c]-centerline[c-1]).Norm(); if (verbose_) cout << "Propagation length [mm] : " << segmentationLength << " / " << propagationLength_ << endl; /****************************************************************************************** * Referential computation on the last disk of the mesh * Computation of the local spinal cord / CSF contrast along the mesh * Computation of the mean contrast from last disk positions. The mean contrast is used as a parameter in the local deformation and as a stop condition *****************************************************************************************/ CVector3 sourcePoint1 = centerline[centerline.size()-1], sourcePoint2 = centerline[centerline.size()-5]; CVector3 lastNormal = (sourcePoint1-sourcePoint2).Normalize(), directionCourantePerpendiculaire; if (lastNormal[2] == 0.0) directionCourantePerpendiculaire = CVector3(0.0,0.0,1.0); else directionCourantePerpendiculaire = CVector3(1.0,2.0,-(lastNormal[0]+2*lastNormal[1])/lastNormal[2]).Normalize(); Referential refCourant = Referential(lastNormal^directionCourantePerpendiculaire, directionCourantePerpendiculaire, lastNormal, sourcePoint2); contrast.push_back(pair<CVector3,double>(refCourant.getOrigine(),computeContrast(refCourant))); int nbContrast = contrast.size()-1; if (verbose_) cout << "Contrast = " << contrast[nbContrast].second << endl; if (nbContrast == 0) meanContrast = (contrast[0].second+2*const_contrast)/3.0; else if (nbContrast == 1) meanContrast = (contrast[nbContrast].second+contrast[nbContrast-1].second+const_contrast)/3.0; else meanContrast = (contrast[nbContrast].second+contrast[nbContrast-1].second+contrast[nbContrast-2].second)/3.0; // mean 445.8476 std 113.5695 max 708.9200 min 148.6900 if (verbose_) cout << "Iteration Position = " << sourcePoint1 << endl; /****************************************************************************************** * newStartPoint is the last point of the mesh and will be the first point of the new section (for duplication and translation) *****************************************************************************************/ newStartPoint = meshOutput->computeGravityCenterLastDisk(numberOfDisks); CVector3 indexPosition, temp; bool inOut = image3D_->TransformPhysicalPointToIndex(newStartPoint,indexPosition); // stop condition CVector3 lastPointReal = lastPoint; if (lastPointReal == CVector3::ZERO) lastPointReal = newStartPoint; /****************************************************************************************** * stop conditions: * length of propagation * mean local contrast between CSF and spinal cord * abnormalities - not good if the new starting point is behind the last starting point * inferior and superior limits can be imposed by the user *****************************************************************************************/ if (segmentationLength < propagationLength_ && meanContrast > minContrast && abs(newStartPoint[1]-lastPointReal[1]) <= 15.0 && indexPosition[1]<upLimit && indexPosition[1]>downLimit) { lastPoint = meshOutput->computeGravityCenterFirstDisk(numberOfDisks); if (position == CVector3()) position = lastPoint; SpinalCord *partMesh = meshOutput->extractLastDiskOfMesh(false); CMatrix4x4 translation, transformation; translation[12] = newStartPoint[0]-position[0]; translation[13] = newStartPoint[1]-position[1]; translation[14] = newStartPoint[2]-position[2]; position = newStartPoint; uniqueMesh->transform(translation); bool orientationBool = true; /****************************************************************************************** * GlobalAdaptation compute the rigid transformation of the mesh to the image using the gradient magnitude * It can provide a value of the gradient at the surface vertice positions for a quality control with the function getInitialValue() * This value is used to change the mesh when it doesn't correspond anymore to the spinal cord edges - this value is negative * The function adaptation() compute the orientation and transform the mesh *****************************************************************************************/ GlobalAdaptation* gAdapt = new GlobalAdaptation(image3D_,uniqueMesh,newStartPoint,"rotation"); normal_mesh = CVector3(translation[12],translation[13],translation[14]); gAdapt->setNormalMesh(normal_mesh); gAdapt->setVerbose(verbose_); rotationValue = gAdapt->getInitialValue(); if (rotationValue >= 0.75*initialRotationValue || rotationValue <= 1.5*initialRotationValue) { // if the value of GlobalAdaptation isn't in range, we replace the mesh // release the memory and create a new mesh using the last disks delete uniqueMesh; uniqueMesh = meshOutput->extractPartOfMesh(numberOfDisks,true,true); uniqueMesh->computeConnectivity(); uniqueMesh->computeTrianglesBarycentre(); lastPoint = meshOutput->computeGravityCenterFirstDisk(numberOfDisks); CMatrix4x4 translation, transformation; translation[12] = newStartPoint[0]-lastPoint[0]; translation[13] = newStartPoint[1]-lastPoint[1]; translation[14] = newStartPoint[2]-lastPoint[2]; position = newStartPoint; uniqueMesh->transform(translation); // translate the mesh to its new position gAdapt = new GlobalAdaptation(image3D_,uniqueMesh,newStartPoint,"rotation"); normal_mesh = CVector3(translation[12],translation[13],translation[14]); gAdapt->setNormalMesh(normal_mesh); gAdapt->setVerbose(verbose_); initialRotationValue = gAdapt->getInitialValue(); rotationValue = gAdapt->getInitialValue(); } /****************************************************************************************** * if the centerline of the spinal cord is provided by the user, it is what it's used to compute the rotation at each propagation iteration * TODO: spline interpolation *****************************************************************************************/ if (propCenterline_) { // Computation of the rotation based on the centerline // Find the point in centerline that is the nearest point to our new starting point. double nearest_point_value = centerline_approximator.getNearestPoint(newStartPoint, range); // Evaluate the derivative of the centerline at this location CVector3 normal = centerline_approximator.EvaluateGradient(nearest_point_value).Normalize(); // Compute normal of our mesh at the starting position if (numberOfMesh == 1) normal = -normal; // the normal is inverted for the first mesh CVector3 lastNormalMesh = (newStartPoint-lastPoint).Normalize(); // Compute rotation between the two normals. We need to compute all the necessary axis for establishing referentials. CVector3 directionCourantePerpendiculaireMesh, directionCourantePerpendiculaireCenterline; if (lastNormalMesh[2] == 0.0) directionCourantePerpendiculaireMesh = CVector3(0.0,0.0,1.0); else directionCourantePerpendiculaireMesh = CVector3(1.0,2.0,-(lastNormalMesh[0]+2*lastNormalMesh[1])/lastNormalMesh[2]).Normalize(); Referential refMesh = Referential(lastNormalMesh^directionCourantePerpendiculaireMesh, directionCourantePerpendiculaireMesh, lastNormalMesh, newStartPoint); if (normal[2] == 0.0) directionCourantePerpendiculaireCenterline = CVector3(0.0,0.0,1.0); else directionCourantePerpendiculaireCenterline = CVector3(1.0,2.0,-(normal[0]+2*normal[1])/normal[2]).Normalize(); Referential refCenterline = Referential(normal^directionCourantePerpendiculaireCenterline, directionCourantePerpendiculaireCenterline, normal, newStartPoint); CMatrix4x4 transformationRotation = refMesh.getTransformation(refCenterline); // As the referential origin is the starting point of our mesh, the transformation that is computed is only a rotation and does not contain any translation. uniqueMesh->transform(transformationRotation,newStartPoint); } /****************************************************************************************** * if the centerline is not provided, the rotation computation used GlobalAdaptation *****************************************************************************************/ else { gAdapt->adaptation(true); // bad orientation (out of known orientation range) can happened if (gAdapt->getBadOrientation()) { numberOfBadOrientation++; numberOfBadOrientationTotal++; } else numberOfBadOrientation = 0; } if (orientationBool) // The program can stop if too much bad orientation - not used for now { /****************************************************************************************** * Creation of a new mesh with the last disk of meshOutput and the new mesh (uniqueMesh) correctly oriented *****************************************************************************************/ partMesh->assembleMeshes(uniqueMesh,numberOfDisks,resolutionRadiale_); partMesh->computeConnectivity(); partMesh->computeTrianglesBarycentre(); if (verbose_) cout << "Mesh deformation : " << partMesh->getNbrOfPoints() << " points and " << partMesh->getNbrOfTriangles() << " triangles" << endl; /****************************************************************************************** * Creation of the deformation object, including the image, the mesh and the deformation parameters *****************************************************************************************/ deformableAdaptator = new DeformableModelBasicAdaptator(image3D_,partMesh,numberOfDeformIteration_,meanContrast,false); if (tradeoff_d_bool) deformableAdaptator->setTradeOff(tradeoff_d_); deformableAdaptator->setVerbose(verbose_); if (this->changedParameters_) { deformableAdaptator->changedParameters(); deformableAdaptator->setAlpha(alpha); deformableAdaptator->setBeta(beta); deformableAdaptator->setLineSearch(line_search); } deformableAdaptator->addCorrectionPoints(points_mask_correction_); /****************************************************************************************** * Deformation of the mesh *****************************************************************************************/ double deformation = deformableAdaptator->adaptation(); /****************************************************************************************** * Extraction of the deformation result and verification of stop conditions: * mesh consistency * maximum deformation * maximum cross-sectional area * number of wrong orientation (sign of a wrong orientation) *****************************************************************************************/ SpinalCord* deformed_spinalcord = deformableAdaptator->getSpinalCordOutput(); deformed_spinalcord->computeConnectivity(); deformed_spinalcord->computeTrianglesBarycentre(); deformed_spinalcord->Initialize(resolutionRadiale_); CVector3 secondPoint = deformed_spinalcord->computeGravityCenterSecondDisk(); double lastCrossSectionalArea = deformed_spinalcord->computeLastCrossSectionalArea(); area[0] = area[1]; area[1] = area[2]; area[2] = lastCrossSectionalArea; meanArea = (area[0]+area[1]+area[2])/3.0; if ((secondPoint-lastPoint)*(newStartPoint-lastPoint)<0.0) { if (verbose_) cout << "Stop by deformation error : overlap during propagation" << endl; done = true; } if (deformation >= maxDeformation) { if (verbose_) cout << "Stop by too large deformation : " << deformation << "/" << maxDeformation << endl; done = true; } if ((meanArea >= maxArea && abs(area[2]-area[1]) >= maxArea/7.0) || meanArea >= 1.2*maxArea) {// || (area[1]>=maxArea && area[2]<maxArea)) { if (verbose_) cout << "Stop by too large cross sectionnal area : " << meanArea << "/" << maxArea << endl; done = true; } if (numberOfBadOrientation >= maxBadOrientation) { if (verbose_) cout << "Stop by too much bad orientation during propagation : " << numberOfBadOrientation << "/" << maxBadOrientation << endl; done = true; } /****************************************************************************************** * Assembling meshOutput (whole spinal cord segmentation) and the new part of the deformed mesh *****************************************************************************************/ if (!done) { meshOutput->assembleMeshes(deformed_spinalcord,numberOfDisks,resolutionRadiale_); meshOutput->computeConnectivity(); meshOutput->computeTrianglesBarycentre(); // if the mesh get out the image, the propagation has to stop if(!inOut || indexPosition[1]>upLimit || indexPosition[1]<downLimit) { done = true; if (!inOut && verbose_) cout << "Stop because out of image" << endl; if (indexPosition[1]>upLimit && verbose_) cout << "Stop because out of range: up" << endl; if (indexPosition[1]<downLimit && verbose_) cout << "Stop because out of range: down" << endl; } } delete deformed_spinalcord; } else { if (verbose_) cout << "Stop by bad orientation" << endl; done = true; } delete partMesh, gAdapt, deformableAdaptator;//, orientationFilter; } else { done = true; if (!inOut && verbose_) cout << "Stop because out of image" << endl; if (indexPosition[1]>=upLimit && verbose_) cout << "Stop because out of range: up" << endl; if (indexPosition[1]<=downLimit && verbose_) cout << "Stop because out of range: down" << endl; if (abs(newStartPoint[1]-lastPointReal[1]) > 15.0 && verbose_) cout << "Stop because bad direction" << endl; } if (verbose_) cout << "Number of bad orientation = " << numberOfBadOrientationTotal << " / " << i << endl; if (verbose_) cout << "Contrast : " << meanContrast << " / " << minContrast << endl; /****************************************************************************************** * Computation of the new spinal cord centerline *****************************************************************************************/ centerline = meshOutput->computeCenterline(); if (verbose_) { double distanceSegmentation = 0.0; int interval = 1; for (unsigned int c=interval; c<centerline.size(); c+=interval) distanceSegmentation += (centerline[c]-centerline[c-interval]).Norm(); cout << "Distance of segmentation [mm] = " << distanceSegmentation << endl; } } /****************************************************************************************** * Smoothing of the low-resolution mesh *****************************************************************************************/ meshOutput->smoothing(70); return meshOutput; }
void PropagatedDeformableModel::computeMeshInitial() { // centerline can be added to be followed. Points of centerline have to be added from bottom to top if (propCenterline_) { if (centerline.size() < 1) { cerr << "Error: Not enought points in centerline" << endl; return; } else { //int init = centerline.size()*init_position_; /****************************************************************************************** * If a centerline is used for the orientation computation, we compute its BSpline approximation. This approximation is used for centerline position and orientation extraction * The parameter range is the centerline approximation accuracy *****************************************************************************************/ initial_centerline = centerline; centerline_approximator = BSplineApproximation(&initial_centerline); initialPoint_ = centerline_approximator.EvaluateBSpline(init_position_); initialNormal1_ = -centerline_approximator.EvaluateGradient(init_position_-0.01).Normalize(); initialNormal2_ = centerline_approximator.EvaluateGradient(init_position_+0.01).Normalize();; //initialNormal1_ = (centerline[init-1]-centerline[init]).Normalize(); //initialNormal2_ = (centerline[init+1]-centerline[init]).Normalize(); hasInitialPointAndNormals_ = true; } } if (centerline.size() < 1 && !hasInitialPointAndNormals_) { cerr << "Error: Not enought points in centerline" << endl; } else if (centerline.size() == 2) { initialTube1 = new SpinalCord; CVector3 directionInitiale = (centerline[1]-centerline[0]).Normalize(), directionInitialePerpendiculaire; if (directionInitiale[2] == 0.0) directionInitialePerpendiculaire = CVector3(0.0,0.0,1.0); else directionInitialePerpendiculaire = CVector3(1.0,2.0,-(directionInitiale[0]+2*directionInitiale[1])/directionInitiale[2]).Normalize(); Referential refInitial = Referential(directionInitiale^directionInitialePerpendiculaire, directionInitialePerpendiculaire, directionInitiale, centerline[0]); CMatrix4x4 transformationFromOrigin = refInitial.getTransformationInverse(); double angle; CMatrix3x3 trZ; CVector3 point, normale; // Compute initial disk for (int k=0; k<resolutionRadiale_; k++) { angle = 2*M_PI*k/(double)resolutionRadiale_; trZ[0] = cos(angle), trZ[1] = sin(angle), trZ[3] = -sin(angle), trZ[4] = cos(angle); point = transformationFromOrigin*(trZ*CVector3(rayon_,0.0,0.0)); initialTube1->addPoint(new Vertex(point,(point-centerline[0]).Normalize())); } CVector3 nextPoint = centerline[0] + deplacementAxial_*directionInitiale; computeNewBand(initialTube1,centerline[0],nextPoint,resolutionAxiale_+2); initialTube1->computeConnectivity(); initialTube1->computeTrianglesBarycentre(); isMeshInitialized = true; meanContrast = computeContrast(refInitial); //cout << "Contrast = " << contrast << endl; } else if (hasInitialPointAndNormals_) { initialTube1 = new SpinalCord; CVector3 directionInitiale = initialNormal1_, directionInitialePerpendiculaire; if (directionInitiale[2] == 0.0) directionInitialePerpendiculaire = CVector3(0.0,0.0,1.0); else directionInitialePerpendiculaire = CVector3(1.0,2.0,-(directionInitiale[0]+2*directionInitiale[1])/directionInitiale[2]).Normalize(); Referential refInitial = Referential(directionInitiale^directionInitialePerpendiculaire, directionInitialePerpendiculaire, directionInitiale, initialPoint_); CMatrix4x4 transformationFromOrigin = refInitial.getTransformationInverse(); double angle; CMatrix3x3 trZ; CVector3 point, normale; CVector3 stretchingFactorWorld = image3D_->TransformContinuousIndexToPhysicalPoint(CVector3((1.0-1.0/stretchingFactor_), 0.0, 0.0))-image3D_->getOrigine(); // Compute initial disk for (int k=0; k<resolutionRadiale_; k++) { angle = 2*M_PI*k/(double)resolutionRadiale_; trZ[0] = cos(angle), trZ[1] = sin(angle), trZ[3] = -sin(angle), trZ[4] = cos(angle); point = transformationFromOrigin*(trZ*CVector3(rayon_,0.0,0.0)); CVector3 vecPoint = initialPoint_ - point; point[0] += stretchingFactorWorld[0]*vecPoint[0]; point[1] += stretchingFactorWorld[1]*vecPoint[1]; point[2] += stretchingFactorWorld[2]*vecPoint[2]; initialTube1->addPoint(new Vertex(point,(point-initialPoint_).Normalize())); } CVector3 nextPoint = initialPoint_ + deplacementAxial_*directionInitiale; computeNewBand(initialTube1,initialPoint_,nextPoint,resolutionAxiale_+2); initialTube1->computeConnectivity(); initialTube1->computeTrianglesBarycentre(); initialTube2 = new SpinalCord; directionInitiale = initialNormal2_; if (directionInitiale[2] == 0.0) directionInitialePerpendiculaire = CVector3(0.0,0.0,1.0); else directionInitialePerpendiculaire = CVector3(1.0,2.0,-(directionInitiale[0]+2*directionInitiale[1])/directionInitiale[2]).Normalize(); refInitial = Referential(directionInitiale^directionInitialePerpendiculaire, directionInitialePerpendiculaire, directionInitiale, initialPoint_); transformationFromOrigin = refInitial.getTransformationInverse(); // Compute initial disk for (int k=0; k<resolutionRadiale_; k++) { angle = 2*M_PI*k/(double)resolutionRadiale_; trZ[0] = cos(angle), trZ[1] = sin(angle), trZ[3] = -sin(angle), trZ[4] = cos(angle); point = transformationFromOrigin*(trZ*CVector3(rayon_,0.0,0.0)); CVector3 vecPoint = initialPoint_ - point; point[0] += stretchingFactorWorld[0]*vecPoint[0]; point[1] += stretchingFactorWorld[1]*vecPoint[1]; point[2] += stretchingFactorWorld[2]*vecPoint[2]; initialTube2->addPoint(new Vertex(point,(point-initialPoint_).Normalize())); } nextPoint = initialPoint_ + deplacementAxial_*directionInitiale; computeNewBand(initialTube2,initialPoint_,nextPoint,resolutionAxiale_+2); initialTube2->computeConnectivity(); initialTube2->computeTrianglesBarycentre(); isMeshInitialized = true; meanContrast = computeContrast(refInitial); } else { initialTube1 = new SpinalCord; CVector3 directionInitiale = (centerline[1]-centerline[0]).Normalize(), directionInitialePerpendiculaire; if (directionInitiale[2] == 0.0) directionInitialePerpendiculaire = CVector3(0.0,0.0,1.0); else directionInitialePerpendiculaire = CVector3(1.0,2.0,-(directionInitiale[0]+2*directionInitiale[1])/directionInitiale[2]).Normalize(); Referential refInitial = Referential(directionInitiale^directionInitialePerpendiculaire, directionInitialePerpendiculaire, directionInitiale, centerline[0]); CMatrix4x4 transformationFromOrigin = refInitial.getTransformationInverse(); double angle; CMatrix3x3 trZ; CVector3 point, normale; // Compute initial disk for (int k=0; k<resolutionRadiale_; k++) { angle = 2*M_PI*k/(double)resolutionRadiale_; trZ[0] = cos(angle), trZ[1] = sin(angle), trZ[3] = -sin(angle), trZ[4] = cos(angle); point = transformationFromOrigin*(trZ*CVector3(rayon_,0.0,0.0)); initialTube1->addPoint(new Vertex(point,(point-centerline[0]).Normalize())); } for (unsigned int i=1; i<centerline.size(); i++) { computeNewBand(initialTube1,centerline[i-1],centerline[i],resolutionAxiale_); } initialTube1->computeConnectivity(); initialTube1->computeTrianglesBarycentre(); isMeshInitialized = true; } }