Простой односвязный список на С

от автора

Всем привет. В 3д помимо моделек — статических, существуют анимации — анимированные модели, которые имеют набор своих данных, эти данные нужны для отображения модельки и её анимирования.

Если выбрать такой язык как С, по каким-либо причинам, рано или поздно можно столкнуться с отсутствием некоторых сущностей. Самой часто используемой сущностью является vector. Если оттолкнуться от того, что простейшая абстракция вектор, и еще чуть упростить можно придти логически к *односвязному списку. Но вот незадача, если пребывать на этом этапе по наименьшему сопротивлению, то придётся на каждую структуру писать свою реализацию это в худшем случае. В этой статье хочу показать как решил вопрос с односвязным списком.

Постановка задачи

**Скелетная анимация — есть моделька,в которой есть сетка — mesh(состоит из полигонов) и скелетная составляющая.

выглядит она например так

Скрытый текст
тестовая прогонка меша и скелетной составляющей не в блендере

тестовая прогонка меша и скелетной составляющей не в блендере

При работе по сбору данных из анимированной модельки в момент её инициализации, при длительном подходе, я выявил следующую задачу: нужна такая сущность, в которую можно добавлять в конец данные. Количество данных в момент первых обходов могут быть не известны. Тоесть, например, чтобы проинициализировать массивы вершин, их можно просто узнать сколько их, когда. В тоже время, иерархия костной составляющей не так тривиальна. Поэтому, на первом масштабном приближении, я пришел к односвязному списку на костную составляющую.

Так же, как опция я принял решение, что тип будет кастомный — не известен как будто, так же понадобится возможность поиска по индексу и вставка в следующий за найденым индексом операции.

И окончательное требование — соглашение на 1 сущность 1 тип данных.

Пользуюсь компилятором gcc 14.2 в среде Linux, assimp из репозитория

Задача поставлена, решил её так:

