Files
Interferometre-de-Michelson/main.c
2025-12-28 10:42:48 +01:00

259 lines
10 KiB
C

#include "raylib.h"
#include <math.h>
#define RAYGUI_IMPLEMENTATION
#include "raygui.h"
#define UI_WIDTH 400
#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;
} 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);
}
void DrawInterferenceView(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 - 30, 20, WHITE);
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);
// Diff de marche du à l'axe (2 * e) + micro -> nano
float deltaLnm = 2.0f * (mic->d1 - mic->d2) * 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 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);
// 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));
int startX = 30;
int startY = 30;
int buttonWidth = 70;
int spacing = 20;
int contentWidth = UI_WIDTH - 60;
int sliderWidth = contentWidth - buttonWidth - spacing - 15;
GuiSetStyle(DEFAULT, TEXT_SIZE, 16);
GuiSetStyle(DEFAULT, BASE_COLOR_NORMAL, 0x2D2D2DFF); // Gris foncé boutons
GuiSetStyle(DEFAULT, BASE_COLOR_FOCUSED, 0x454545FF); // Gris clair hover
GuiSetStyle(DEFAULT, BASE_COLOR_PRESSED, 0x1A1A1AFF); // Très foncé click
GuiSetStyle(DEFAULT, TEXT_COLOR_NORMAL, 0xFFFFFFFF); // Texte blanc
GuiSetStyle(DEFAULT, BORDER_COLOR_NORMAL, 0x505050FF);
GuiSetStyle(DEFAULT, BORDER_COLOR_FOCUSED, 0x0078D7FF); // Bordures bleues (focus)
GuiSetStyle(DEFAULT, BACKGROUND_COLOR, 0x1E1E1EFF);
DrawText("PARAMETRES", startX, startY, 30, WHITE);
DrawLine(startX, startY + 35, startX + contentWidth, startY + 35, COLOR_ACCENT);
// Sliders
startY += 70;
DrawText(TextFormat("Longueur d'onde: %.0f nm", mic->lambda), startX, startY, 20, COLOR_TEXT_SEC);
GuiSlider((Rectangle){startX, startY + 30, contentWidth - 45, 25}, NULL, NULL, &mic->lambda, 380, 780);
DrawRectangle(startX + contentWidth - 35, startY + 30, 35, 25, WavelengthToColor(mic->lambda));
startY += 90;
DrawText(TextFormat("Position M1 (d1): %.1f um", mic->d1), startX, startY, 20, ORANGE);
GuiSlider((Rectangle){startX, startY + 30, sliderWidth, 25}, NULL, NULL, &mic->d1, 100, 600);
if (GuiButton((Rectangle){startX + sliderWidth + spacing + 15, startY + 30, buttonWidth, 25}, "Egal")) mic->d1 = mic->d2;
startY += 90;
DrawText(TextFormat("Inclinaison M1: %.2f deg", mic->angleM1), startX, startY, 20, ORANGE);
GuiSlider((Rectangle){startX, startY + 30, sliderWidth, 25}, "-1", "+1", &mic->angleM1, -1.0f, 1.0f);
if (GuiButton((Rectangle){startX + sliderWidth + spacing + 15, startY + 30, buttonWidth, 25}, "0.0")) mic->angleM1 = 0.0f;
bool isLameAir = (fabs(mic->angleM1) < 0.01f);
const char* modeTxt = isLameAir ? "MODE: LAME D'AIR" : "MODE: COIN D'AIR";
Color modeColor = isLameAir ? SKYBLUE : GREEN;
DrawRectangle(startX, startY + 70, contentWidth, 30, Fade(modeColor, 0.2f));
DrawRectangleLines(startX, startY + 70, contentWidth, 30, modeColor);
DrawText(modeTxt, startX + 10, startY + 78, 18, modeColor);
// Données
startY += 150;
DrawLine(startX, startY - 20, startX + contentWidth, startY - 20, 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);
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);
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;
Rectangle screenViewBounds = {1920 - 530, 50, 500, 500};
//Rectangle screenViewBounds = {400, 0, 1080, 1080};
while (!WindowShouldClose()) {
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);
DrawInterferenceView(&mic, screenViewBounds);
EndDrawing();
}
CloseWindow();
return 0;
}