/* SPDX-License-Identifier: MIT */ /* * kon2d.c * * Copyright (c) 2026 KonAki * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "kon2d.h" #include /*** camera ***/ static float clampAxis(float value, float view, float bounds) { if (bounds <= view) return (bounds - view) * 0.5f; if (value < 0.0f) return 0.0f; if (value > bounds - view) return bounds - view; return value; } void kon_camera2Init(kon_camera2_t *cam, int viewWidth, int viewHeight, float boundsWidth, float boundsHeight) { cam->x = cam->y = 0.0f; cam->viewWidth = viewWidth; cam->viewHeight = viewHeight; cam->boundsWidth = boundsWidth; cam->boundsHeight = boundsHeight; cam->stiffness = 8.0f; cam->x = clampAxis(0.0f, (float)viewWidth, boundsWidth); cam->y = clampAxis(0.0f, (float)viewHeight, boundsHeight); } void kon_camera2Snap(kon_camera2_t *cam, float targetX, float targetY) { cam->x = clampAxis(targetX - (float)cam->viewWidth * 0.5f, (float)cam->viewWidth, cam->boundsWidth); cam->y = clampAxis(targetY - (float)cam->viewHeight * 0.5f, (float)cam->viewHeight, cam->boundsHeight); } void kon_camera2Follow(kon_camera2_t *cam, float targetX, float targetY, float dt) { float wantX = clampAxis(targetX - (float)cam->viewWidth * 0.5f, (float)cam->viewWidth, cam->boundsWidth); float wantY = clampAxis(targetY - (float)cam->viewHeight * 0.5f, (float)cam->viewHeight, cam->boundsHeight); float ease = 1.0f; if (cam->stiffness > 0.0f && dt > 0.0f) ease = 1.0f - expf(-cam->stiffness * dt); cam->x += (wantX - cam->x) * ease; cam->y += (wantY - cam->y) * ease; } int kon_camera2X(const kon_camera2_t *cam) { return (int)floorf(cam->x + 0.5f); } int kon_camera2Y(const kon_camera2_t *cam) { return (int)floorf(cam->y + 0.5f); } /*** animation ***/ void kon_animPlay(kon_anim_t *anim, int first, int count, float fps, int loop) { if (anim->first == first && anim->count == count && anim->fps == fps && anim->loop == loop) return; anim->first = first; anim->count = count; anim->fps = fps; anim->loop = loop; anim->time = 0.0f; } void kon_animStep(kon_anim_t *anim, float dt) { if (anim->count <= 0 || !(dt > 0.0f)) return; anim->time += dt; if (anim->loop) { float length = (float)anim->count / anim->fps; if (anim->fps > 0.0f && anim->time >= length) anim->time = fmodf(anim->time, length); } } int kon_animFrame(const kon_anim_t *anim) { int n; if (anim->count <= 0) return anim->first; if (!(anim->fps > 0.0f)) return anim->first; n = (int)(anim->time * anim->fps); if (n >= anim->count) n = anim->loop ? n % anim->count : anim->count - 1; return anim->first + n; } int kon_animDone(const kon_anim_t *anim) { if (anim->loop || anim->count <= 0) return 0; return anim->fps > 0.0f && (int)(anim->time * anim->fps) >= anim->count; } void kon_drawFrame(kon_framebuffer_t *fb, const kon_image *sheet, int frameWidth, int frameHeight, int index, int x, int y, int flip) { kon_rect_t src = kon_frameRect(sheet, frameWidth, frameHeight, index); if (src.w <= 0) return; if (!flip) { kon_drawImageRegion(fb, x, y, sheet, src); return; } kon_drawImageEx(fb, sheet, src, (float)x + (float)frameWidth * 0.5f, (float)y + (float)frameHeight * 0.5f, (float)frameWidth * 0.5f, (float)frameHeight * 0.5f, -1.0f, 1.0f, 0.0f, KON_FILTER_NEAREST); } /*** tweens ***/ float kon_easeValue(kon_ease_t ease, float t) { if (!(t > 0.0f)) return 0.0f; if (t >= 1.0f) return 1.0f; switch (ease) { case KON_EASE_IN: return t * t * t; case KON_EASE_OUT: return 1.0f - (1.0f - t) * (1.0f - t) * (1.0f - t); case KON_EASE_INOUT: return t < 0.5f ? 4.0f * t * t * t : 1.0f - 4.0f * (1.0f - t) * (1.0f - t) * (1.0f - t); case KON_EASE_LINEAR: break; } return t; } void kon_tweenSet(kon_tween_t *tween, float value) { tween->from = tween->to = value; tween->duration = tween->time = 0.0f; tween->ease = KON_EASE_LINEAR; } void kon_tweenStart(kon_tween_t *tween, float from, float to, float seconds, kon_ease_t ease) { tween->from = from; tween->to = to; tween->ease = ease; tween->time = 0.0f; tween->duration = seconds > 0.0f ? seconds : 0.0f; } void kon_tweenTo(kon_tween_t *tween, float to, float seconds, kon_ease_t ease) { kon_tweenStart(tween, kon_tweenValue(tween), to, seconds, ease); } float kon_tweenStep(kon_tween_t *tween, float dt) { if (dt > 0.0f && tween->time < tween->duration) { tween->time += dt; if (tween->time > tween->duration) tween->time = tween->duration; } return kon_tweenValue(tween); } float kon_tweenValue(const kon_tween_t *tween) { if (!(tween->duration > 0.0f)) return tween->to; return tween->from + (tween->to - tween->from) * kon_easeValue(tween->ease, tween->time / tween->duration); } int kon_tweenDone(const kon_tween_t *tween) { return tween->time >= tween->duration; }