Скрытый текст
//язык С #include <stdio.h> #include <stdlib.h> #include <string.h> #pragma pack(1)  //кастомная тестовая структура struct EpicLEgendaryNodes_t {     char *name;     int Num;     float weights; }; typedef struct EpicLEgendaryNodes_t EpicLEgendaryNodes;  //определение типов enum typeElements  {     INT,     CHAR,     DOUBLE,     FLOAT,     CHARLINE,     EPICLEGENDARYNODES };  //структура списка typedef struct NodeL_t NodeL; struct NodeL_t  {     enum typeElements T;     void* data;     NodeL* Next; };   //структура хендлера typedef struct VNodeL_t VNodeL; struct VNodeL_t {   int NodeSz;   NodeL *head; };   //определение необходимого минимума функций, которые покроют поставленную задачу NodeL* getNode(VNodeL *vNodeL,enum typeElements T,void* d);  NodeL* addNodeL(void *d,enum typeElements T,VNodeL *vNodeL);  NodeL* FREENodeL(VNodeL *vNodeL);  NodeL* getByIndex(VNodeL *vNodeL,int Index);  NodeL* setByAfterIndex(VNodeL *vNodeL,int Index,void *d,enum typeElements T);  void printList(VNodeL *vNodeL);   //метод получения ноды - создание NodeL* getNode(VNodeL *vNodeL,enum typeElements T,void* d)  {     char* dd=NULL;     NodeL* tmp=NULL;     tmp=malloc(sizeof(NodeL));     switch(T)     {         case INT:             tmp->data=malloc(sizeof(int));             memcpy(tmp->data, d, sizeof(int)); //! использую memcpy             vNodeL->NodeSz++;             break;         case CHAR:             tmp->data=malloc(sizeof(char));             memcpy(tmp->data, d, sizeof(char));             vNodeL->NodeSz++;             break;         case DOUBLE:             tmp->data=malloc(sizeof(double));             memcpy(tmp->data, d, sizeof(double));             vNodeL->NodeSz++;             break;         case FLOAT:             tmp->data=malloc(sizeof(float));             memcpy(tmp->data, d, sizeof(float));             vNodeL->NodeSz++;             break;         case CHARLINE:             dd=d;             size_t szS=strlen(dd)+1;             tmp->data=malloc(sizeof(char)*szS);             ///dd[szS]='\0';             memcpy(tmp->data, d, sizeof(char)*szS);             vNodeL->NodeSz++;             break;         case EPICLEGENDARYNODES:             tmp->data=malloc(sizeof(EpicLEgendaryNodes));             memcpy(tmp->data, d, sizeof(EpicLEgendaryNodes));             vNodeL->NodeSz++;             break;     }     tmp->Next=NULL;     return tmp; }   //добавление в конец ноды NodeL* addNodeL(void *d,enum typeElements T,VNodeL *vNodeL)  {     NodeL* tmp=getNode(vNodeL,T,d);     if( vNodeL->head == NULL )     {         tmp->T=T;         return tmp;     }      NodeL* cur = vNodeL->head;     while( cur->Next != NULL )cur = cur->Next;      cur->Next = tmp;      return vNodeL->head; }  //очищение структуры списка NodeL* FREENodeL(VNodeL *vNodeL) {     NodeL* cur=vNodeL->head;     while( cur != NULL )     {         NodeL* temp=cur;         cur = temp->Next;         free(temp);     }        return vNodeL->head; }   //получение значения по индексу NodeL* getByIndex(VNodeL *vNodeL,int Index) {     if(Index>vNodeL->NodeSz-1)     {       printf("\n\ngetByIndex\nSearchIndex > SizeNodeLists\n");       return 0;     }        int tInd=0;     NodeL *cur=vNodeL->head;        while(cur!=NULL)     {         if(tInd==Index)break;         tInd++;         cur=cur->Next;     }     return cur; }   //вставка в место следующее за заданным индексом NodeL* setByAfterIndex(VNodeL *vNodeL,int Index,void *d,enum typeElements T) {     NodeL *setting=getNode(vNodeL,T,d);     if(Index>vNodeL->NodeSz-1)     {       printf("SearchIndex > SizeNodeLists\n");       return 0;     }        int tInd=0;     NodeL *cur=vNodeL->head;     while(cur!=NULL)     {         if(tInd==Index)break;         tInd++;         cur=cur->Next;     }        NodeL *tmp1=cur->Next;     cur->Next=setting;     setting->Next=tmp1;     return vNodeL->head; }   //вывод списка void printList(VNodeL *vNodeL) {     if(vNodeL->head==NULL){printf("LIST IS EMPTY\n");return ;}     if(vNodeL==NULL){printf("LIST IS EMPTY\n");return ;}     NodeL* cur=vNodeL->head;     enum typeElements Type=vNodeL->head->T;          while(cur!=NULL)     {         switch(Type)         {             case INT:                 printf("%d\n",*(int*)cur->data);                 break;             case CHAR:                 printf("%c\n",*(char*)cur->data);                 break;             case DOUBLE:                 printf("%f\n",*(double*)cur->data);                 break;             case FLOAT:                 printf("%f\n",*(float*)cur->data);                 break;             case CHARLINE:                 printf("%s\n",(char*)cur->data);                 break;             case EPICLEGENDARYNODES:               {                 EpicLEgendaryNodes *temp=(EpicLEgendaryNodes*)cur->data;                 printf("%s\n",temp->name);                 printf("%d\n",temp->Num);                 printf("%f\n",temp->weights);                 break;               }         }         cur=cur->Next;     } }   //типовая прогонка списка 1 тест базовых типов 2 тест кастомной структурки int main() {   //1     VNodeL *tempBuf=malloc(sizeof(VNodeL));     tempBuf->head=NULL;     tempBuf->NodeSz=0;     int t=4;     float qq=0.1f;     double qw=0.1f;     char* strline=(char*)"CHARLINE";     tempBuf->head=addNodeL(strline,CHARLINE,tempBuf);t++;qq++;qw++;     strline="CHARLINE1";     tempBuf->head=addNodeL(strline,CHARLINE,tempBuf);t++;qq++;qw++;     strline="CHARLINE2";     tempBuf->head=addNodeL(strline,CHARLINE,tempBuf);t++;qq++;qw++;     strline="CHARLINE3";     tempBuf->head=addNodeL(strline,CHARLINE,tempBuf);t++;qq++;qw++;      printList(tempBuf);      NodeL* search=getByIndex(tempBuf,4);     printf("%d\n",tempBuf->NodeSz);      tempBuf->head=setByAfterIndex(tempBuf,2,"CHARLINE10",CHARLINE);      search=getByIndex(tempBuf,4);     printf("\nSearched element\n%s\n\n",(char*)search->data);//см в printList     tempBuf->head=FREENodeL(tempBuf);     free(tempBuf);     //tempBuf->head=NULL;     //printList(tempBuf);        //2     VNodeL *tempBuf1=malloc(sizeof(VNodeL));;     tempBuf1->head=NULL;     tempBuf1->NodeSz=0;     EpicLEgendaryNodes temp;     temp.name="CHARLINE111111";     temp.Num=t;     temp.weights=qq;     tempBuf1->head=addNodeL(&temp,EPICLEGENDARYNODES,tempBuf1);t++;qq++;qw++;     temp.name="CHARLINE22221";     temp.Num=t;     temp.weights=qq;     tempBuf1->head=addNodeL(&temp,EPICLEGENDARYNODES,tempBuf1);t++;qq++;qw++;     temp.name="CHARLINE23333";     temp.Num=t;     temp.weights=qq;     tempBuf1->head=addNodeL(&temp,EPICLEGENDARYNODES,tempBuf1);t++;qq++;qw++;     temp.name="CHARLINE34444";     temp.Num=t;     temp.weights=qq;     tempBuf1->head=addNodeL(&temp,EPICLEGENDARYNODES,tempBuf1);     printList(tempBuf1);     tempBuf1->head=FREENodeL(tempBuf1);     free(tempBuf1);     //tempBuf1->head=NULL;     //printList(tempBuf1);     return 0; }

Вывод:

CHARLINE CHARLINE1 CHARLINE2 CHARLINE3   getByIndex SearchIndex > SizeNodeLists 4  Searched element CHARLINE3  CHARLINE111111 8 4.100000 CHARLINE22221 9 5.100000 CHARLINE23333 10 6.100000 CHARLINE34444 11 7.100000

gcc -Ofast -ffast-math -flto -msse4.1 main.c -lm

/nologo /MD /O2 /Ob2 /DNDEBUG — x64! msvc v19.40 VS17.10

Выше представлено то, что получилось. Это, возможно, не лучшая реализация односвязного списка. Но на текущем подходе к скелетной анимации достаточно, теперь можно создавать структуры и заполнять.

При такой организации, конечно придётся хранить структуры рядом с перечислением.

Дополнительная информация:

*https://ru.wikipedia.org/wiki/Скелетная_анимация

**https://en.wikipedia.org/wiki/Linked_list


ссылка на оригинал статьи https://habr.com/ru/articles/882918/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *