From 013356bdbe25653a39cfcc6fe128766fd5c8efb7 Mon Sep 17 00:00:00 2001 From: midiphony Date: Mon, 6 Oct 2025 20:45:00 +0200 Subject: [PATCH] Add shapes_math_sine_cosine example --- examples/shapes/shapes_math_sine_cosine.c | 204 ++++++++++++++++++++ examples/shapes/shapes_math_sine_cosine.png | Bin 0 -> 15050 bytes 2 files changed, 204 insertions(+) create mode 100644 examples/shapes/shapes_math_sine_cosine.c create mode 100644 examples/shapes/shapes_math_sine_cosine.png diff --git a/examples/shapes/shapes_math_sine_cosine.c b/examples/shapes/shapes_math_sine_cosine.c new file mode 100644 index 000000000..1cc3b53dc --- /dev/null +++ b/examples/shapes/shapes_math_sine_cosine.c @@ -0,0 +1,204 @@ +/******************************************************************************************* +* +* raylib [shapes] example - math sine cosine +* +* Example complexity rating: [★☆☆☆] 1/4 +* +* Example originally created with raylib 5.6, last time updated with raylib 5.6 +* +* Example contributed by Midiphony (@midiphony) and reviewed by Ramon Santamaria (@raysan5) +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2025-2025 Zero (@zerohorsepower) +* +********************************************************************************************/ + +#include "raylib.h" +#include "raymath.h" // Required for: Vector2 operations and Clamp() +#include // Required for: malloc(), free() +#include // Required for: cosf(), sinf() + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const Color cosineColor = RED; + const Color sineColor = ORANGE; + const int pointsSize = 6; + const int lineThickness = 2; + + const int screenWidth = 800; + const int screenHeight = 450; + + // Circle + const int circleX = screenWidth/4 - 20; + const int circleY = screenHeight/2; + const Vector2 circlePosition = { circleX, circleY }; + const int circleRadius = 140; + const int circleLeft = circleX - circleRadius; + const int circleRight = circleX + circleRadius; + const int circleTop = circleY - circleRadius; + const int circleBottom = circleY + circleRadius; + + const int circleTextFontSize = 20; + + // Graph + const int graphLeft = screenWidth/2; + const int graphRight = 750; + const int graphHeight = 200; + const int graphHalfHeight = graphHeight/2; + const int graphYMiddle = screenHeight/2; + const int graphTop = graphYMiddle - graphHalfHeight; + const int graphBottom = graphYMiddle + graphHalfHeight; + const int graphWidth = graphRight - graphLeft; + + const int graphTextFontSize = 20; + const int graphTextPadding = 10; + + Vector2* cosineCurvePoints = (Vector2*)malloc(graphWidth*sizeof(Vector2)); // Points array + Vector2* sineCurvePoints = (Vector2*)malloc(graphWidth*sizeof(Vector2)); // Points array + + // Initialize cosine curve + for (int x = 0; x < graphWidth; x++) + { + float y = -cosf(((float)x/graphWidth)*2.0*PI); + int yCoord = graphYMiddle + y*graphHalfHeight; + cosineCurvePoints[x] = (Vector2){ graphLeft + x, yCoord }; + } + + // Initialize sine curve + for (int x = 0; x < graphWidth; x++) + { + float y = sinf(((float)x/graphWidth)*2.0*PI); + int yCoord = graphYMiddle - y*graphHalfHeight; + sineCurvePoints[x] = (Vector2){ graphLeft + x, yCoord }; + } + + const int windowSplitX = ((circleX + circleRadius) + graphLeft)/2; + + InitWindow(screenWidth, screenHeight, "raylib [shapes] example - math sine cosine"); + + const int circleTextMaxLength = MeasureText("-1.000", circleTextFontSize); + const int graphTextMaxLength = MeasureText("-1.000", graphTextFontSize); + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + Vector2 mousePosition = GetMousePosition(); + float angleInDegrees = 0.0f; + float angle = 0; + + if (mousePosition.x <= windowSplitX) // Calculate angle relative to the circle + { + angle = Vector2Angle(Vector2Subtract(mousePosition, circlePosition), (Vector2) { 1, 0 }); + if (angle < 0.0f) + { + angle += 2*PI; + } + } + else // Calculate angle relative to the graph + { + angle = Clamp((mousePosition.x - graphLeft)*2*PI/graphWidth, 0.0f, 2*PI); + } + + angleInDegrees = angle*RAD2DEG; + + float cosine = cosf(angle); + float sine = sinf(angle); + int pointX = circleX + circleRadius*cosine; + int pointY = circleY - circleRadius*sine; + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + // Draw top angle label + DrawText(TextFormat("Angle:%.1f", angleInDegrees), 20, 20, 30, GRAY); + + // Trigonometry circle + // -------------------- + DrawRing(circlePosition, circleRadius - lineThickness/2, circleRadius + lineThickness/2, 0, 360, 0, GRAY); // 0 ring segment to let DrawRing choose a number of segments giving a smooth circle + DrawLineEx((Vector2) { circleLeft, circleY }, (Vector2) { circleRight, circleY }, lineThickness, GRAY); + DrawLineEx((Vector2) { circleX, circleTop }, (Vector2) { circleX, circleBottom }, lineThickness, GRAY); + + DrawCircleSectorLines(circlePosition, circleRadius/3, 0, -angleInDegrees, 0, BLUE); + + // Draw line to point + DrawLine(circleX, circleY, pointX, pointY, GRAY); + + // Draw cosine point + DrawLineEx((Vector2) { circleX, circleY }, (Vector2) { pointX, circleY }, lineThickness, cosineColor); + DrawText(TextFormat("%.3f", cosine), ((pointX + circleX)/2) - circleTextMaxLength/2, circleY + 2, circleTextFontSize, cosineColor); + // Draw sine point + DrawLineEx((Vector2) { pointX, circleY }, (Vector2) { pointX, pointY }, lineThickness, sineColor); + DrawText(TextFormat("%.3f", sine), pointX + 5, (pointY + circleY)/2 - circleTextFontSize/2, circleTextFontSize, sineColor); + + // Draw point + DrawCircle(pointX, pointY, pointsSize, BLACK); + // -------------------- + + // Window split + DrawLine(windowSplitX, 0, windowSplitX, screenHeight - 1, GRAY); + + // Graph + // -------------------- + // Draw graph borders + DrawLineEx((Vector2) { graphLeft, graphTop }, (Vector2) { graphLeft, graphBottom }, 2, GRAY); + DrawLineEx((Vector2) { graphRight, graphTop }, (Vector2) { graphRight, graphBottom }, 2, GRAY); + DrawLineEx((Vector2) { graphLeft, graphYMiddle }, (Vector2) { graphRight, graphYMiddle }, 2, GRAY); + + // Draw graph outer texts + DrawText("1", graphLeft - graphTextPadding - MeasureText("1", graphTextFontSize), graphTop - graphTextFontSize/2, graphTextFontSize, GRAY); + DrawText("0", graphLeft - graphTextPadding - MeasureText("0", graphTextFontSize), graphYMiddle - graphTextFontSize/2, graphTextFontSize, GRAY); + DrawText("-1", graphLeft - graphTextPadding - MeasureText("-1", graphTextFontSize), graphBottom - graphTextFontSize/2, graphTextFontSize, GRAY); + DrawText("0", graphLeft - MeasureText("0", graphTextFontSize)/2, graphBottom + graphTextPadding/2, graphTextFontSize, GRAY); + DrawText("360", graphRight - MeasureText("360", graphTextFontSize)/2, graphBottom + graphTextPadding/2, graphTextFontSize, GRAY); + + // Draw cosine curve + DrawSplineLinear(cosineCurvePoints, graphWidth, 2, cosineColor); + DrawText("cos", graphRight + graphTextPadding, cosineCurvePoints[graphWidth - 1].y - graphTextFontSize/2, graphTextFontSize, cosineColor); + + // Draw sine curve + DrawSplineLinear(sineCurvePoints, graphWidth, 2, sineColor); + DrawText("sin", graphRight + graphTextPadding, sineCurvePoints[graphWidth - 1].y - graphTextFontSize/2, graphTextFontSize, sineColor); + + // Draw graph progress line + int x = graphLeft + graphWidth*angleInDegrees/360.0f; + DrawLine(x, graphBottom, x, graphTop, BLUE); + + // Draw cosine and sine points on graph + int cosineY = graphYMiddle - cosine*graphHalfHeight; + int sineY = graphYMiddle - sine*graphHalfHeight; + DrawCircle(x, cosineY, pointsSize, cosineColor); + DrawText(TextFormat("%.3f", cosine), x - circleTextMaxLength/2, cosineY - circleTextFontSize - 5, circleTextFontSize, cosineColor); + DrawCircle(x, sineY, pointsSize, sineColor); + DrawText(TextFormat("%.3f", sine), x - circleTextMaxLength/2, sineY + 8, circleTextFontSize, sineColor); + // -------------------- + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + + free(cosineCurvePoints); + free(sineCurvePoints); + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/shapes/shapes_math_sine_cosine.png b/examples/shapes/shapes_math_sine_cosine.png new file mode 100644 index 0000000000000000000000000000000000000000..4bac572fae67e2a1f5e53b0442975ab0d4c14ba0 GIT binary patch literal 15050 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 literal 0 HcmV?d00001