//datalen是计算出来的,所以可以直接append //return 0 ssize_t MemKeyCache::Append(const char* pKey,const ssize_t KEYSIZE,char* szData,ssize_t iDataSize,ssize_t iDataFlag) { #ifdef _BUFFMNG_APPEND_SKIP if (!pKey || !szData) return E_PARAM; ssize_t iNodeIdx = GetNodeIdx(pKey,KEYSIZE); if( iNodeIdx < 0 ) return E_NO_DATA; ssize_t iRet = m_BuffMng.AppendBuffer(iNodeIdx,szData,iDataSize); if(iRet != 0) { return iRet; } //写binlog if (m_iBinLogOpen) { ssize_t iOldDataFlag = GetFlag(iNodeIdx); if((iDataFlag != F_DATA_CLEAN) || (iOldDataFlag != F_DATA_CLEAN)) { iRet = _WriteDataLog(op_append,pKey,KEYSIZE,szData,iDataSize,iDataFlag); if(iRet != 0) return E_WRITE_BINLOG; } } #else ASSERT(1); #endif return 0; }
//返回数据长度 ssize_t MemKeyCache::Get(const char* pKey,const ssize_t KEYSIZE, char* pData,const ssize_t DATASIZE,ssize_t &iDataFlag, char* pReserve,const ssize_t RESERVESIZE,ssize_t &iReserveLen) { if (!pKey || !pData || DATASIZE<=0 || KEYSIZE<=0 || KEYSIZE>=MAX_KEY_LEN) return E_PARAM; ssize_t iNodeIdx = GetNodeIdx(pKey,KEYSIZE); if( iNodeIdx < 0 ) return E_NO_DATA; ssize_t iRet = GetDataByIdx(iNodeIdx, pData,DATASIZE,iDataFlag, pReserve,RESERVESIZE,iReserveLen); if(iDataFlag == F_DATA_CLEAN) { m_NodeRLruQueue.DeleteItem(iNodeIdx); m_NodeRLruQueue.AppendToTail(iNodeIdx); } else { m_NodeWLruQueue.DeleteItem(iNodeIdx); m_NodeWLruQueue.AppendToTail(iNodeIdx); } return iRet; }
void CCharShape::AddAction (size_t node_name, int type, const TVector3d& vec, ETR_DOUBLE val) { size_t idx = GetNodeIdx (node_name); TCharAction *act = Nodes[idx]->action; act->type[act->num] = type; act->vec[act->num] = vec; act->dval[act->num] = val; act->num++; }
void CCharShape::AddAction (int node_name, int type, TVector3 vec, double val) { int idx = GetNodeIdx (node_name); if (idx < 0) return; TCharAction *act = Actions[idx]; act->type[act->num] = type; act->vec[act->num] = vec; act->dval[act->num] = val; act->num++; }
bool CCharShape::GetNode (int node_name, TCharNode **node) { int idx = GetNodeIdx (node_name); if (idx < 0 || idx >= numNodes) { *node = NULL; return false; } else { *node = Nodes[idx]; return true; } }
bool CCharShape::MaterialNode (int node_name, string mat_name) { TCharMaterial *mat; TCharNode *node; int idx = GetNodeIdx (node_name); if (GetNode (node_name, &node) == false) return false; if (GetMaterial (mat_name.c_str(), &mat) == false) return false; node->mat = mat; if (newActions && useActions) Actions[idx]->mat = mat_name; return true; }
//返回数据长度 ssize_t MemKeyCache::GetNoLRU(const char* pKey,const ssize_t KEYSIZE, char* pData,const ssize_t DATASIZE,ssize_t &iDataFlag, char* pReserve,const ssize_t RESERVESIZE,ssize_t &iReserveLen) { if (!pKey || !pData || DATASIZE<=0 || KEYSIZE<=0 || KEYSIZE>=MAX_KEY_LEN) return E_PARAM; ssize_t iNodeIdx = GetNodeIdx(pKey,KEYSIZE); if( iNodeIdx < 0 ) return E_NO_DATA; ssize_t iRet = GetDataByIdx(iNodeIdx, pData,DATASIZE,iDataFlag, pReserve,RESERVESIZE,iReserveLen); return iRet; }
ssize_t MemKeyCache::GetDataSize(const char* pKey,const ssize_t KEYSIZE) { ssize_t iNodeIdx = GetNodeIdx(pKey, KEYSIZE); if (iNodeIdx <0) return E_NO_DATA; char szTmpBuffer[MIN_DATA_LEN+MAX_KEY_LEN]; ssize_t iMinGetLen = MIN_DATA_LEN+KEYSIZE; ssize_t iAllLen = m_BuffMng.GetBuffer(iNodeIdx,szTmpBuffer,iMinGetLen); if(iAllLen < iMinGetLen) return E_NO_DATA; iAllLen = m_BuffMng.GetBufferSize(iNodeIdx); DecodeBufferFormat(szTmpBuffer,iAllLen); return iNodeDataLen; }
///' Calculate the network properties ///' ///' @details ///' \subsection{Input expectations:}{ ///' Note that this function expects all inputs to be sensible, as checked by ///' the R function 'checkUserInput' and processed by 'networkProperties'. ///' ///' These requirements are: ///' \itemize{ ///' \item{The ordering of node names across 'data' and 'net' is consistent.} ///' \item{The columns of 'data' are the nodes.} ///' \item{'net' is a square matrix, and its rownames are identical to its ///' column names.} ///' \item{'moduleAssigments' is a named character vector, where the names ///' represent node labels found in the discovery dataset. Unlike ///' 'PermutationProcedure', these may include nodes that are not ///' present in 'data' and 'net'.} ///' \item{The module labels specified in 'modules' must occur in ///' 'moduleAssignments'.} ///' } ///' } ///' ///' @param data data matrix from the dataset in which to calculate the network ///' properties. ///' @param net adjacency matrix of network edge weights between all pairs of ///' nodes in the dataset in which to calculate the network properties. ///' @param moduleAssignments a named character vector containing the module ///' each node belongs to in the discovery dataset. ///' @param modules a character vector of modules for which to calculate the ///' network properties for. ///' ///' @return a list containing the summary profile, node contribution, module ///' coherence, weighted degree, and average edge weight for each 'module'. ///' ///' @keywords internal // [[Rcpp::export]] Rcpp::List NetProps ( Rcpp::NumericMatrix data, Rcpp::NumericMatrix net, Rcpp::CharacterVector moduleAssignments, Rcpp::CharacterVector modules ) { // First, scale the matrix data unsigned int nSamples = data.nrow(); unsigned int nNodes = data.ncol(); arma::mat scaledData = Scale(data.begin(), nSamples, nNodes); R_CheckUserInterrupt(); // convert the colnames / rownames to C++ equivalents const std::vector<std::string> nodeNames (Rcpp::as<std::vector<std::string>>(colnames(net))); const std::vector<std::string> sampleNames (Rcpp::as<std::vector<std::string>>(rownames(data))); /* Next, we need to create two mappings: * - From node IDs to indices in the dataset of interest * - From modules to node IDs * - From modules to only node IDs present in the dataset of interest */ const namemap nodeIdxMap = MakeIdxMap(nodeNames); const stringmap modNodeMap = MakeModMap(moduleAssignments); const stringmap modNodePresentMap = MakeModMap(moduleAssignments, nodeIdxMap); // What modules do we actually want to analyse? const std::vector<std::string> mods (Rcpp::as<std::vector<std::string>>(modules)); R_CheckUserInterrupt(); // Calculate the network properties for each module std::string mod; // iterators unsigned int mNodesPresent, mNodes; arma::uvec nodeIdx, propIdx, nodeRank; namemap propIdxMap; std::vector<std::string> modNodeNames; arma::vec WD, SP, NC; // results containers double avgWeight, coherence; Rcpp::NumericVector degree, summary, contribution; // for casting to R equivalents Rcpp::List results; // final storage container for (auto mi = mods.begin(); mi != mods.end(); ++mi) { // What nodes are in this module? mod = *mi; modNodeNames = GetModNodeNames(mod, modNodeMap); // initialise results containers with NA values for nodes not present in // the dataset we're calculating the network properties in. degree = Rcpp::NumericVector(modNodeNames.size(), NA_REAL); contribution = Rcpp::NumericVector(modNodeNames.size(), NA_REAL); summary = Rcpp::NumericVector(nSamples, NA_REAL); avgWeight = NA_REAL; coherence = NA_REAL; degree.names() = modNodeNames; contribution.names() = modNodeNames; // Create a mapping between node names and the result vectors propIdxMap = MakeIdxMap(modNodeNames); // Get just the indices of nodes that are present in the requested dataset nodeIdx = GetNodeIdx(mod, modNodePresentMap, nodeIdxMap); mNodesPresent = nodeIdx.n_elem; // And a mapping of those nodes to the initialised vectors propIdx = GetNodeIdx(mod, modNodePresentMap, propIdxMap); mNodes = propIdx.n_elem; // Calculate the properties if the module has nodes in the test dataset if (nodeIdx.n_elem > 0) { // sort the node indices for sequential memory access nodeRank = SortNodes(nodeIdx.memptr(), mNodesPresent); WD = WeightedDegree(net.begin(), nNodes, nodeIdx.memptr(), mNodesPresent); WD = WD(nodeRank); // reorder results avgWeight = AverageEdgeWeight(WD.memptr(), WD.n_elem); R_CheckUserInterrupt(); SP = SummaryProfile(scaledData.memptr(), nSamples, nNodes, nodeIdx.memptr(), mNodesPresent); R_CheckUserInterrupt(); NC = NodeContribution(scaledData.memptr(), nSamples, nNodes, nodeIdx.memptr(), mNodesPresent, SP.memptr()); NC = NC(nodeRank); // reorder results coherence = ModuleCoherence(NC.memptr(), mNodesPresent); R_CheckUserInterrupt(); // Convert NaNs to NAs SP.elem(arma::find_nonfinite(SP)).fill(NA_REAL); NC.elem(arma::find_nonfinite(NC)).fill(NA_REAL); if (!arma::is_finite(coherence)) { coherence = NA_REAL; } // Fill results vectors Fill(degree, WD.memptr(), mNodesPresent, propIdx.memptr(), mNodes); Fill(contribution, NC.memptr(), mNodesPresent, propIdx.memptr(), mNodes); summary = Rcpp::NumericVector(SP.begin(), SP.end()); } summary.names() = sampleNames; results.push_back( Rcpp::List::create( Rcpp::Named("summary") = summary, Rcpp::Named("contribution") = contribution, Rcpp::Named("coherence") = coherence, Rcpp::Named("degree") = degree, Rcpp::Named("avgWeight") = avgWeight ) ); } results.names() = mods; return(results); }
///' Calculate the network properties, data matrix not provided ///' ///' @details ///' \subsection{Input expectations:}{ ///' Note that this function expects all inputs to be sensible, as checked by ///' the R function 'checkUserInput' and processed by 'networkProperties'. ///' ///' These requirements are: ///' \itemize{ ///' \item{'net' is a square matrix, and its rownames are identical to its ///' column names.} ///' \item{'moduleAssigments' is a named character vector, where the names ///' represent node labels found in the discovery dataset. Unlike ///' 'PermutationProcedure', these may include nodes that are not ///' present in 'data' and 'net'.} ///' \item{The module labels specified in 'modules' must occur in ///' 'moduleAssignments'.} ///' } ///' } ///' ///' @param net adjacency matrix of network edge weights between all pairs of ///' nodes in the dataset in which to calculate the network properties. ///' @param moduleAssignments a named character vector containing the module ///' each node belongs to in the discovery dataset. ///' @param modules a character vector of modules for which to calculate the ///' network properties for. ///' ///' @return a list containing the summary profile, node contribution, module ///' coherence, weighted degree, and average edge weight for each 'module'. ///' ///' @keywords internal // [[Rcpp::export]] Rcpp::List NetPropsNoData ( Rcpp::NumericMatrix net, Rcpp::CharacterVector moduleAssignments, Rcpp::CharacterVector modules ) { // convert the colnames / rownames to C++ equivalents const std::vector<std::string> nodeNames (Rcpp::as<std::vector<std::string>>(colnames(net))); unsigned int nNodes = net.ncol(); R_CheckUserInterrupt(); /* Next, we need to create two mappings: * - From node IDs to indices in the dataset of interest * - From modules to node IDs * - From modules to only node IDs present in the dataset of interest */ const namemap nodeIdxMap = MakeIdxMap(nodeNames); const stringmap modNodeMap = MakeModMap(moduleAssignments); const stringmap modNodePresentMap = MakeModMap(moduleAssignments, nodeIdxMap); // What modules do we actually want to analyse? const std::vector<std::string> mods (Rcpp::as<std::vector<std::string>>(modules)); R_CheckUserInterrupt(); // Calculate the network properties for each module std::string mod; // iterators unsigned int mNodesPresent, mNodes; arma::uvec nodeIdx, propIdx, nodeRank; namemap propIdxMap; std::vector<std::string> modNodeNames; arma::vec WD; // results containers double avgWeight; Rcpp::NumericVector degree; // for casting to R equivalents Rcpp::List results; // final storage container for (auto mi = mods.begin(); mi != mods.end(); ++mi) { // What nodes are in this module? // modNodeNames = names(moduleAssignments[moduleAssignments == mod]) mod = *mi; modNodeNames = GetModNodeNames(mod, modNodeMap); // initialise results containers with NA values for nodes not present in // the dataset we're calculating the network properties in. degree = Rcpp::NumericVector(modNodeNames.size(), NA_REAL); avgWeight = NA_REAL; degree.names() = modNodeNames; // Create a mapping between node names and the result vectors propIdxMap = MakeIdxMap(modNodeNames); // Get just the indices of nodes that are present in the requested dataset nodeIdx = GetNodeIdx(mod, modNodePresentMap, nodeIdxMap); mNodesPresent = nodeIdx.n_elem; // And a mapping of those nodes to the initialised vectors propIdx = GetNodeIdx(mod, modNodePresentMap, propIdxMap); mNodes = propIdx.n_elem; // Calculate the properties if the module has nodes in the test dataset if (nodeIdx.n_elem > 0) { // sort the node indices for sequential memory access nodeRank = SortNodes(nodeIdx.memptr(), mNodesPresent); WD = WeightedDegree(net.begin(), nNodes, nodeIdx.memptr(), mNodesPresent); WD = WD(nodeRank); // reorder results avgWeight = AverageEdgeWeight(WD.memptr(), WD.n_elem); R_CheckUserInterrupt(); // Fill the results vectors appropriately Fill(degree, WD.memptr(), mNodesPresent, propIdx.memptr(), mNodes); } results.push_back( Rcpp::List::create( Rcpp::Named("degree") = degree, Rcpp::Named("avgWeight") = avgWeight ) ); } results.names() = mods; return(results); }
///' Calculate the intermediate network properties in the discovery dataset ///' ///' These properties are need at every permutation: so they will be computed ///' once. ///' ///' @details ///' \subsection{Input expectations:}{ ///' Note that this function expects all inputs to be sensible, as checked by ///' the R function 'checkUserInput' and processed by 'modulePreservation'. ///' ///' These requirements are: ///' \itemize{ ///' \item{The ordering of node names across 'dData', 'dCorr', and 'dNet' is ///' consistent.} ///' \item{The columns of 'dData' are the nodes.} ///' \item{'dData' has been scaled by 'Scale'.} ///' \item{'dCorr' and 'dNet' are square matrices, and their rownames are ///' identical to their column names.} ///' \item{'moduleAssigments' is a named character vector, where the names ///' represent node labels found in the discovery dataset (e.g. 'dNet').} ///' } ///' } ///' ///' @param dData scaled data matrix from the \emph{discovery} dataset. ///' @param dCorr matrix of correlation coefficients between all pairs of ///' variables/nodes in the \emph{discovery} dataset. ///' @param dNet adjacency matrix of network edge weights between all pairs of ///' nodes in the \emph{discovery} dataset. ///' @param tNodeNames a character vector of node names in the test dataset ///' @param moduleAssignments a named character vector containing the module ///' each node belongs to in the discovery dataset. ///' @param modules a character vector of modules for which to calculate the ///' module preservation statistics. ///' ///' @return a list containing three lists: a list of weighted degree vectors, ///' a list of correlation coefficient vectors, and a list of node ///' contribution vectors. There is one vector for each module in each list. ///' ///' @keywords internal // [[Rcpp::export]] Rcpp::List IntermediateProperties ( Rcpp::NumericMatrix dData, Rcpp::NumericMatrix dCorr, Rcpp::NumericMatrix dNet, Rcpp::CharacterVector tNodeNames, Rcpp::CharacterVector moduleAssignments, Rcpp::CharacterVector modules ) { // First, scale the matrix data unsigned int nSamples = dData.nrow(); unsigned int nNodes = dData.ncol(); R_CheckUserInterrupt(); // convert the colnames / rownames to C++ equivalents const std::vector<std::string> dNames (Rcpp::as<std::vector<std::string>>(colnames(dNet))); const std::vector<std::string> tNames (Rcpp::as<std::vector<std::string>>(tNodeNames)); /* Next, we need to create three mappings: * - From node IDs to indices in the discovery dataset. * - From modules to all node IDs. * - From modules to just node IDs present in the test dataset. */ const namemap dIdxMap = MakeIdxMap(dNames); const stringmap modNodeMap = MakeModMap(moduleAssignments); const namemap tIdxMap = MakeIdxMap(tNames); const stringmap modNodePresentMap = MakeModMap(moduleAssignments, tIdxMap); // What modules do we actually want to analyse? const std::vector<std::string> mods (Rcpp::as<std::vector<std::string>>(modules)); // We only need to iterate through modules which have nodes in the test // dataset std::vector<std::string> modsPresent; for (auto it = mods.begin(); it != mods.end(); ++it) { if (modNodePresentMap.count(*it) > 0) { modsPresent.push_back(*it); } } R_CheckUserInterrupt(); Rcpp::List degree; Rcpp::List corr; Rcpp::List contribution; // Calculate the network properties in the discovery dataset. std::string mod; unsigned int mNodes; arma::uvec dIdx, dRank; arma::vec dSP, dWD, dCV, dNC; for (auto mi = modsPresent.begin(); mi != modsPresent.end(); ++mi) { // Get the node indices in the discovery dataset for this module mod = *mi; dIdx = GetNodeIdx(mod, modNodePresentMap, dIdxMap); mNodes = dIdx.n_elem; R_CheckUserInterrupt(); // Calculate the network properties and insert into their storage containers dCV = CorrVector(dCorr.begin(), nNodes, dIdx.memptr(), mNodes); R_CheckUserInterrupt(); // Sort node indices for sequential memory access dRank = SortNodes(dIdx.memptr(), mNodes); dWD = WeightedDegree(dNet.begin(), nNodes, dIdx.memptr(), mNodes); dWD = dWD(dRank); // reorder R_CheckUserInterrupt(); dSP = SummaryProfile(dData.begin(), nSamples, nNodes, dIdx.memptr(), mNodes); R_CheckUserInterrupt(); dNC = NodeContribution(dData.begin(), nSamples, nNodes, dIdx.memptr(), mNodes, dSP.memptr()); dNC = dNC(dRank); // reorder results R_CheckUserInterrupt(); // Cast to R-vectors and add to results lists corr.push_back(Rcpp::NumericVector(dCV.begin(), dCV.end())); degree.push_back(Rcpp::NumericVector(dWD.begin(), dWD.end())); contribution.push_back(Rcpp::NumericVector(dNC.begin(), dNC.end())); } degree.names() = modsPresent; corr.names() = modsPresent; contribution.names() = modsPresent; return Rcpp::List::create( Rcpp::Named("degree") = degree, Rcpp::Named("corr") = corr, Rcpp::Named("contribution") = contribution ); }
ssize_t MemKeyCache::MarkFlag(const char* pKey,const ssize_t KEYSIZE,ssize_t iDataFlag) { ssize_t iNodeIdx = GetNodeIdx(pKey,KEYSIZE); if(iNodeIdx < 0) return E_NO_DATA; ssize_t iOldDataFlag = GetFlag(iNodeIdx); if(iOldDataFlag == iDataFlag) return 0; //dirty,del -> clean if(iDataFlag == F_DATA_CLEAN) { m_NodeWLruQueue.DeleteItem(iNodeIdx); m_NodeRLruQueue.AppendToTail(iNodeIdx); } // clean -> dirty,del else { m_NodeRLruQueue.DeleteItem(iNodeIdx); m_NodeWLruQueue.AppendToTail(iNodeIdx); } //new flag copy in DecodeBufferKeyFormat(m_BuffMng.GetFirstBlockPtr(iNodeIdx),m_BuffMng.GetBlockSize()); *piDataFlag = iDataFlag; if (m_iBinLogOpen) { //dirty,del -> clean if(iOldDataFlag != F_DATA_CLEAN) { char szTmpBuffer[MIN_DATA_LEN + MAX_KEY_LEN]; *(char*)szTmpBuffer = op_mark; ssize_t iEncodeLen = EncodeBufferKeyFormat(szTmpBuffer+sizeof(char),MIN_DATA_LEN + MAX_KEY_LEN-sizeof(char), iDataFlag,KEYSIZE,pKey); ASSERT(iEncodeLen > 0); ssize_t iRet = m_stBinLog.WriteToBinLog(szTmpBuffer,sizeof(char)+iEncodeLen); if(iRet != 0) return E_WRITE_BINLOG; } //clean -> dirty,del else { ssize_t iBuffLen = GetBufferSize(GetNodeIdx(pKey,KEYSIZE)); if(iBuffLen < 10240) { char pTmpData[10240]; char pReserve[10240]; ssize_t iReserveLen = 0; ssize_t iDataFlag; ssize_t iDataLen = Get(pKey,KEYSIZE, pTmpData,10240,iDataFlag, pReserve,10240,iReserveLen); ssize_t iRet = _WriteDataLog(op_set,pKey,KEYSIZE,pTmpData,iDataLen,iDataFlag,pReserve,iReserveLen); if(iRet != 0) return E_WRITE_BINLOG; } else { char *pTmpData = new char[iBuffLen]; char *pReserve = new char[iBuffLen]; ssize_t iReserveLen = 0; ssize_t iDataFlag; ssize_t iDataLen = Get(pKey,KEYSIZE, pTmpData,iBuffLen,iDataFlag, pReserve,iBuffLen,iReserveLen); ssize_t iRet = _WriteDataLog(op_set,pKey,KEYSIZE,pTmpData,iDataLen,iDataFlag,pReserve,iReserveLen); delete []pTmpData; delete []pReserve; if(iRet != 0) return E_WRITE_BINLOG; } } } return 0; }
//return 0 ssize_t MemKeyCache::_Set(const char* pKey,const ssize_t KEYSIZE, char* szData,ssize_t iDataSize,ssize_t iDataFlag,ssize_t &iOldDataFlag, char* pReserve,ssize_t iReserveLen) { if (!pKey || !szData || KEYSIZE<=0 || KEYSIZE>=MAX_KEY_LEN) return -1; ssize_t iEncodeBytes = EncodeSize(KEYSIZE,iReserveLen,iDataSize); if(!m_BuffMng.HaveFreeSpace(iEncodeBytes)) return E_NO_SPACE; iOldDataFlag = 0; ssize_t iNodeIdx = GetNodeIdx(pKey,KEYSIZE); if( iNodeIdx < 0 ) { size_t unHashIdx = MemMakeHashKey((char*)pKey,(u_int32_t)KEYSIZE); unHashIdx %= m_pMemCacheHead->m_iBucketNum; ssize_t iNewNodeIdx = m_NodeObjMng.CreateObject(); if( iNewNodeIdx < 0 ) { printf("ERR:%s NodeObjMng Create Object failed![%s:%d]\n",__FUNCTION__,__FILE__,__LINE__); return E_NO_SPACE; } m_NodeObjMng.SetDsIdx(iNewNodeIdx,m_pMemCacheHead->m_iDSSuffix,m_piBucket[unHashIdx]); if(m_piBucket[unHashIdx] == -1) m_pMemCacheHead->m_iBucketUsed++; m_piBucket[unHashIdx] = iNewNodeIdx; iNodeIdx = iNewNodeIdx; } else { iOldDataFlag = GetFlag(iNodeIdx); } if(iOldDataFlag != F_DATA_CLEAN) { m_NodeWLruQueue.DeleteItem(iNodeIdx); } else { m_NodeRLruQueue.DeleteItem(iNodeIdx); } if(iDataFlag != F_DATA_CLEAN) { m_NodeWLruQueue.AppendToTail(iNodeIdx); } else { m_NodeRLruQueue.AppendToTail(iNodeIdx); } EncodeBufferFormat(m_pBuffer,BUFFSIZE,iDataFlag,KEYSIZE,pKey,iReserveLen,pReserve,iDataSize,szData); return m_BuffMng.SetBuffer(iNodeIdx,m_pBuffer,iEncodeBytes); }
TCharNode *CCharShape::GetNode (size_t node_name) { size_t idx = GetNodeIdx (node_name); if (idx >= numNodes) return NULL; return Nodes[idx]; }
TCharNode *CCharShape::GetNode (int node_name) { int idx = GetNodeIdx (node_name); if (idx < 0 || idx >= numNodes) return NULL; return Nodes[idx]; }