#include "raylib.h" #include #define RAYGUI_IMPLEMENTATION #include "raygui.h" #define UI_WIDTH 450 #define COLOR_BG (Color){ 25, 25, 30, 255 } #define COLOR_PANEL (Color){ 40, 40, 45, 255 } #define COLOR_ACCENT (Color){ 0, 120, 240, 255 } #define COLOR_TEXT_DIM (Color){ 180, 180, 190, 255 } #define COLOR_TEXT_SEC (Color){ 160, 160, 170, 255 } typedef struct { float d1; float d2; float lambda; float angleM1; Vector2 center; bool gaz; float nGaz; } Michelson; Color WavelengthToColor(float lambda) { float r = 0.0f, g = 0.0f, b = 0.0f; if (lambda >= 380 && lambda < 440) { r = -(lambda - 440) / (440 - 380); b = 1.0f; } else if (lambda >= 440 && lambda < 490) { g = (lambda - 440) / (490 - 440); b = 1.0f; } else if (lambda >= 490 && lambda < 510) { g = 1.0f; b = -(lambda - 510) / (510 - 490); } else if (lambda >= 510 && lambda < 580) { r = (lambda - 510) / (580 - 510); g = 1.0f; } else if (lambda >= 580 && lambda < 645) { r = 1.0f; g = -(lambda - 645) / (645 - 580); } else if (lambda >= 645 && lambda <= 780) { r = 1.0f; } float factor = 1.0f; if (lambda >= 380 && lambda < 420) factor = 0.3f + 0.7f * (lambda - 380) / (420 - 380); else if (lambda >= 380 && lambda <= 645) factor = 1.0f; else if (lambda > 700 && lambda <= 780) factor = 0.3f + 0.7f * (780 - lambda) / (780 - 700); return (Color){(unsigned char)(r * factor * 255), (unsigned char)(g * factor * 255), (unsigned char)(b * factor * 255), 255}; } void DrawLaserBeam(Vector2 start, Vector2 end, Color color, float thickness) { DrawLineEx(start, end, thickness * 4.0f, Fade(color, 0.15f)); DrawLineEx(start, end, thickness * 2.0f, Fade(color, 0.4f)); DrawLineEx(start, end, thickness, color); } bool GuiButtonRepeat(Rectangle bounds, const char* text, bool shouldRepeat) { bool clicked = GuiButton(bounds, text); bool held = CheckCollisionPointRec(GetMousePosition(), bounds) && IsMouseButtonDown(MOUSE_LEFT_BUTTON) && shouldRepeat; return clicked || held; } /* void DrawInterferenceViewCPU(Michelson *mic, Rectangle rec) { DrawRectangleRec((Rectangle){rec.x - 4, rec.y - 30, rec.width + 8, rec.height + 34}, COLOR_PANEL); DrawText("ECRAN", rec.x, rec.y - 26, 20, COLOR_TEXT_DIM); DrawRectangleRec(rec, BLACK); DrawRectangleLinesEx(rec, 1, Fade(COLOR_ACCENT, 0.3f)); Color baseColor = WavelengthToColor(mic->lambda); float extraPath = 0.0f; if (mic->gaz) extraPath = 2.0f * 100.0f * (mic->nGaz - 1.0f); float d2_effective = mic->d2 + (extraPath / 2.0f); // Diff de marche du à l'axe (2 * e) + micro -> nano float deltaLnm = 2.0f * (mic->d1 - d2_effective) * 1000.0f; int cx = rec.x + rec.width / 2; int cy = rec.y + rec.height / 2; float K = PI / mic->lambda; // Dessin pixel par pixel lent... => faire avec gpu for (int y = rec.y; y < rec.y + rec.height; y++) { for (int x = rec.x; x < rec.x + rec.width; x++) { float relX = (float)(x - cx); float relY = (float)(y - cy); float radiusSq = relX * relX + relY * relY; //if (radiusSq > (rec.width / 2.0f - 5) * (rec.width / 2.0f - 5)) continue; // Diff de marche lamme d'air 2 * e cos(i) mais petits angles => cos(i) ~ 1 - i^2/2 mais i ~ r/f et r^2 = x^2 + y^2 float ringFactor = radiusSq * 0.065f; // Diff de marche dut au coin d'air float wDelta = relX * mic->angleM1 * 200.0f; float currDelta = deltaLnm - ringFactor + wDelta; // Formule de Fresnel : I = I_0 * cos^2(phi) float phase = currDelta * K; float intensity = cosf(phase); intensity = intensity * intensity; Color pixColor = {(unsigned char)(baseColor.r * intensity), (unsigned char)(baseColor.g * intensity), (unsigned char)(baseColor.b * intensity), 255 }; DrawPixel(x, y, pixColor); } } DrawRectangleLinesEx(rec, 2, COLOR_PANEL); // croix centre DrawLine(cx - 10, cy, cx + 10, cy, Fade(WHITE, 0.5f)); DrawLine(cx, cy - 10, cx, cy + 10, Fade(WHITE, 0.5f)); } */ void DrawInterferenceViewGPU(Michelson *mic, Rectangle rec, Shader shader, bool *isFullscreen) { DrawRectangleRec((Rectangle){rec.x - 4, rec.y - 30, rec.width + 8, rec.height + 34}, COLOR_PANEL); DrawText("ECRAN", rec.x, rec.y - 26, 20, COLOR_TEXT_DIM); const char* btnText = *isFullscreen ? "PETIT" : "GRAND"; if (GuiButton((Rectangle){rec.x + rec.width - 80, rec.y - 28, 80, 24}, btnText)) { *isFullscreen = !(*isFullscreen); } if (CheckCollisionPointRec(GetMousePosition(), (Rectangle){rec.x + rec.width - 80, rec.y - 28, 80, 24})) { DrawText("(Touche F)", rec.x + rec.width - 160, rec.y - 22, 10, GRAY); } // Shader glsl float centerX = rec.x + rec.width / 2.0f; float centerY = rec.y + rec.height / 2.0f; float center[2] = { centerX, centerY }; float scrHeight = (float)GetScreenHeight(); float zoom = *isFullscreen ? 0.7 : 1; float addedOpticalPathOneWay = 0.0f; if (mic->gaz) { addedOpticalPathOneWay = 100.0f * (mic->nGaz - 1.0f); } float d2_effective = mic->d2 + addedOpticalPathOneWay; SetShaderValue(shader, GetShaderLocation(shader, "center"), center, SHADER_UNIFORM_VEC2); SetShaderValue(shader, GetShaderLocation(shader, "screenHeight"), &scrHeight, SHADER_UNIFORM_FLOAT); SetShaderValue(shader, GetShaderLocation(shader, "d1"), &mic->d1, SHADER_UNIFORM_FLOAT); SetShaderValue(shader, GetShaderLocation(shader, "d2"), &d2_effective, SHADER_UNIFORM_FLOAT); SetShaderValue(shader, GetShaderLocation(shader, "lambda"), &mic->lambda, SHADER_UNIFORM_FLOAT); SetShaderValue(shader, GetShaderLocation(shader, "angleM1"), &mic->angleM1, SHADER_UNIFORM_FLOAT); SetShaderValue(shader, GetShaderLocation(shader, "zoom"), &zoom, SHADER_UNIFORM_FLOAT); // GPU BeginShaderMode(shader); DrawRectangleRec(rec, WHITE); EndShaderMode(); DrawRectangleLinesEx(rec, 1, Fade(COLOR_ACCENT, 0.3f)); DrawRectangleLinesEx(rec, 2, COLOR_PANEL); int cx = rec.x + rec.width / 2; int cy = rec.y + rec.height / 2; DrawLine(cx - 10, cy, cx + 10, cy, Fade(WHITE, 0.5f)); DrawLine(cx, cy - 10, cx, cy + 10, Fade(WHITE, 0.5f)); } void DrawMichelsonSchema (Michelson *mic) { Vector2 c = mic->center; Color laserColor = WavelengthToColor(mic->lambda); float thickness = 3.0f; float sourceLen = 450.0f; float screenLen = 450.0f; float mirrorSize = 140.0f; // Laser BeginBlendMode(BLEND_ADDITIVE); // Source Vector2 startSource = {c.x - sourceLen, c.y}; DrawLaserBeam(startSource, c, laserColor, thickness); // Miroir M2 (fixe) Vector2 posM2 = {c.x + mic->d2, c.y}; DrawLaserBeam(c, posM2, laserColor, thickness); // Miroir M1 (mobile) Vector2 posM1 = {c.x, c.y - mic->d1}; DrawLaserBeam(c, posM1, laserColor, thickness); // Projection rayon sur écran (corection tangente) float angleRad = -(mic->angleM1 * 2.0f) * (PI / 180.0f); float yScreen = c.y + screenLen; float vertDist = yScreen - posM1.y; float xOff = vertDist * tanf(angleRad); Vector2 endPointRetour = { posM1.x + xOff, yScreen }; DrawLaserBeam(posM1, endPointRetour, Fade(laserColor, 0.6f), thickness); Vector2 endPointM2 = {c.x, c.y + screenLen}; DrawLaserBeam(c, endPointM2, Fade(laserColor, 0.5f), thickness); EndBlendMode(); // Source DrawRectangle(startSource.x - 30, startSource.y - 20, 30, 40, DARKGRAY); DrawRectangleLines(startSource.x - 30, startSource.y - 20, 30, 40, GRAY); DrawText("Source", startSource.x - 50, startSource.y - 50, 20, LIGHTGRAY); if (mic->gaz) { float cellDist = 80.0f; float cellW = 60.0f; float cellH = 40.0f; Rectangle cellRect = {c.x + cellDist, c.y - cellH / 2, cellW, cellH}; DrawRectangleRec(cellRect, Fade(BLUE, 0.15f + (mic->nGaz - 1.0f) * 2.0f)); DrawRectangleLinesEx(cellRect, 2, SKYBLUE); DrawText(TextFormat("n=%.3f", mic->nGaz), c.x + cellDist, c.y + 25, 10, SKYBLUE); } // M2 DrawRectangle(posM2.x, posM2.y - (mirrorSize / 2), 12, mirrorSize, LIGHTGRAY); DrawRectangle(posM2.x, posM2.y - (mirrorSize / 2) + 2, 4, mirrorSize - 4, WHITE); DrawText("M2", posM2.x + 20, posM2.y - 10, 20, GRAY); // M1 Rectangle recM1 = {posM1.x, posM1.y, mirrorSize, 12.0f}; Vector2 originM1 = {mirrorSize / 2, 6.0f}; DrawRectanglePro(recM1, originM1, mic->angleM1, LIGHTGRAY); Rectangle recM1Face = {posM1.x, posM1.y, mirrorSize - 4, 4.0f}; Vector2 originM1Face = {(mirrorSize - 4) / 2, 6.0f}; DrawRectanglePro(recM1Face, originM1Face, mic->angleM1, WHITE); DrawText("M1", posM1.x - 40, posM1.y - 30, 20, ORANGE); // Séparatrice float sepLen = 220.0f; Rectangle sepRec = {c.x, c.y, 6.0f, sepLen}; Vector2 sepOrigin = {2.0f, sepLen / 2}; DrawRectanglePro(sepRec, sepOrigin, -45.0f, Fade(SKYBLUE, 0.4f)); DrawRectanglePro((Rectangle){c.x, c.y, 2.0f, sepLen}, (Vector2){1.0f, sepLen / 2}, -45.0f, Fade(WHITE, 0.4f)); DrawRectangle(c.x - 300, yScreen, 600, 15, COLOR_PANEL); DrawText("Ecran", c.x - 20, yScreen + 20, 20, COLOR_TEXT_DIM); } void DrawControlPanel(Michelson *mic) { DrawRectangle(0, 0, UI_WIDTH, GetScreenHeight(), Fade(COLOR_PANEL, 0.95f)); DrawLine(UI_WIDTH, 0, UI_WIDTH, GetScreenHeight(), Fade(WHITE, 0.1f)); static int holdTimer = 0; if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) holdTimer++; else holdTimer = 0; bool shouldRepeat = (holdTimer > 30 && (holdTimer % 3 == 0)); // Répète / 3 frames int startX = 20; int startY = 30; int contentWidth = UI_WIDTH - 40; int btnSize = 30; int sliderWidth = contentWidth - (btnSize * 2) - 10; GuiSetStyle(DEFAULT, TEXT_SIZE, 16); GuiSetStyle(DEFAULT, BASE_COLOR_NORMAL, 0x2D2D2DFF); GuiSetStyle(DEFAULT, BASE_COLOR_FOCUSED, 0x454545FF); GuiSetStyle(DEFAULT, BASE_COLOR_PRESSED, 0x1A1A1AFF); GuiSetStyle(DEFAULT, TEXT_COLOR_NORMAL, 0xFFFFFFFF); GuiSetStyle(DEFAULT, BORDER_COLOR_NORMAL, 0x505050FF); GuiSetStyle(DEFAULT, BORDER_COLOR_FOCUSED, 0x0078D7FF); GuiSetStyle(DEFAULT, BACKGROUND_COLOR, 0x1E1E1EFF); DrawText("PARAMETRES", startX, startY, 30, WHITE); DrawLine(startX, startY + 35, startX + contentWidth, startY + 35, COLOR_ACCENT); bool speedMode = IsKeyDown(KEY_LEFT_SHIFT) || IsKeyDown(KEY_RIGHT_SHIFT); const char* modeTxtStatus = speedMode ? "(SHIFT ACTIF: VITESSE RAPIDE)" : "(Maintenir SHIFT pour accelerer)"; Color statusColor = speedMode ? ORANGE : GRAY; DrawText(modeTxtStatus, startX, startY + 45, 10, statusColor); // Longueur d'onde startY += 80; DrawText(TextFormat("Longueur d'onde: %.0f nm", mic->lambda), startX, startY, 20, COLOR_TEXT_SEC); float stepLambda = speedMode ? 10.0f : 1.0f; if (GuiButtonRepeat((Rectangle){startX, startY + 30, btnSize, 25}, "-", shouldRepeat)) mic->lambda -= stepLambda; GuiSlider((Rectangle){startX + btnSize + 5, startY + 30, sliderWidth - 40, 25}, NULL, NULL, &mic->lambda, 380, 780); if (GuiButtonRepeat((Rectangle){startX + btnSize + sliderWidth - 30, startY + 30, btnSize, 25}, "+", shouldRepeat)) mic->lambda += stepLambda; DrawRectangle(startX + contentWidth - 30, startY + 30, 30, 25, WavelengthToColor(mic->lambda)); // Pos M1 startY += 90; DrawText(TextFormat("Position M1 : %.2f um", mic->d1), startX, startY, 20, ORANGE); // Pas des + et - float stepD1 = speedMode ? 0.5f : 0.005f; if (GuiButtonRepeat((Rectangle){startX, startY + 30, btnSize, 25}, "-", shouldRepeat)) mic->d1 -= stepD1; GuiSlider((Rectangle){startX + btnSize + 5, startY + 30, sliderWidth - 75, 25}, NULL, NULL, &mic->d1, 100, 600); if (GuiButtonRepeat((Rectangle){startX + btnSize + sliderWidth - 65, startY + 30, btnSize, 25}, "+", shouldRepeat)) mic->d1 += stepD1; if (GuiButton((Rectangle){startX + contentWidth - 60, startY + 30, 60, 25}, "Egal")) mic->d1 = mic->d2; // Angle M1 startY += 90; DrawText(TextFormat("Inclinaison M1: %.3f deg", mic->angleM1), startX, startY, 20, ORANGE); float stepAngle = speedMode ? 0.05f : 0.001f; if (GuiButtonRepeat((Rectangle){startX, startY + 30, btnSize, 25}, "-", shouldRepeat)) mic->angleM1 -= stepAngle; GuiSlider((Rectangle){startX + btnSize + 5, startY + 30, sliderWidth - 75, 25}, 0, 0, &mic->angleM1, -1.0f, 1.0f); if (GuiButtonRepeat((Rectangle){startX + btnSize + sliderWidth - 65, startY + 30, btnSize, 25}, "+", shouldRepeat)) mic->angleM1 += stepAngle; if (GuiButton((Rectangle){startX + contentWidth - 60, startY + 30, 60, 25}, "Zero")) mic->angleM1 = 0.0f; // Status startY += 70; bool isLameAir = (fabs(mic->angleM1) < 0.001f); const char* modeTxt = isLameAir ? "LAME D'AIR" : "COIN D'AIR"; Color modeColor = isLameAir ? SKYBLUE : GREEN; DrawRectangle(startX, startY, contentWidth, 30, Fade(modeColor, 0.2f)); DrawRectangleLines(startX, startY, contentWidth, 30, modeColor); DrawText(modeTxt, startX + 10, startY + 8, 20, modeColor); // Cuve de gaz startY += 50; DrawLine(startX, startY - 10, startX + contentWidth, startY - 10, Fade(GRAY, 0.3f)); DrawText("Cuve de Gaz (Indice n)", startX, startY, 20, SKYBLUE); GuiCheckBox((Rectangle){startX, startY + 30, 20, 20}, "Cuve", &mic->gaz); if (mic->gaz) { DrawText(TextFormat("n = %.4f", mic->nGaz), startX + 150, startY + 30, 20, WHITE); float stepN = speedMode ? 0.001f : 0.0001f; if (GuiButtonRepeat((Rectangle){startX, startY + 55, btnSize, 25}, "-", shouldRepeat)) mic->nGaz -= stepN; GuiSlider((Rectangle){startX + btnSize + 5, startY + 55, sliderWidth - 10, 25}, NULL, NULL, &mic->nGaz, 1.000f, 1.100f); if (GuiButtonRepeat((Rectangle){startX + btnSize + sliderWidth, startY + 55, btnSize, 25}, "+", shouldRepeat)) mic->nGaz += stepN; if (mic->nGaz < 1.0f) mic->nGaz = 1.0f; //startY += 90; } // Données startY += 100; DrawLine(startX, startY - 10, startX + contentWidth, startY - 10, GRAY); DrawText("DONNEES TEMPS REEL", startX, startY, 20, GRAY); float delta = 2 * (mic->d1 - mic->d2); DrawText(TextFormat("Delta = %.2f um", delta), startX, startY + 35, 20, WHITE); float p = (delta * 1000.0f) / mic->lambda; DrawText(TextFormat("Ordre p = %.2f", p), startX, startY + 65, 20, WHITE); // Bas int bottomY = GetScreenHeight() - 40; DrawLine(0, bottomY, UI_WIDTH, bottomY, Fade(WHITE, 0.1f)); int fps = GetFPS(); Color fpsColor = (fps >= 100) ? COLOR_ACCENT : (fps >= 60 ? GREEN : (fps >= 30 ? ORANGE : RED)); DrawText("STATUT:", startX, bottomY + 12, 20, COLOR_TEXT_DIM); DrawText(TextFormat("%i FPS", fps), startX + 100, bottomY + 12, 20, fpsColor); } int main () { SetConfigFlags(FLAG_MSAA_4X_HINT); InitWindow(1920, 1080, "Interferometre de Michelson"); SetTargetFPS(144); Shader shader = LoadShader(0, "michelson.fs"); Michelson mic = {0}; mic.center = (Vector2){ UI_WIDTH + (1920 - UI_WIDTH) / 2.0f - 100, 1080 / 2.0f }; mic.d1 = 250.0f; mic.d2 = 250.0f; mic.lambda = 550.0f; mic.angleM1 = 0.0f; bool isFullscreen = false; Rectangle normalBounds = {1920 - 530, 50, 500, 500}; Rectangle fullScreenBounds = {UI_WIDTH + 10, 40, 1920 - UI_WIDTH - 20, 1030}; //Rectangle screenViewBounds = {400, 0, 1080, 1080}; while (!WindowShouldClose()) { if (IsKeyPressed(KEY_F)) { isFullscreen = !isFullscreen; } Rectangle currentViewBounds = isFullscreen ? fullScreenBounds : normalBounds; BeginDrawing(); ClearBackground(COLOR_BG); // Grille fond for(int i = UI_WIDTH; i < 1920; i += 100) DrawLine(i, 0, i, 1080, Fade(WHITE, 0.05f)); for(int i = 0; i < 1080; i += 100) DrawLine(UI_WIDTH, i, 1920, i, Fade(WHITE, 0.05f)); DrawMichelsonSchema(&mic); DrawControlPanel(&mic); DrawInterferenceViewGPU(&mic, currentViewBounds, shader, &isFullscreen); EndDrawing(); } CloseWindow(); return 0; }