/* * LaGUI: A graphical application framework. * Copyright (C) 2022-2023 Wu Yiming * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "la_5.h" extern LA MAIN; int la_GetKeyablePropertyStorageSize(laProp* p); laAction* laAnimiationNewAction(char* Name){ laAction* aa=memAcquire(sizeof(laAction)); if(!Name || !Name[0]){ Name="New Action"; } strSafeSet(&aa->Name,Name); aa->Length=5; aa->FrameCount=120; memAssignRef(MAIN.Animation,&MAIN.Animation->CurrentAction,aa); lstAppendItem(&MAIN.Animation->Actions,aa); laNotifyUsers("la.animation.actions"); return aa; } laAnimationChannel* laAnimationEnsureChannel(laAction* aa, void* hyper1, laProp* p){ laAnimationChannel* acf=0; int DataSize=la_GetKeyablePropertyStorageSize(p); if(!DataSize) return 0; for(laAnimationChannel* ac=aa->Channels.pFirst;ac;ac=ac->Item.pNext){ if(ac->For==hyper1 && ac->Prop==p){ acf=ac; break; } } if(acf) return acf; acf=memAcquire(sizeof(laAnimationChannel)); memAssignRef(acf,&acf->For,hyper1); acf->Prop=p; acf->DataSize = DataSize; lstAppendItem(&aa->Channels,acf); return acf; } laAnimationChannel* laAnimationEnsureFrame(laAnimationChannel* ac, int frame){ laAnimationKey* akf=0,*beforeakf=0; for(laAnimationKey* ak=ac->Keys.pFirst;ak;ak=ak->Item.pNext){ if(ak->At==frame){ akf=ak; break; } if(ak->At>frame){ beforeakf=ak; break; } } if(!akf){ akf=memAcquireSimple(sizeof(laAnimationKey)-sizeof(uint64_t)+ac->DataSize); akf->At=frame; if(beforeakf){ lstInsertItemBefore(&ac->Keys, akf, beforeakf); } lstAppendItem(&ac->Keys, akf); } return akf; } void laAnimationStoreKeyValue(laAnimationChannel* ac, laAnimationKey* ak){ laPropPack PP={0}; laPropStep PS={0}; PS.p=ac->Prop; PS.Type='.'; PS.UseInstance=ac->For; PP.LastPs=&PS; PP.EndInstance=ac->For; switch(ac->Prop->PropertyType){ case LA_PROP_INT: case LA_PROP_INT | LA_PROP_ARRAY: laGetIntArray(&PP,(int*)&ak->Data); case LA_PROP_FLOAT: case LA_PROP_FLOAT | LA_PROP_ARRAY: laGetFloatArray(&PP,(real*)&ak->Data); case LA_PROP_ENUM: case LA_PROP_ENUM | LA_PROP_ARRAY: laGetEnumArray(&PP,(laEnumItem**)&ak->Data); case LA_PROP_SUB: case LA_PROP_OPERATOR: case LA_PROP_STRING: case LA_PROP_RAW: default: return; } } laAnimationKey* laAnimationInsertKeyFrame(laAction* aa, void* hyper1, laProp* p){ laAnimationChannel* ac=laAnimationEnsureChannel(aa,hyper1,p); if(!ac || !ac->For) return; int frame=(aa->PlayHead*(real)aa->FrameCount+0.5); laAnimationKey* ak=laAnimationEnsureFrame(ac->DataSize,frame); laAnimationStoreKeyValue(ac,ak); } void laAnimationSetPlayStatus(int PlayStatus){ if(PlayStatus>3||PlayStatus<0) PlayStatus=0; MAIN.Animation->PlayStatus=PlayStatus; if(PlayStatus) laRecordTime(&MAIN.Animation->TimeOrigin); laNotifyUsers("la.animation.play_status"); } void laAnimationSetPlayHead(real time){ MAIN.Animation->PlayHead=time; } int OPINV_AnimationNewAction(laOperator *a, laEvent *e){ laAnimiationNewAction(0); return LA_FINISHED; } int OPINV_AnimationPlayAction(laOperator *a, laEvent *e){ char* str=strGetArgumentString(a->ExtraInstructionsP, "mode"); int PlayStatus=LA_ANIMATION_STATUS_PAUSED; if(strSame(str,"forward")){ PlayStatus=LA_ANIMATION_STATUS_PLAY_FWD; } elif(strSame(str,"reverse")){ if(MAIN.Animation->PlayHead>0) PlayStatus=LA_ANIMATION_STATUS_PLAY_REV; } laAnimationSetPlayStatus(PlayStatus); return LA_FINISHED; } int OPINV_AnimationResetTime(laOperator *a, laEvent *e){ laAnimationSetPlayHead(0); return LA_FINISHED; } void la_AnimationEvaluateActions(){ int any=0; for(laAction* aa=MAIN.Animation->Actions.pFirst;aa;aa=aa->Item.pNext){ real UseTime=MAIN.Animation->PlayHead-aa->Offset; int repeats=UseTime/aa->Length; real remaining=UseTime-repeats*aa->Length; while(remaining<0){ remaining+=aa->Length; } if(aa->PlayMode==LA_ANIMATION_PLAY_MODE_REPEAT){ aa->PlayHead=remaining/aa->Length; } elif(aa->PlayMode==LA_ANIMATION_PLAY_MODE_HOLD){ aa->PlayHead=(UseTime>aa->Length)?1.0:(UseTime<0?0:UseTime/aa->Length); } elif(aa->PlayMode==LA_ANIMATION_PLAY_MODE_BOUNCE){ real t=remaining/aa->Length; aa->PlayHead=(repeats%2)?(1-t):t; } any=1; } if(any) laNotifyUsers("la.animation"); } void la_AnimationPreFrame(){ if(MAIN.Animation->PlayHead<0){ MAIN.Animation->PlayHead=0; laAnimationSetPlayStatus(0); laNotifyUsers("la.animation.play_head"); } if(MAIN.Animation->PlayStatus){ la_AnimationEvaluateActions(); } } void la_AnimationPostFrame(){ laTimeRecorder cur={0}; if(MAIN.Animation->PlayStatus){ laRecordTime(&cur); real td=laTimeElapsedSecondsf(&cur,&MAIN.Animation->TimeOrigin); laRecordTime(&MAIN.Animation->TimeOrigin); if(MAIN.Animation->PlayStatus==LA_ANIMATION_STATUS_PLAY_REV){ td*=-1; } MAIN.Animation->PlayHead+=td; laNotifyUsers("la.animation.play_head"); } } int LAMOD_AnimationActionsCanvas(laOperator *a, laEvent *e){ laUiItem *ui = a->Instance; laBoxedTheme *bt = (*ui->Type->Theme); laCanvasExtra *ex = a->CustomData; laAction* aa=ui->PP.EndInstance; if(!aa) LA_RUNNING_PASS; return LA_RUNNING_PASS; } void la_AnimationActionCanvasInit(laUiItem *ui){ la_CanvasInit(ui); laCanvasExtra* e=ui->Extra; e->LW=LA_RH*7; e->ShowLegend=1; } void la_AnimationActionDrawCanvas(laBoxedTheme *bt, laAction *aa, laUiItem* ui){ laCanvasExtra* e=ui->Extra; //laAction* aa=ui->PP.EndInstance; int W, H; W = ui->R - ui->L; H = ui->B - ui->U; if (W<=0 || H<=0) return; int ShowSide=(e->ShowLegend&&e->LWL, lr=ll+(ShowSide?e->LW:0), tl=lr, tr=ui->R; //if (!e->OffScr || e->OffScr->pColor[0]->Height != ui->B - ui->U || e->Be.OffScr->pColor[0]->Width != ui->R - ui->L){ // if (e->OffScr) tnsDelete2DOffscreen(e->OffScr); // e->OffScr = tnsCreate2DOffscreen(GL_RGBA, W, H, MAIN.PanelMultisample, 1, 0); //} tnsUseNoTexture(); tnsColor4dv(laThemeColor(bt,LA_BT_BORDER)); tnsVertex2d(ui->L, ui->U); tnsVertex2d(ui->R, ui->U); tnsVertex2d(ui->R, ui->B); tnsVertex2d(ui->L, ui->B); tnsPackAs(GL_LINE_LOOP); if(ShowSide){ tnsColor4dv(laThemeColor(bt,LA_BT_NORMAL)); tnsVertex2d(ll, ui->U); tnsVertex2d(lr, ui->U); tnsVertex2d(lr, ui->B); tnsVertex2d(ll, ui->B); tnsPackAs(GL_TRIANGLE_FAN); } } void la_AnimationActionDrawOverlay(laUiItem *ui, int h){ laCanvasExtra *e = ui->Extra; laBoxedTheme *bt = (*ui->Type->Theme); tnsUseImmShader(); if(MAIN.CurrentWindow->MaximizedUi!=ui){ tnsUseNoTexture(); tnsColor4dv(laThemeColor(bt,LA_BT_BORDER)); tnsVertex2d(ui->L, ui->U); tnsVertex2d(ui->R, ui->U); tnsVertex2d(ui->R, ui->B); tnsVertex2d(ui->L, ui->B); tnsPackAs(GL_LINE_LOOP); } if (e->DrawCursor){ tnsColor4dv(laThemeColor(bt,LA_BT_TEXT)); int drawx=(e->DrawCursor==LA_CANVAS_CURSOR_CROSS)||(e->DrawCursor==LA_CANVAS_CURSOR_X); int drawy=(e->DrawCursor==LA_CANVAS_CURSOR_CROSS)||(e->DrawCursor==LA_CANVAS_CURSOR_Y); if(drawx) tnsVertex2d(e->OnX, ui->U); tnsVertex2d(e->OnX, ui->B); if(drawy) tnsVertex2d(ui->L, e->OnY); tnsVertex2d(ui->R, e->OnY); tnsPackAs(GL_LINES); tnsFlush(); } return; } void la_RegisterAnimationResources(){ laPropContainer *pc; laProp *p; laOperatorType *at; laEnumProp *ep; laCreateOperatorType("LA_animation_new_action", "New Action", "Add a new action",0,0,0,OPINV_AnimationNewAction,0,U'🞦',0); laCreateOperatorType("LA_animation_set_play_status", "Set Play", "Set global animation player status",0,0,0,OPINV_AnimationPlayAction,0,0,0); laCreateOperatorType("LA_animation_reset_time", "Reset Time", "Reset Time",0,0,0,OPINV_AnimationResetTime,0,U'🡄',0); laCanvasTemplate* ct=laRegisterCanvasTemplate("la_AnimationActionDrawCanvas", "la_animation_action", LAMOD_AnimationActionsCanvas, la_AnimationActionDrawCanvas, la_AnimationActionDrawOverlay, la_AnimationActionCanvasInit, la_CanvasDestroy); pc = laCanvasHasExtraProps(ct,sizeof(laCanvasExtra),2); laAddIntProperty(pc, "height_coeff", "Ui Height", "Ui Height Coefficiency Entry", 0, 0, "Rows", 100, -100, 1, 0, 0, offsetof(laCanvasExtra, HeightCoeff), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); }