예제 #1
0
bool LimitedNodeList::packetVersionAndHashMatch(const QByteArray& packet) {
    PacketType checkType = packetTypeForPacket(packet);
    int numPacketTypeBytes = numBytesArithmeticCodingFromBuffer(packet.data());

    if (packet[numPacketTypeBytes] != versionForPacketType(checkType)
            && checkType != PacketTypeStunResponse) {
        PacketType mismatchType = packetTypeForPacket(packet);

        static QMultiMap<QUuid, PacketType> versionDebugSuppressMap;

        QUuid senderUUID = uuidFromPacketHeader(packet);
        if (!versionDebugSuppressMap.contains(senderUUID, checkType)) {
            qCDebug(networking) << "Packet version mismatch on" << packetTypeForPacket(packet) << "- Sender"
                                << uuidFromPacketHeader(packet) << "sent" << qPrintable(QString::number(packet[numPacketTypeBytes])) << "but"
                                << qPrintable(QString::number(versionForPacketType(mismatchType))) << "expected.";

            emit packetVersionMismatch();

            versionDebugSuppressMap.insert(senderUUID, checkType);
        }

        return false;
    }

    if (!NON_VERIFIED_PACKETS.contains(checkType)) {
        // figure out which node this is from
        SharedNodePointer sendingNode = sendingNodeForPacket(packet);
        if (sendingNode) {
            // check if the md5 hash in the header matches the hash we would expect
            if (hashFromPacketHeader(packet) == hashForPacketAndConnectionUUID(packet, sendingNode->getConnectionSecret())) {
                return true;
            } else {
                static QMultiMap<QUuid, PacketType> hashDebugSuppressMap;

                QUuid senderUUID = uuidFromPacketHeader(packet);
                if (!hashDebugSuppressMap.contains(senderUUID, checkType)) {
                    qCDebug(networking) << "Packet hash mismatch on" << checkType << "- Sender"
                                        << uuidFromPacketHeader(packet);

                    hashDebugSuppressMap.insert(senderUUID, checkType);
                }
            }
        } else {
            static QString repeatedMessage
                = LogHandler::getInstance().addRepeatedMessageRegex("Packet of type \\d+ received from unknown node with UUID");

            qCDebug(networking) << "Packet of type" << checkType << "received from unknown node with UUID"
                                << qPrintable(uuidStringWithoutCurlyBraces(uuidFromPacketHeader(packet)));
        }
    } else {
        return true;
    }

    return false;
}
예제 #2
0
void XCursorThemeData::parseIndexFile () {
  QMultiMap<QString, QString> cfg = loadCfgFile(mPath+"/index.theme", true);
  if (cfg.contains("icon theme/name")) mTitle = cfg.values("icon theme/name").at(0).trimmed();
  if (cfg.contains("icon theme/comment")) mDescription = cfg.values("icon theme/comment").at(0).trimmed();
  if (cfg.contains("icon theme/example")) mSample = cfg.values("icon theme/example").at(0).trimmed();
  if (cfg.contains("icon theme/hidden")) {
      QString hiddenValue = cfg.values("icon theme/hidden").at(0).toLower();
      mHidden = hiddenValue=="false" ? false : true;
  }
  if (cfg.contains("icon theme/inherits")) {
    QStringList i = cfg.values("icon theme/inherits"), res;
    for (int f = i.size()-1; f >= 0; f--) res << i.at(f).trimmed();
  }
  if (mDescription.startsWith("- Converted by")) mDescription.remove(0, 2);
}
예제 #3
0
bool LimitedNodeList::packetSourceAndHashMatch(const udt::Packet& packet) {
    
    PacketType headerType = NLPacket::typeInHeader(packet);
    
    if (NON_SOURCED_PACKETS.contains(headerType)) {
        return true;
    } else {
        QUuid sourceID = NLPacket::sourceIDInHeader(packet);
        
        // figure out which node this is from
        SharedNodePointer matchingNode = nodeWithUUID(sourceID);
        
        if (matchingNode) {
            if (!NON_VERIFIED_PACKETS.contains(headerType)) {
                
                QByteArray packetHeaderHash = NLPacket::verificationHashInHeader(packet);
                QByteArray expectedHash = NLPacket::hashForPacketAndSecret(packet, matchingNode->getConnectionSecret());
            
                // check if the md5 hash in the header matches the hash we would expect
                if (packetHeaderHash != expectedHash) {
                    static QMultiMap<QUuid, PacketType> hashDebugSuppressMap;
                    
                    if (!hashDebugSuppressMap.contains(sourceID, headerType)) {
                        qCDebug(networking) << "Packet hash mismatch on" << headerType << "- Sender" << sourceID;

                        hashDebugSuppressMap.insert(sourceID, headerType);
                    }

                    return false;
                }
            }

            // No matter if this packet is handled or not, we update the timestamp for the last time we heard
            // from this sending node
            matchingNode->setLastHeardMicrostamp(usecTimestampNow());
            
            return true;
            
        } else {
            static const QString UNKNOWN_REGEX = "Packet of type \\d+ \\([\\sa-zA-Z:]+\\) received from unknown node with UUID";
            static QString repeatedMessage
                = LogHandler::getInstance().addRepeatedMessageRegex(UNKNOWN_REGEX);

            qCDebug(networking) << "Packet of type" << headerType 
                << "received from unknown node with UUID" << qPrintable(uuidStringWithoutCurlyBraces(sourceID));
        }
    }

    return false;
}
예제 #4
0
bool LimitedNodeList::packetSourceAndHashMatch(const NLPacket& packet, SharedNodePointer& matchingNode) {
    
    if (NON_SOURCED_PACKETS.contains(packet.getType())) {
        return true;
    } else {
        
        // figure out which node this is from
        matchingNode = nodeWithUUID(packet.getSourceID());
        
        if (matchingNode) {
            if (!NON_VERIFIED_PACKETS.contains(packet.getType())) {
                // check if the md5 hash in the header matches the hash we would expect
                if (packet.getVerificationHash() != packet.payloadHashWithConnectionUUID(matchingNode->getConnectionSecret())) {
                    static QMultiMap<QUuid, PacketType::Value> hashDebugSuppressMap;
                    
                    const QUuid& senderID = packet.getSourceID();

                    if (!hashDebugSuppressMap.contains(senderID, packet.getType())) {
                        qCDebug(networking) << "Packet hash mismatch on" << packet.getType() << "- Sender" << senderID;

                        hashDebugSuppressMap.insert(senderID, packet.getType());
                    }

                    return false;
                }
            }
            
            return true;
            
        } else {
            static QString repeatedMessage
                = LogHandler::getInstance().addRepeatedMessageRegex("Packet of type \\d+ \\([\\sa-zA-Z]+\\) received from unknown node with UUID");

            qCDebug(networking) << "Packet of type" << packet.getType() << "(" << qPrintable(nameForPacketType(packet.getType())) << ")"
                << "received from unknown node with UUID" << qPrintable(uuidStringWithoutCurlyBraces(packet.getSourceID()));
        }
    }

    return false;
}
예제 #5
0
void OctreePacketProcessor::processPacket(const SharedNodePointer& sendingNode, const QByteArray& packet) {
    PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
                            "OctreePacketProcessor::processPacket()");
    
    QByteArray mutablePacket = packet;

    const int WAY_BEHIND = 300;

    if (packetsToProcessCount() > WAY_BEHIND && Application::getInstance()->getLogger()->extraDebugging()) {
        qDebug("OctreePacketProcessor::processPacket() packets to process=%d", packetsToProcessCount());
    }
    int messageLength = mutablePacket.size();

    Application* app = Application::getInstance();
    bool wasStatsPacket = false;


    PacketType voxelPacketType = packetTypeForPacket(mutablePacket);
    
    // note: PacketType_OCTREE_STATS can have PacketType_VOXEL_DATA
    // immediately following them inside the same packet. So, we process the PacketType_OCTREE_STATS first
    // then process any remaining bytes as if it was another packet
    if (voxelPacketType == PacketTypeOctreeStats) {
        int statsMessageLength = app->parseOctreeStats(mutablePacket, sendingNode);
        wasStatsPacket = true;
        if (messageLength > statsMessageLength) {
            mutablePacket = mutablePacket.mid(statsMessageLength);
            
            // TODO: this does not look correct, the goal is to test the packet version for the piggyback, but
            //       this is testing the version and hash of the original packet
            if (!DependencyManager::get<NodeList>()->packetVersionAndHashMatch(packet)) {
                return; // bail since piggyback data doesn't match our versioning
            }
        } else {
            // Note... stats packets don't have sequence numbers, so we don't want to send those to trackIncomingVoxelPacket()
            return; // bail since no piggyback data
        }
    } // fall through to piggyback message
    
    voxelPacketType = packetTypeForPacket(mutablePacket);
    PacketVersion packetVersion = mutablePacket[1];
    PacketVersion expectedVersion = versionForPacketType(voxelPacketType);
    
    // check version of piggyback packet against expected version
    if (packetVersion != expectedVersion) {
        static QMultiMap<QUuid, PacketType> versionDebugSuppressMap;
        
        QUuid senderUUID = uuidFromPacketHeader(packet);
        if (!versionDebugSuppressMap.contains(senderUUID, voxelPacketType)) {
            qDebug() << "Packet version mismatch on" << voxelPacketType << "- Sender"
            << senderUUID << "sent" << (int)packetVersion << "but"
            << (int)expectedVersion << "expected.";
            
            emit packetVersionMismatch();

            versionDebugSuppressMap.insert(senderUUID, voxelPacketType);
        }
        return; // bail since piggyback version doesn't match
    }
    
    app->trackIncomingOctreePacket(mutablePacket, sendingNode, wasStatsPacket);

    if (sendingNode) {

        switch(voxelPacketType) {
            case PacketTypeEntityErase: {
                if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderEntities()) {
                    app->_entities.processEraseMessage(mutablePacket, sendingNode);
                }
            } break;

            case PacketTypeEntityData: {
                if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderEntities()) {
                    app->_entities.processDatagram(mutablePacket, sendingNode);
                }
            } break;

            case PacketTypeEnvironmentData: {
                app->_environment.parseData(*sendingNode->getActiveSocket(), mutablePacket);
            } break;

            default: {
                // nothing to do
            } break;
        }
    }
}
void CalculateTaskScore::run()
{

    qDebug() << "CalculateTaskScore: Starting new thread";

    ConfigParser settings;
    QDateTime started = QDateTime::currentDateTime();
    ctemplate::TemplateDictionary dict("user_task_score");
    db = MySQLHandler::getInstance();
    QMultiMap<int, LCCode> users = UserDao::getUserNativeLCCodes(db);
    QList<QSharedPointer<Task> > tasks = this->getTasks();  //Must use custom function to check message for task id

    QMultiMap<int, LCCode> userSecondaryLanguages = UserDao::getUserLCCodes(db);
    QMultiMap<int, int> userTags = UserDao::getUserTagIds(db);
    QMultiMap<int, int> taskTags = TaskDao::getTaskTagIds(db);

    if(users.count() > 0) {
        for(QMultiMap<int, LCCode>::ConstIterator usersIter = users.constBegin(); usersIter != users.constEnd(); ++usersIter) {
            if(tasks.length() > 0) {
                const LCCode userNativeLCCode = users.value(usersIter.key());
                QList<TaskScore> taskScores;
                foreach(QSharedPointer<Task> task, tasks) {
                    int score = 0;

                    Locale taskSourceLocale = task->sourcelocale();

                    if(userNativeLCCode.first == taskSourceLocale.languagecode()) {
                        score += 750;
                        if(userNativeLCCode.second == taskSourceLocale.countrycode()) {
                            score += 75;
                        }
                    }

                    Locale taskTargetLocale = task->targetlocale();

                    if(userNativeLCCode.first == taskTargetLocale.languagecode()) {
                        score += 1000;
                        if(userNativeLCCode.second == taskTargetLocale.countrycode()) {
                            score += 100;
                        }
                    }

                    if(userSecondaryLanguages.contains(usersIter.key())) {
                        const QList<LCCode> lcCodes = userSecondaryLanguages.values(usersIter.key());
                        if(lcCodes.end() != std::find_if(lcCodes.begin(), lcCodes.end(), LidMatch(taskSourceLocale.languagecode()))) {
                            score += 500;
                            if(lcCodes.end() != std::find_if(lcCodes.begin(), lcCodes.end(), CidMatch(taskSourceLocale.countrycode())) ) {
                                score += 50;
                            }
                        }
                        if(lcCodes.end() != std::find_if(lcCodes.begin(), lcCodes.end(), LidMatch(taskTargetLocale.languagecode()))) {
                            score += 500;
                            if(lcCodes.end() != std::find_if(lcCodes.begin(), lcCodes.end(), CidMatch(taskTargetLocale.countrycode())) ) {
                                score += 50;
                            }
                        }
                    }

                    if(userTags.contains(usersIter.key()) && taskTags.contains(task->id())) {
                        int increment_value = 250;
                        QList<int> userTagIds = userTags.values(usersIter.key());
                        QList<int> userTaskTagIds = taskTags.values(task->id());

                        foreach(int userTagId, userTagIds) {
                            if(userTaskTagIds.contains(userTagId)) {
                                    score += increment_value;
                                    increment_value *= 0.75;
                            }
                        }
                    }

                    QDateTime created_time = QDateTime::fromString(
                                            QString::fromStdString(task->createdtime()), Qt::ISODate);
                    //increase score by one per day since created time
                    score += created_time.daysTo(QDateTime::currentDateTime());
                    taskScores.append(TaskScore(task->id(), score));
                }

                    this->saveUserTaskScore(usersIter.key(),taskScores);

            } else {
예제 #7
0
void Naa2TlvConverter::findSuspectInks() {
  QMultiMap<TUINT32, int> paintColorTable;
  for (int i = 0; i < m_regions.count(); i++) {
    RegionInfo &region = m_regions[i];
    if (0 == (region.type & RegionInfo::Paint)) continue;
    TPixel32 color = m_colors[region.colorIndex];
    if (color == TPixel32(0, 0, 0)) {
      int x = 1234;
      continue;
    }
    TUINT32 rawColor = *(TUINT32 *)&color;
    paintColorTable.insert(rawColor, i);
  }

  int count = 0;
  for (int i = 0; i < m_regions.count(); i++) {
    RegionInfo &region = m_regions[i];
    if (region.isInk() && region.links.size() == 2) {
      int ra = region.links.keys().at(0), rb = region.links.keys().at(1);
      if (ra >= 0 && rb >= 0) {
        if (!m_regions[ra].isInk()) qSwap(ra, rb);
        if (m_regions[ra].isInk() && !m_regions[rb].isInk()) {
          int sa = region.links[ra];
          int sb = region.links[rb];
          if (sa > sb) {
            region.type = RegionInfo::Paint;
            count++;
            continue;
          }
        }
      }
    }

    if (region.type != RegionInfo::ThinInk) continue;
    TUINT32 rawColor = *(TUINT32 *)&m_colors[region.colorIndex];
    if (paintColorTable.contains(rawColor)) {
      region.type = RegionInfo::Unknown;
      count++;
    }
  }

  for (int i = 0; i < m_regions.count(); i++) {
    RegionInfo &region = m_regions[i];
    if (region.isInk() && 10 <= region.pixelCount && region.pixelCount < 100) {
      int lx = region.x1 - region.x0 + 1;
      int ly = region.y1 - region.y0 + 1;
      int d  = qMax(lx, ly);
      if (qMin(lx, ly) * 2 > qMax(lx, ly) && region.pixelCount > d * d / 2) {
        region.type = RegionInfo::Paint;
      }
    }

    if (region.type == RegionInfo::Paint ||
        region.type == RegionInfo::Unknown) {
      bool isInk = false;
      if (region.boundaries.at(0) == 0) {
        if (region.boundaries.count() == 2)
          isInk = true;
        else if (region.boundaries.count() == 3) {
          int b1                 = region.boundaries.at(1);
          int b2                 = region.boundaries.at(2);
          if (b1 * 2 < b2) isInk = true;
        }
      }
      if (isInk) region.type = RegionInfo::Ink;
    }
  }
}
예제 #8
0
void OctreePacketProcessor::processPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
    PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
                            "OctreePacketProcessor::processPacket()");

    const int WAY_BEHIND = 300;

    if (packetsToProcessCount() > WAY_BEHIND && qApp->getLogger()->extraDebugging()) {
        qDebug("OctreePacketProcessor::processPacket() packets to process=%d", packetsToProcessCount());
    }
    
    bool wasStatsPacket = false;

    PacketType octreePacketType = message->getType();

    // note: PacketType_OCTREE_STATS can have PacketType_VOXEL_DATA
    // immediately following them inside the same packet. So, we process the PacketType_OCTREE_STATS first
    // then process any remaining bytes as if it was another packet
    if (octreePacketType == PacketType::OctreeStats) {
        int statsMessageLength = qApp->processOctreeStats(*message, sendingNode);

        wasStatsPacket = true;
        int piggybackBytes = message->getSize() - statsMessageLength;
        
        if (piggybackBytes) {
            // construct a new packet from the piggybacked one
            auto buffer = std::unique_ptr<char[]>(new char[piggybackBytes]);
            memcpy(buffer.get(), message->getRawMessage() + statsMessageLength, piggybackBytes);
            
            auto newPacket = NLPacket::fromReceivedPacket(std::move(buffer), piggybackBytes, message->getSenderSockAddr());
            message = QSharedPointer<ReceivedMessage>::create(*newPacket);
        } else {
            // Note... stats packets don't have sequence numbers, so we don't want to send those to trackIncomingVoxelPacket()
            return; // bail since no piggyback data
        }
    } // fall through to piggyback message

    PacketType packetType = message->getType();

    // check version of piggyback packet against expected version
    if (message->getVersion() != versionForPacketType(message->getType())) {
        static QMultiMap<QUuid, PacketType> versionDebugSuppressMap;

        const QUuid& senderUUID = message->getSourceID();
        if (!versionDebugSuppressMap.contains(senderUUID, packetType)) {
            
            qDebug() << "Was stats packet? " << wasStatsPacket;
            qDebug() << "OctreePacketProcessor - piggyback packet version mismatch on" << packetType << "- Sender"
                << senderUUID << "sent" << (int) message->getVersion() << "but"
                << (int) versionForPacketType(packetType) << "expected.";

            emit packetVersionMismatch();

            versionDebugSuppressMap.insert(senderUUID, packetType);
        }
        return; // bail since piggyback version doesn't match
    }

    qApp->trackIncomingOctreePacket(*message, sendingNode, wasStatsPacket);
    
    // seek back to beginning of packet after tracking
    message->seek(0);

    switch(packetType) {
        case PacketType::EntityErase: {
            if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderEntities()) {
                qApp->getEntities()->processEraseMessage(*message, sendingNode);
            }
        } break;

        case PacketType::EntityData: {
            if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderEntities()) {
                qApp->getEntities()->processDatagram(*message, sendingNode);
            }
        } break;

        default: {
            // nothing to do
        } break;
    }
}
예제 #9
0
int main(int argc, char *argv[])
{
    QApplication a(argc, argv, false);

    if (a.argc() < 2)
    {
        cerr << "You must specify at least a starting directory." << endl;
        a.exit(-1);
    }

    indir = a.argv()[1];
    if (a.argc() == 3)
        outfilebase = a.argv()[2];
    else
        outfilebase = indir;

    if (indir.isEmpty() || outfilebase.isEmpty())
    {
        cerr << "no filenames\n";
        exit(-1);
    }

    QDir themeDir(indir);
    if (!themeDir.exists())
    {
        cerr << "Starting directory does not exist\n";
        exit(-1);
    }

    outfile = outfilebase + '/' + "themestrings.h";

    parseDirectory(indir);

    fstringout.setFileName(outfile);

    if (!fstringout.open(QIODevice::WriteOnly))
    {
        cerr << "can't open " << qPrintable(outfile) << " for writing\n";
        exit(-1);
    }

    fdataout.setDevice(&fstringout);
    fdataout.setCodec("UTF-8");

    fdataout << QString("// This is an automatically generated file\n");
    fdataout << QString("// Do not edit\n\n");
    fdataout << QString("void strings_null() {\n");

    int lineCount = 2;
    QStringList::const_iterator strit;
    for (strit = strings.begin(); strit != strings.end(); ++strit)
    {
        QString string = (*strit);
        if (string.contains("%n"))
            fdataout << QString("    ThemeUI::tr(\"%1\", 0, 1);\n")
                                .arg(string);
        else
            fdataout << QString("    ThemeUI::tr(\"%1\");\n")
                                .arg(string);

#if 0
        if (translatedStrings.contains(*strit))
        {
            QStringList prevSeenLanguages;
            QList<QString> values = translatedStrings.values(*strit);
            for (int i = 0; i < values.size(); ++i)
            {
                QString language = values.at(i).section("{}", 0, 0);
                if (prevSeenLanguages.contains(language))
                    continue;
                prevSeenLanguages << language;

                QString translation = values.at(i).section("{}", 1, 1);
                if (!transFiles.contains(language))
                {
                    QFile *tmp = new QFile(outfile + '_' + language + ".ts");
                    if (!tmp->open(QIODevice::WriteOnly))
                    {
                        cerr << "couldn't open language file\n";
                        exit(-1);
                    }

                    transFiles[language] = tmp;
                }

                QTextStream dstream(transFiles[language]);
                dstream.setCodec("UTF-8");

                dstream << "    <message>\n"
                        << "        <location filename=\"" << qPrintable(outfile) << "\" line=\"" << lineCount << "\"/>\n"
                        << "        <source>" << Qt::escape(*strit).replace("\"", "&quot;") << "<source>\n"
                        << "        <translation>" << Qt::escape(translation).replace("\"", "&quot;") << "<translation>\n"
                        << "    <message>\n";
            }
        }
#endif
        ++lineCount;
    }

    fdataout << QString("}\n");
    fstringout.close();

#if 0
    QMap<QString, QFile *>::Iterator it;
    for (it = transFiles.begin(); it != transFiles.end(); ++it)
    {
        it.value()->close();
    }
#endif

    cout << endl;
    cout << "---------------------------------------" << endl;
    cout << "Found " << totalStringCount << " total strings" << endl;
    cout << strings.count() << " unique" << endl;

    return 0;
}
예제 #10
0
// virtual
void SkeletonRagdoll::buildConstraints() {
    QVector<JointState>& jointStates = _model->getJointStates();

    // NOTE: the length of DistanceConstraints is computed and locked in at this time
    // so make sure the ragdoll positions are in a normal configuration before here.
    const int numPoints = _points.size();
    assert(numPoints == jointStates.size());

    float minBone = FLT_MAX;
    float maxBone = -FLT_MAX;
    QMultiMap<int, int> families;
    for (int i = _rootIndex; i < numPoints; ++i) {
        const JointState& state = jointStates.at(i);
        int parentIndex = state.getParentIndex();
        if (parentIndex != -1) {
            DistanceConstraint* bone = new DistanceConstraint(&(_points[i]), &(_points[parentIndex]));
            bone->setDistance(state.getDistanceToParent());
            _boneConstraints.push_back(bone);
            families.insert(parentIndex, i);
        }
        float boneLength = glm::length(state.getPositionInParentFrame());
        if (boneLength > maxBone) {
            maxBone = boneLength;
        } else if (boneLength < minBone) {
            minBone = boneLength;
        }
    }
    // Joints that have multiple children effectively have rigid constraints between the children
    // in the parent frame, so we add DistanceConstraints between children in the same family.
    QMultiMap<int, int>::iterator itr = families.begin();
    while (itr != families.end()) {
        QList<int> children = families.values(itr.key());
        int numChildren = children.size();
        if (numChildren > 1) {
            for (int i = 1; i < numChildren; ++i) {
                DistanceConstraint* bone = new DistanceConstraint(&(_points[children[i-1]]), &(_points[children[i]]));
                _boneConstraints.push_back(bone);
            }
            if (numChildren > 2) {
                DistanceConstraint* bone = new DistanceConstraint(&(_points[children[numChildren-1]]), &(_points[children[0]]));
                _boneConstraints.push_back(bone);
            }
        }
        ++itr;
    }

    float MAX_STRENGTH = 0.6f;
    float MIN_STRENGTH = 0.05f;
    // each joint gets a MuscleConstraint to its parent
    for (int i = _rootIndex + 1; i < numPoints; ++i) {
        const JointState& state = jointStates.at(i);
        int p = state.getParentIndex();
        if (p == -1) {
            continue;
        }
        MuscleConstraint* constraint = new MuscleConstraint(&(_points[p]), &(_points[i]));
        _muscleConstraints.push_back(constraint);

        // Short joints are more susceptible to wiggle so we modulate the strength based on the joint's length: 
        // long = weak and short = strong.
        constraint->setIndices(p, i);
        float boneLength = glm::length(state.getPositionInParentFrame());

        float strength = MIN_STRENGTH + (MAX_STRENGTH - MIN_STRENGTH) * (maxBone - boneLength) / (maxBone - minBone);
        if (!families.contains(i)) {
            // Although muscles only pull on the children not parents, nevertheless those joints that have
            // parents AND children are more stable than joints at the end such as fingers.  For such joints we
            // bestow maximum strength which helps reduce wiggle.
            strength = MAX_MUSCLE_STRENGTH;
        }
        constraint->setStrength(strength);
    }
}