Initial commit.

This commit is contained in:
Félix Faisant 2020-03-04 23:14:44 +01:00
parent 41a2582ff6
commit 088196decf
39 changed files with 4484 additions and 0 deletions

View File

@ -0,0 +1,385 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
022C25F6239ADF4400E2D35D /* Source.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 022C25F4239ADF4400E2D35D /* Source.cpp */; };
026842D323D727A70023E614 /* ObjetDiffusant.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 026842D123D727A70023E614 /* ObjetDiffusant.cpp */; };
02862264240ED9750000A6ED /* Scene.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F319B423DA713B0059D3AF /* Scene.cpp */; };
02AEC71D23F2CB8900816C72 /* ObjetsOptiques.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02AEC71B23F2CB8900816C72 /* ObjetsOptiques.cpp */; };
02B2BEE923FF44B8009FDF7F /* SceneTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02B2BEE723FF44B8009FDF7F /* SceneTest.cpp */; };
02C0BBF923F99485006F99C7 /* Brouillard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02C0BBF723F99485006F99C7 /* Brouillard.cpp */; };
02D1B5942376E96400B7FC13 /* ObjetsCourbes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02D1B5922376E96400B7FC13 /* ObjetsCourbes.cpp */; };
02D1B5972376EC0E00B7FC13 /* Rayon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02D1B5952376EC0E00B7FC13 /* Rayon.cpp */; };
02D1B59A2377242B00B7FC13 /* Util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02D1B5982377242B00B7FC13 /* Util.cpp */; };
02D1B59D237744EC00B7FC13 /* ObjetMilieux.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02D1B59B237744EC00B7FC13 /* ObjetMilieux.cpp */; };
02D1B5A02379ED3B00B7FC13 /* Ecran.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02D1B59E2379ED3B00B7FC13 /* Ecran.cpp */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
020AFBD8240006F6009C70B1 /* main_brouillard.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = main_brouillard.cpp; sourceTree = "<group>"; };
022C25F4239ADF4400E2D35D /* Source.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Source.cpp; sourceTree = "<group>"; };
022C25F5239ADF4400E2D35D /* Source.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Source.h; sourceTree = "<group>"; };
0231D0D223FF5743009E26E3 /* main_milieux.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = main_milieux.cpp; sourceTree = "<group>"; };
0266D47823A9A64D00A89D08 /* sfml_c01.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = sfml_c01.hpp; path = ../../Libs/sfml_c01.hpp; sourceTree = "<group>"; };
026842D123D727A70023E614 /* ObjetDiffusant.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ObjetDiffusant.cpp; sourceTree = "<group>"; };
026842D223D727A70023E614 /* ObjetDiffusant.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ObjetDiffusant.h; sourceTree = "<group>"; };
0273457B240C0E6B00C35E09 /* Makefile */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = "<none>"; };
02AEC71B23F2CB8900816C72 /* ObjetsOptiques.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ObjetsOptiques.cpp; sourceTree = "<group>"; };
02AEC71C23F2CB8900816C72 /* ObjetsOptiques.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ObjetsOptiques.h; sourceTree = "<group>"; };
02AEC71F23F33ACF00816C72 /* ObjetsCourbes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ObjetsCourbes.h; sourceTree = "<group>"; };
02AEC72123F3619600816C72 /* main_debug.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = main_debug.cpp; sourceTree = "<group>"; };
02B2BEE523FEAF75009FDF7F /* main_diffus_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = main_diffus_test.cpp; sourceTree = "<group>"; };
02B2BEE723FF44B8009FDF7F /* SceneTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SceneTest.cpp; sourceTree = "<group>"; };
02B2BEE823FF44B8009FDF7F /* SceneTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SceneTest.h; sourceTree = "<group>"; };
02C0BBF723F99485006F99C7 /* Brouillard.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Brouillard.cpp; sourceTree = "<group>"; };
02C0BBF823F99485006F99C7 /* Brouillard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Brouillard.h; sourceTree = "<group>"; };
02D1B57A23762C8200B7FC13 /* LightRays.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LightRays.app; sourceTree = BUILT_PRODUCTS_DIR; };
02D1B57E23762C8300B7FC13 /* LightRays-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "LightRays-Info.plist"; sourceTree = "<group>"; };
02D1B58223762C8300B7FC13 /* main_store.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = main_store.cpp; sourceTree = "<group>"; };
02D1B5922376E96400B7FC13 /* ObjetsCourbes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ObjetsCourbes.cpp; sourceTree = "<group>"; };
02D1B5932376E96400B7FC13 /* Objet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Objet.h; sourceTree = "<group>"; };
02D1B5952376EC0E00B7FC13 /* Rayon.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Rayon.cpp; sourceTree = "<group>"; };
02D1B5962376EC0E00B7FC13 /* Rayon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Rayon.h; sourceTree = "<group>"; };
02D1B5982377242B00B7FC13 /* Util.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Util.cpp; sourceTree = "<group>"; };
02D1B5992377242B00B7FC13 /* Util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Util.h; sourceTree = "<group>"; };
02D1B59B237744EC00B7FC13 /* ObjetMilieux.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ObjetMilieux.cpp; sourceTree = "<group>"; };
02D1B59C237744EC00B7FC13 /* ObjetMilieux.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ObjetMilieux.h; sourceTree = "<group>"; };
02D1B59E2379ED3B00B7FC13 /* Ecran.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Ecran.cpp; sourceTree = "<group>"; };
02D1B59F2379ED3B00B7FC13 /* Ecran.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Ecran.h; sourceTree = "<group>"; };
02F319B423DA713B0059D3AF /* Scene.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Scene.cpp; sourceTree = "<group>"; };
02F319B523DA713B0059D3AF /* Scene.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Scene.h; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
02D1B57623762C8200B7FC13 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
02D1B57023762C8200B7FC13 = {
isa = PBXGroup;
children = (
02D1B57C23762C8200B7FC13 /* LightRays */,
02D1B57B23762C8200B7FC13 /* Products */,
);
sourceTree = "<group>";
};
02D1B57B23762C8200B7FC13 /* Products */ = {
isa = PBXGroup;
children = (
02D1B57A23762C8200B7FC13 /* LightRays.app */,
);
name = Products;
sourceTree = "<group>";
};
02D1B57C23762C8200B7FC13 /* LightRays */ = {
isa = PBXGroup;
children = (
02D1B5992377242B00B7FC13 /* Util.h */,
02D1B5982377242B00B7FC13 /* Util.cpp */,
02D1B5962376EC0E00B7FC13 /* Rayon.h */,
02D1B5952376EC0E00B7FC13 /* Rayon.cpp */,
02D1B5932376E96400B7FC13 /* Objet.h */,
02AEC71F23F33ACF00816C72 /* ObjetsCourbes.h */,
02D1B5922376E96400B7FC13 /* ObjetsCourbes.cpp */,
02D1B59C237744EC00B7FC13 /* ObjetMilieux.h */,
02D1B59B237744EC00B7FC13 /* ObjetMilieux.cpp */,
026842D223D727A70023E614 /* ObjetDiffusant.h */,
026842D123D727A70023E614 /* ObjetDiffusant.cpp */,
02AEC71C23F2CB8900816C72 /* ObjetsOptiques.h */,
02AEC71B23F2CB8900816C72 /* ObjetsOptiques.cpp */,
02C0BBF823F99485006F99C7 /* Brouillard.h */,
02C0BBF723F99485006F99C7 /* Brouillard.cpp */,
022C25F5239ADF4400E2D35D /* Source.h */,
022C25F4239ADF4400E2D35D /* Source.cpp */,
02D1B59F2379ED3B00B7FC13 /* Ecran.h */,
02D1B59E2379ED3B00B7FC13 /* Ecran.cpp */,
02F319B523DA713B0059D3AF /* Scene.h */,
02F319B423DA713B0059D3AF /* Scene.cpp */,
02B2BEE823FF44B8009FDF7F /* SceneTest.h */,
02B2BEE723FF44B8009FDF7F /* SceneTest.cpp */,
02B2BEE523FEAF75009FDF7F /* main_diffus_test.cpp */,
02D1B58223762C8300B7FC13 /* main_store.cpp */,
020AFBD8240006F6009C70B1 /* main_brouillard.cpp */,
0231D0D223FF5743009E26E3 /* main_milieux.cpp */,
02AEC72123F3619600816C72 /* main_debug.cpp */,
0266D47823A9A64D00A89D08 /* sfml_c01.hpp */,
0273457B240C0E6B00C35E09 /* Makefile */,
02D1B57D23762C8200B7FC13 /* Supporting Files */,
);
path = LightRays;
sourceTree = "<group>";
};
02D1B57D23762C8200B7FC13 /* Supporting Files */ = {
isa = PBXGroup;
children = (
02D1B57E23762C8300B7FC13 /* LightRays-Info.plist */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
02D1B57923762C8200B7FC13 /* LightRays */ = {
isa = PBXNativeTarget;
buildConfigurationList = 02D1B58F23762C8300B7FC13 /* Build configuration list for PBXNativeTarget "LightRays" */;
buildPhases = (
02D1B57523762C8200B7FC13 /* Sources */,
02D1B57623762C8200B7FC13 /* Frameworks */,
02D1B57723762C8200B7FC13 /* Resources */,
02D1B57823762C8200B7FC13 /* ShellScript */,
);
buildRules = (
);
dependencies = (
);
name = LightRays;
productName = LightRays;
productReference = 02D1B57A23762C8200B7FC13 /* LightRays.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
02D1B57123762C8200B7FC13 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0620;
ORGANIZATIONNAME = xif;
TargetAttributes = {
02D1B57923762C8200B7FC13 = {
CreatedOnToolsVersion = 6.2;
};
};
};
buildConfigurationList = 02D1B57423762C8200B7FC13 /* Build configuration list for PBXProject "LightRays" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = 02D1B57023762C8200B7FC13;
productRefGroup = 02D1B57B23762C8200B7FC13 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
02D1B57923762C8200B7FC13 /* LightRays */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
02D1B57723762C8200B7FC13 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
02D1B57823762C8200B7FC13 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "# This shell script simply copies required SFML dylibs/frameworks into the application bundle frameworks folder.\n# If you're using static libraries (which is not recommended) you should remove this script from your project.\n\n# SETTINGS\nCMAKE_INSTALL_FRAMEWORK_PREFIX=\"/Library/Frameworks\"\nCMAKE_INSTALL_LIB_PREFIX=\"/usr/local/lib\"\nFRAMEWORKS_FULL_PATH=\"$BUILT_PRODUCTS_DIR/$FRAMEWORKS_FOLDER_PATH/\"\n\n# Are we building a project that uses frameworks or dylibs?\ncase \"$SFML_BINARY_TYPE\" in\n DYLIBS)\n frameworks=\"false\"\n ;;\n *)\n frameworks=\"true\"\n ;;\nesac\n\n# Echoes to stderr\nerror () # $* message to display\n{\n echo $* 1>&2\n exit 2\n}\n\nassert () # $1 is a boolean, $2...N is an error message\n{\n if [ $# -lt 2 ]\n then\n error \"Internal error in assert: not enough args\"\n fi\n\n if [ $1 -ne 0 ]\n then\n shift\n error \"$*\"\n fi\n}\n\nforce_remove () # $@ is some paths\n{\n test $# -ge 1\n assert $? \"force_remove() requires at least one parameter\"\n rm -fr $@\n assert $? \"couldn't remove $@\"\n}\n\ncopy () # $1 is a source, $2 is a destination\n{\n test $# -eq 2\n assert $? \"copy() requires two parameters\"\n ditto \"$1\" \"$2\"\n assert $? \"couldn't copy $1 to $2\"\n}\n\nrequire () # $1 is a SFML module like 'system' or 'audio'\n{\n dest=\"$BUILT_PRODUCTS_DIR/$FRAMEWORKS_FOLDER_PATH/\"\n\n if [ -z \"$1\" ]\n then\n error \"require() requires one parameter!\"\n else\n # clean potentially old stuff\n force_remove \"$dest/libsfml-$1\"*\n force_remove \"$dest/sfml-$1.framework\"\n\n # copy SFML libraries\n if [ \"$frameworks\" = \"true\" ]\n then\n source=\"$CMAKE_INSTALL_FRAMEWORK_PREFIX/sfml-$1.framework\"\n target=\"sfml-$1.framework\"\n elif [ \"$SFML_LINK_DYLIBS_SUFFIX\" = \"-d\" ]\n then\n source=\"$CMAKE_INSTALL_LIB_PREFIX/libsfml-$1-d.dylib\"\n target=\"`readlink $source`\"\n else\n source=\"$CMAKE_INSTALL_LIB_PREFIX/libsfml-$1.dylib\"\n target=\"`readlink $source`\"\n fi\n\n copy \"$source\" \"$dest/$target\"\n\n # copy extra dependencies\n if [ \"$1\" = \"audio\" ]\n then\n # copy \"FLAC\" \"ogg\" \"vorbis\" \"vorbisenc\" \"vorbisfile\" \"OpenAL\" frameworks too\n for f in \"FLAC\" \"ogg\" \"vorbis\" \"vorbisenc\" \"vorbisfile\" \"OpenAL\"\n do\n copy \"$CMAKE_INSTALL_FRAMEWORK_PREFIX/$f.framework\" \"$dest/$f.framework\"\n done\n elif [ \"$1\" = \"graphics\" ]\n then\n copy \"$CMAKE_INSTALL_FRAMEWORK_PREFIX/freetype.framework\" \"$dest/freetype.framework\"\n fi\n fi\n}\n\nif [ -n \"$SFML_SYSTEM\" ]\nthen\n require \"system\"\nfi\n\nif [ -n \"$SFML_AUDIO\" ]\nthen\n require \"audio\"\nfi\n\nif [ -n \"$SFML_NETWORK\" ]\nthen\n require \"network\"\nfi\n\nif [ -n \"$SFML_WINDOW\" ]\nthen\n require \"window\"\nfi\n\nif [ -n \"$SFML_GRAPHICS\" ]\nthen\n require \"graphics\"\nfi\n\n ";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
02D1B57523762C8200B7FC13 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
02D1B5942376E96400B7FC13 /* ObjetsCourbes.cpp in Sources */,
02862264240ED9750000A6ED /* Scene.cpp in Sources */,
026842D323D727A70023E614 /* ObjetDiffusant.cpp in Sources */,
02D1B59D237744EC00B7FC13 /* ObjetMilieux.cpp in Sources */,
022C25F6239ADF4400E2D35D /* Source.cpp in Sources */,
02D1B5A02379ED3B00B7FC13 /* Ecran.cpp in Sources */,
02C0BBF923F99485006F99C7 /* Brouillard.cpp in Sources */,
02D1B5972376EC0E00B7FC13 /* Rayon.cpp in Sources */,
02B2BEE923FF44B8009FDF7F /* SceneTest.cpp in Sources */,
02D1B59A2377242B00B7FC13 /* Util.cpp in Sources */,
02AEC71D23F2CB8900816C72 /* ObjetsOptiques.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
02D1B58D23762C8300B7FC13 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = "$(NATIVE_ARCH_ACTUAL)";
CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
CLANG_CXX_LIBRARY = "libc++";
FRAMEWORK_SEARCH_PATHS = (
/Library/Frameworks/,
"$(inherited)",
);
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
HEADER_SEARCH_PATHS = (
/usr/local/include/,
"$(inherited)",
../../Libs/,
);
LIBRARY_SEARCH_PATHS = (
/usr/local/lib/,
"$(inherited)",
);
MACOSX_DEPLOYMENT_TARGET = 10.7;
OTHER_CPLUSPLUSFLAGS = (
"$(OTHER_CFLAGS)",
"-DSFMLC01_WINDOW_UNIT=720",
"-Dvec2_t=vec_t",
"-Dpt2_t=point_t",
"-DFONT_PATH=\\\"/Users/xif/Desktop/Code/LightRays/LightRays/DejaVuSansMono.ttf\\\"",
"-DNOSTDOPTIONAL",
);
OTHER_LDFLAGS = (
"$(inherited)",
"$(SFML_SYSTEM)",
"$(SFML_WINDOW)",
"$(SFML_GRAPHICS)",
"$(SFML_AUDIO)",
"$(SFML_NETWORK)",
"-lfmt",
);
SFML_AUDIO = "";
SFML_BINARY_TYPE = FRAMEWORKS;
SFML_GRAPHICS = "$(SFML_LINK_PREFIX) sfml-graphics$(SFML_LINK_SUFFIX)";
SFML_LINK_DYLIBS_PREFIX = "-l";
SFML_LINK_DYLIBS_SUFFIX = "";
SFML_LINK_FRAMEWORKS_PREFIX = "-framework";
SFML_LINK_FRAMEWORKS_SUFFIX = "";
SFML_LINK_PREFIX = "$(SFML_LINK_$(SFML_BINARY_TYPE)_PREFIX)";
SFML_LINK_SUFFIX = "$(SFML_LINK_$(SFML_BINARY_TYPE)_SUFFIX)";
SFML_NETWORK = "";
SFML_SYSTEM = "$(SFML_LINK_PREFIX) sfml-system$(SFML_LINK_SUFFIX)";
SFML_WINDOW = "$(SFML_LINK_PREFIX) sfml-window$(SFML_LINK_SUFFIX)";
SUPPORTED_PLATFORMS = macosx;
};
name = Debug;
};
02D1B58E23762C8300B7FC13 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = "$(NATIVE_ARCH_ACTUAL)";
CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
CLANG_CXX_LIBRARY = "libc++";
FRAMEWORK_SEARCH_PATHS = (
/Library/Frameworks/,
"$(inherited)",
);
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
HEADER_SEARCH_PATHS = (
/usr/local/include/,
"$(inherited)",
../../Libs/,
);
LIBRARY_SEARCH_PATHS = (
/usr/local/lib/,
"$(inherited)",
);
MACOSX_DEPLOYMENT_TARGET = 10.7;
OTHER_CPLUSPLUSFLAGS = (
"$(OTHER_CFLAGS)",
"-DSFMLC01_WINDOW_UNIT=720",
"-Dvec2_t=vec_t",
"-Dpt2_t=point_t",
"-DFONT_PATH=\\\"/Users/xif/Desktop/Code/LightRays/LightRays/DejaVuSansMono.ttf\\\"",
"-DNOSTDOPTIONAL",
);
OTHER_LDFLAGS = (
"$(inherited)",
"$(SFML_SYSTEM)",
"$(SFML_WINDOW)",
"$(SFML_GRAPHICS)",
"$(SFML_AUDIO)",
"$(SFML_NETWORK)",
"-lfmt",
);
SFML_AUDIO = "";
SFML_BINARY_TYPE = FRAMEWORKS;
SFML_GRAPHICS = "$(SFML_LINK_PREFIX) sfml-graphics$(SFML_LINK_SUFFIX)";
SFML_LINK_DYLIBS_PREFIX = "-l";
SFML_LINK_DYLIBS_SUFFIX = "";
SFML_LINK_FRAMEWORKS_PREFIX = "-framework";
SFML_LINK_FRAMEWORKS_SUFFIX = "";
SFML_LINK_PREFIX = "$(SFML_LINK_$(SFML_BINARY_TYPE)_PREFIX)";
SFML_LINK_SUFFIX = "$(SFML_LINK_$(SFML_BINARY_TYPE)_SUFFIX)";
SFML_NETWORK = "";
SFML_SYSTEM = "$(SFML_LINK_PREFIX) sfml-system$(SFML_LINK_SUFFIX)";
SFML_WINDOW = "$(SFML_LINK_PREFIX) sfml-window$(SFML_LINK_SUFFIX)";
SUPPORTED_PLATFORMS = macosx;
};
name = Release;
};
02D1B59023762C8300B7FC13 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
INFOPLIST_FILE = "LightRays/LightRays-Info.plist";
LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks";
OTHER_CFLAGS = "";
PRODUCT_NAME = "$(TARGET_NAME)";
WARNING_CFLAGS = "-Wall";
};
name = Debug;
};
02D1B59123762C8300B7FC13 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
INFOPLIST_FILE = "LightRays/LightRays-Info.plist";
LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks";
OTHER_CFLAGS = "";
PRODUCT_NAME = "$(TARGET_NAME)";
WARNING_CFLAGS = "-Wall";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
02D1B57423762C8200B7FC13 /* Build configuration list for PBXProject "LightRays" */ = {
isa = XCConfigurationList;
buildConfigurations = (
02D1B58D23762C8300B7FC13 /* Debug */,
02D1B58E23762C8300B7FC13 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
02D1B58F23762C8300B7FC13 /* Build configuration list for PBXNativeTarget "LightRays" */ = {
isa = XCConfigurationList;
buildConfigurations = (
02D1B59023762C8300B7FC13 /* Debug */,
02D1B59123762C8300B7FC13 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 02D1B57123762C8200B7FC13 /* Project object */;
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:LightRays.xcodeproj">
</FileRef>
</Workspace>

151
LightRays/Brouillard.cpp Normal file
View File

@ -0,0 +1,151 @@
#include "Brouillard.h"
#include "ObjetsCourbes.h"
#include <cmath>
Objet::extension_t Objet_Brouillard::objet_extension () const {
vec_t v = { lx*reso_x/2, ly*reso_y/2 };
return {
.pos = o + v,
.rayon = std::max(v.x, v.y)
};
}
// Test d'interception avec le brouillard : parcours du rayon à travers les cellules
// et décision si oui ou non on diffuserait le rayon
//
Objet::intercept_t Objet_Brouillard::essai_intercept (const Rayon& ray) const {
if (ray.spectre.intensite_tot() < intens_cutoff)
return { .dist2 = Inf, .intercept_struct = nullptr };
// on test si le rayon passe sur le rectange bornant le brouillard
std::vector<ObjetLigne::intersection_segdd_t> isects;
auto test_isect = [&] (point_t a, point_t b) {
auto isect = ObjetLigne::intersection_segment_demidroite(a, b, ray.orig, ray.dir_angle);
if (isect.has_value())
isects.push_back(isect.value());
};
point_t o2 = o + vec_t{lx*reso_x,0},
o3 = o + vec_t{lx*reso_x,ly*reso_y},
o4 = o + vec_t{0,ly*reso_y};
test_isect(o , o2);
test_isect(o2, o3);
test_isect(o3, o4);
test_isect(o4, o );
// assert(isects.size() <= 2); // le rayon ne peut avoir que 1 (si source interne) ou 2 intersections (ou zéro) avec les bords
if (isects.size() != 0) {
float s;
float s_fin = isects[0].t_dd;
if (isects.size() == 1) { // rayon émis à l'intérieur du brouillard
s = 0;
} else { // rayon provenant de l'expérieur du brouillard
s = isects[1].t_dd;
if (s > s_fin)
std::swap(s, s_fin);
}
float ds = std::min(reso_x,reso_y) / 2;
vec_t u_ray = isects[0].u_dd;
// le rayon parcourt le brouillard jusqu'à s_fin par incréments de ds
// avec une probabilité donnée par `densit_brouillard` d'être diffusé à chaque pas, pour la méthode 1
// et avec une probabilité de `diffus_partielle_syst_proba`
intercept_brouillard_t p;
float proba_acc = 0;
while (s < s_fin) {
p.p_diff = ray.orig + s * u_ray;
vec_t v = p.p_diff - this->o;
p.x_diff = floorf( v.x / reso_x );
p.y_diff = floorf( v.y / reso_y );
if (0 <= p.x_diff and p.x_diff < (int)this->lx and 0 <= p.y_diff and p.y_diff < (int)this->ly) {
float proba = ds / diffus_partielle_syst_libreparcours;
if (proba > 1e-5) {
// méthode par diffusion systématique partielle
proba_acc += proba;
p.fraction_transmis = std::max<float>(0, 1 - ds/proba * this->densit_brouillard(p.x_diff, p.y_diff));
} else {
// méthode par diffusion probabiliste complète
proba = ds * this->densit_brouillard(p.x_diff, p.y_diff);
proba_acc += proba;
p.fraction_transmis = 0;
}
bool diffuse = parcours_deterministe ? (proba_acc >= 1) : (rand01() < proba);
if (diffuse) {
// rayon diffusé
s += /*rand01() */ ds;
p.p_diff = ray.orig + s * u_ray;
return { .dist2 = s*s,
.intercept_struct = std::make_shared<intercept_brouillard_t>(std::move(p)) };
}
}
s += ds;
}
}
return { .dist2 = Inf, .intercept_struct = nullptr };
};
// Ré-émission du rayon intercepté par le brouillard
//
std::vector<Rayon> Objet_Brouillard::re_emit (const Rayon& ray, std::shared_ptr<void> interception) {
const intercept_brouillard_t& intercept = *(intercept_brouillard_t*)interception.get();
std::vector<Rayon> rayons;
// Nombre de rayons secondaires
size_t n_re_emit = std::max<size_t>(1, lroundf(
n_re_emit_par_intens * ray.spectre.intensite_tot() * (1-intercept.fraction_transmis))
);
float fact_I_re_emit = 1./n_re_emit;
// On laisse vivre le rayon incident seulement si il a une intensité > 0.01
if (intercept.fraction_transmis > 0.01) {
Rayon ray_trsm = ray;
ray_trsm.spectre.for_each([&] (float lambda, pola_t, float& I) {
I *= intercept.fraction_transmis;
});
ray_trsm.orig = intercept.p_diff;
rayons.push_back(std::move(ray_trsm));
fact_I_re_emit *= 1 - intercept.fraction_transmis;
}
// Émission des rayons secondaires
for (size_t k = 0; k < n_re_emit; k++) {
float ang_diff = 2*M_PI * rand01();
Rayon ray_diff;
ray_diff.orig = intercept.p_diff;
ray_diff.dir_angle = ray.dir_angle + ang_diff;
ray_diff.spectre = ray.spectre;
ray_diff.spectre.for_each([&] (float lambda, pola_t, float& I) {
I *= this->directivite_diffus(ang_diff, lambda) * fact_I_re_emit;
});
rayons.push_back(std::move(ray_diff));
}
return rayons;
}
#include "sfml_c01.hpp"
void Objet_Brouillard::dessiner (sf::RenderWindow& window, bool emphasize) const {
for (size_t x = 0; x < this->lx; x++) {
for (size_t y = 0; y < this->ly; y++) {
sf::RectangleShape dens;
sf::c01::setRectShape(dens, o + vec_t{reso_x*x, reso_y*y}, {reso_x, reso_y});
dens.setFillColor(sf::Color(255,255,255, std::min(255.f, this->densit_brouillard(x,y)) ));
window.draw(dens);
}
}
}
std::optional<point_t> Objet_Brouillard::point_interception (std::shared_ptr<void> interception) const {
if (interception) {
return ((intercept_brouillard_t*)interception.get())->p_diff;
} else
return std::nullopt;
}

75
LightRays/Brouillard.h Normal file
View File

@ -0,0 +1,75 @@
/*******************************************************************************
* Brouillards.
*******************************************************************************/
#ifndef _LIGHTRAYS_BROUILLARD_H_
#define _LIGHTRAYS_BROUILLARD_H_
#include "Objet.h"
//------------------------------------------------------------------------------
// Brouillard à "cellules/voxels" de densité arbitraire. Implémentation naïve,
// loin d'être performante.
class Objet_Brouillard : virtual public Objet {
public:
point_t o; // bottom-left corner (origine)
float reso_x, reso_y; // résolutions (taille d'une cellule du brouillard) dans les directions x et y
uint lx, ly; // taille du brouillard en nombre de cellules
std::function< float(size_t x, size_t y) > densit_brouillard; // fonction densité(x,y) du brouillard
std::function< float(float theta, float lambda) > directivite_diffus; // distribution angulaire des rayons diffusés, avec theta l'angle du rayon diffusé par rapport au rayon incident
// Il y a deux techniques de diffusion (non équivalentes en terme de résultat) :
// - soit on diffuse totalement (en `n_re_emit_par_intens` rayons-fils, avec la directivité `directivite_diffus`)
// le rayon avec une probabilité à chaque pas donnée par la densité du brouillard
// ("diffusion browienne")
// (adapté au temps réel car peu de rayons produits, mais résultat bruité)
// - soit on diffuse partiellement (fraction donnée par la densité du brouillard, et en
// `n_re_emit_par_intens` rayons-fils, avec la directivité `directivite_diffus`) le rayon,
// mais systématiquement avec un libre parcours moyen `diffus_partielle_syst_libreparcours`
// ("diffusion à la bert-lambert") (attention, la directivité effective est `directivite_diffus` + un dirac theta=0)
// (peu bruité, mais lent car "cascade" -> nombreux rayons produits)
// La deuxième méthode est utilisée lorsque `diffus_partielle_syst_libreparcours` est non infini
// Lorsque la longueur `diffus_partielle_syst_libreparcours` n'est pas trop grande face à la taille du brouillard,
// et qu'elle est supérieure à la résolution, les deux méthodes doivent donner la même intensité
// transmise en ligne droite ("rayon non dévié") en moyenne
float n_re_emit_par_intens;
float diffus_partielle_syst_libreparcours;
// Parcours déterministe ou non du rayon passant à traver le brouillard. Typiquement, soit
// on fait une diffusion du rayon aléatoire avec une certaine probabilité, soit on diffuse
// régulièrement le rayon avec cette même "probabilité". Les deux méthodes convergent à
// grande résolution.
bool parcours_deterministe;
// Les rayons d'intensité < `intens_cutoff` sont ignorés (amélioration de performance)
float intens_cutoff;
// Par défaut, méthode "brownienne", directivté uniforme, parcours aléatoire, pas de cutoff
Objet_Brouillard (point_t o, float reso_x, float reso_y, uint lx, uint ly, decltype(densit_brouillard) densit_brouillard) :
o(o), reso_x(reso_x), reso_y(reso_y), lx(lx), ly(ly), densit_brouillard(densit_brouillard),
directivite_diffus([] (float, float) { return 1.f; }),
n_re_emit_par_intens(5), diffus_partielle_syst_libreparcours(Inf), parcours_deterministe(false), intens_cutoff(0) {}
Objet_Brouillard& operator= (const Objet_Brouillard&) = default;
Objet_Brouillard (const Objet_Brouillard&) = default;
virtual ~Objet_Brouillard () {}
// Routines d'interception et de ré-émission
struct intercept_brouillard_t {
point_t p_diff;
int x_diff, y_diff;
float fraction_transmis;
};
virtual intercept_t essai_intercept (const Rayon&) const override;
virtual extension_t objet_extension () const override;
virtual std::vector<Rayon> re_emit (const Rayon&, std::shared_ptr<void>) override;
virtual std::optional<point_t> point_interception (std::shared_ptr<void> intercept_struct) const override;
// Dessin
virtual void dessiner (sf::RenderWindow& window, bool emphasize) const override;
// Pas de dessin d'interception
virtual void dessiner_interception (sf::RenderWindow& window, const Rayon& ray, std::shared_ptr<void> intercept) const override {};
};
#endif

Binary file not shown.

142
LightRays/Ecran.cpp Normal file
View File

@ -0,0 +1,142 @@
#include "Ecran.h"
#include <algorithm>
#include <cmath>
#include "sfml_c01.hpp"
///------------------------ EcranLigne_Multi ------------------------///
EcranLigne_Multi::EcranLigne_Multi (point_t a, point_t b, float bin_dens, float lumino) :
EcranLigne_Multi (a, b, (uint16_t)(bin_dens * !(b-a)), lumino) {}
EcranLigne_Multi::EcranLigne_Multi (point_t a, point_t b, uint16_t bins_n, float lumino) :
Ecran_Base(lumino),
ObjetLigne(a, b),
bins_intensit( std::max<size_t>(1, bins_n) ),
epaisseur_affich(std::nullopt)
{ this->reset(); }
void EcranLigne_Multi::reset () {
n_acc = 0;
for (Specte& intensit : bins_intensit) {
intensit.for_each([&] (float, pola_t, float& I) -> void {
I = 0;
});
}
}
// Accumulation des rayons dans les pixels
//
std::vector<Rayon> EcranLigne_Multi::re_emit (const Rayon& ray, std::shared_ptr<void> interception) {
intercept_ligne_t& intercept = *(intercept_ligne_t*)interception.get();
size_t N = bins_intensit.size();
ssize_t k_bin = floorf(intercept.s_incid * N);
if (k_bin == -1) k_bin = 0;
if (k_bin == (ssize_t)N) k_bin = N-1;
Specte::for_each_manual([&] (size_t i, float lambda, pola_t pol) -> void {
bins_intensit[k_bin].comps[i] += ray.spectre.comps[i];
});
return {};
}
// Retrourne la matrice de pixels traitée
//
std::vector<EcranLigne_Multi::pixel_t> EcranLigne_Multi::matrice_pixels () const {
size_t N = this->bins_intensit.size();
// dans chaque pixel, c'est une puissance qu'on accumule, proportionnelle
// à la taille d'un pixel ( |b-a|/N ) pour une source omnidirectionnelle
// -> il faut diviser par la taille d'un pixel pour avoir d'intensité
// lumineuse (dont la luminosité RGB affichée est proportionnelle)
float ItoL = this->luminosite / this->n_acc * (float)N / !(b-a);
std::vector<pixel_t> mat (N);
for (size_t k = 0; k < N; k++) {
mat[k].s1 = k / (float)N;
mat[k].s2 = (k+1) / (float)N;
mat[k].s_mid = (mat[k].s1 + mat[k].s2) / 2;
mat[k].spectre = bins_intensit[k];
mat[k].spectre.for_each([&] (float, pola_t, float& I) -> void {
I *= ItoL;
});
std::tie(mat[k].r, mat[k].g, mat[k].b, mat[k].sat) = mat[k].spectre.rgb256_noir_intensite(false);
}
return mat;
}
// Dessin de la matrice de pixels
//
void EcranLigne_Multi::dessiner (sf::RenderWindow& window, bool emphasize) const {
std::vector<pixel_t> pix =
this->matrice_pixels();
vec_t v = a - b;
auto p = [&] (float s) { return b + v * s; };
if (epaisseur_affich.has_value()) {
vec_t perp = v.rotate_p90() / (!v) * (*epaisseur_affich);
for (size_t k = 0; k < pix.size(); k++) {
auto color = sf::Color(pix[k].r, pix[k].g, pix[k].b);
vec_t lng = v * (pix[k].s2 - pix[k].s1);
sf::ConvexShape rect = sf::c01::buildParallelogram(p(pix[k].s1), lng, perp, color);
window.draw(rect);
}
} else {
for (size_t k = 0; k < pix.size(); k++) {
if (pix[k].sat) { // saturation
auto c = sf::c01::buildCircleShapeCR(p(pix[k].s_mid), 0.003);
c.setFillColor(sf::Color::Yellow);
window.draw(c);
}
auto color = sf::Color(pix[k].r, pix[k].g, pix[k].b);
auto line = sf::c01::buildLine(p(pix[k].s1), p(pix[k].s2), color, color);
window.draw(line);
}
}
}
///------------------------ EcranLigne_Mono ------------------------///
EcranLigne_Mono::EcranLigne_Mono (point_t a, point_t b, float lumino) :
Ecran_Base(lumino),
ObjetLigne(a, b),
intensit()
{ this->reset(); }
void EcranLigne_Mono::reset () {
n_acc = 0;
intensit.for_each([&] (float, pola_t, float& I) -> void {
I = 0;
});
}
// Accumulations des rayons sur l'écran
//
std::vector<Rayon> EcranLigne_Mono::re_emit (const Rayon& ray, std::shared_ptr<void>) {
Specte::for_each_manual([&] (size_t i, float lambda, pola_t pol) -> void {
intensit.comps[i] += ray.spectre.comps[i];
});
return {};
}
// Pixel traité
//
EcranLigne_Mono::pixel_t EcranLigne_Mono::pixel () const {
float ItoL = this->luminosite / this->n_acc / !(b-a);
pixel_t pix;
Specte sp = intensit;
sp.for_each([&] (float, pola_t, float& I) -> void {
I *= ItoL;
});
std::tie(pix.r, pix.g, pix.b, pix.sat) = sp.rgb256_noir_intensite(false);
return pix;
}
// Dessin du pixel
//
void EcranLigne_Mono::dessiner (sf::RenderWindow& window, bool emphasize) const {
pixel_t pix = this->pixel();
if (pix.sat) {
auto c = sf::c01::buildCircleShapeCR(milieu_2points(a,b), 0.003);
c.setFillColor(sf::Color::Yellow);
window.draw(c);
}
auto color = sf::Color(pix.r, pix.g, pix.b);
auto line = sf::c01::buildLine(a, b, color, color);
window.draw(line);
}

83
LightRays/Ecran.h Normal file
View File

@ -0,0 +1,83 @@
/*******************************************************************************
* Écrans : objets absorbant les rayons et accumulant l'intensité pour
* l'afficher ou l'enregistrer ("CCD").
*******************************************************************************/
#ifndef _LIGHTRAYS_ECRAN_H_
#define _LIGHTRAYS_ECRAN_H_
#include "ObjetsCourbes.h"
#include <vector>
#include "Rayon.h"
//------------------------------------------------------------------------------
// Classe de base virtuelle des écrans. Ne fait rien.
class Ecran_Base : virtual public Objet {
protected:
size_t n_acc; // nombre de frames accumulées
public:
float luminosite; // coefficient de conversion intensité réelle -> intensité affichée
Ecran_Base (float lumino = 1.) : n_acc(0), luminosite(lumino) {}
virtual ~Ecran_Base () {}
// appellé à la fin de chaque frame
virtual void commit () { n_acc++; }
// réinitialisation de l'écran
virtual void reset () = 0;
};
//------------------------------------------------------------------------------
// Écran en forme de segment. Matrice (1D) de pixels, affichable ou enregistrable.
class EcranLigne_Multi : virtual public Ecran_Base, virtual public ObjetLigne {
protected:
std::vector<Specte> bins_intensit; // matrice de pixels; un spectre par pixel
public:
std::optional<float> epaisseur_affich = std::nullopt; // épaisseur de l'écran affiché
// Construction à partir des points A et B et du nombre de pixels par unité de longueur
EcranLigne_Multi (point_t pos_a, point_t pos_b, float pixel_density, float lumino = 1.);
// Construction à partir des points A et B et du nombre de pixels
EcranLigne_Multi (point_t pos_a, point_t pos_b, uint16_t pixels_n, float lumino = 1.);
virtual ~EcranLigne_Multi () {}
// Absorption et accumulation des rayons; pas de ré-émission
virtual std::vector<Rayon> re_emit (const Rayon& ray, std::shared_ptr<void> intercept) override;
// Réinitialisation de l'écran
virtual void reset () override;
// Récupération de la matrice de pixels RGB ou spectres :
struct pixel_t {
float s1, s_mid, s2; // abscice du début, du milieu et de la fin du pixel
uint8_t r, g, b; // valeurs RGB (les mêmes qu'affichées)
bool sat; // saturation du pixel
Specte spectre; // spectre accumulé sur le pixel
};
std::vector<pixel_t> matrice_pixels () const;
// Dessin de cette matrice de pixels
virtual void dessiner (sf::RenderWindow& window, bool emphasize) const override;
};
//------------------------------------------------------------------------------
// Écran en forme de segment avec un unique pixel
class EcranLigne_Mono : virtual public Ecran_Base, virtual public ObjetLigne {
protected:
Specte intensit; // spectre accumulé
public:
EcranLigne_Mono (point_t pos_a, point_t pos_b, float lumino = 1.);
virtual ~EcranLigne_Mono () {}
virtual std::vector<Rayon> re_emit (const Rayon& ray, std::shared_ptr<void> intercept) override;
virtual void reset () override;
struct pixel_t { uint8_t r, g, b; bool sat; };
pixel_t pixel () const;
virtual void dessiner (sf::RenderWindow& window, bool emphasize) const override;
};
#endif

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>xif.${PRODUCT_NAME:rfc1034identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>NSHighResolutionCapable</key>
<true/>
</dict>
</plist>

29
LightRays/Makefile Normal file
View File

@ -0,0 +1,29 @@
CPPFLAGS := -O3 -Wall -DSFMLC01_WINDOW_UNIT=720 -Dvec2_t=vec_t -Dpt2_t=point_t -DFONT_PATH=\"DejaVuSansMono.ttf\"
LDFLAGS := -lsfml-graphics -lsfml-window -lsfml-system
all: brouillard diffus_test milieux store
COMMON := Rayon.o Util.o Scene.o SceneTest.o Ecran.o ObjetsCourbes.o Source.o ObjetsOptiques.o
brouillard: $(COMMON) Brouillard.o ObjetDiffusant.o main_brouillard.o
g++ -o lightrays-brouillard -lm $^ $(LDFLAGS)
./lightrays-brouillard
diffus_test: $(COMMON) ObjetDiffusant.o main_diffus_test.o
g++ -o lightrays-diffus_test -lm $^ $(LDFLAGS)
./lightrays-diffus_test
milieux: $(COMMON) ObjetMilieux.o main_milieux.o
g++ -o lightrays-milieux -lm $^ $(LDFLAGS)
./lightrays-milieux
store: $(COMMON) ObjetDiffusant.o main_store.o
g++ -o lightrays-store -lm $^ $(LDFLAGS)
./lightrays-store
%.o: %.cpp
g++ -o $@ -c $< -std=c++17 $(CPPFLAGS)
clean:
rm *.o
rm lightrays-*

58
LightRays/Objet.h Normal file
View File

@ -0,0 +1,58 @@
/*******************************************************************************
* Objet optique générique, capable de se dessiner, et surtout de tester
* l'interception d'un rayon, et, le cas échéant, de -émettre le rayon.
*******************************************************************************/
#ifndef _LIGHTRAYS_OBJET_H_
#define _LIGHTRAYS_OBJET_H_
#include <memory>
#include "Rayon.h"
#include "Util.h"
#include <vector>
#include <SFML/Graphics/RenderWindow.hpp>
// Note : tous les std::optional<std::shared_ptr<T>> pourraient être remplacés par des T* (null ou pas),
// mais il faudrait gérer la désallocation à la main
//------------------------------------------------------------------------------
// Classe de base des objets optiques. Déclare l'interface commune utilisée lors
// de la propagation (interception puis ré-émission) des rayons, et l'affichage.
// Un objet est capable de tester l'interception d'un rayon avec `essai_intercept`,
// et, le cas échéant, de ré-émettre le rayon `re_emit` et (optionnellement) de
// donner le point d'interception avec `point_interception`. Il est aussi capable
// de se dessiner et de donner son extension spatiale approximative.
class Objet {
public:
Objet () {}
Objet& operator= (const Objet&) = default;
Objet (const Objet&) = default;
virtual ~Objet () {}
// L'objet intercepte-t-il le rayon ? Si oui, donne la distance géométrique au carré
// `dist2` parcourue par le rayon depuis son point d'émission, et renvoie une structure
// interne à utiliser éventuellement pour la ré-émission du même rayon.
// Si non, `intercept_struct` est nul et `dist2 = Inf`
// Attention, la création de std::shared_ptr<void> est délicate (https://stackoverflow.com/questions/25858939/is-there-a-way-to-cast-shared-ptrvoid-to-shared-ptrt/25858963 et voir ObjetCourbe::essai_intercept)
struct intercept_t { float dist2; std::shared_ptr<void> intercept_struct; };
virtual intercept_t essai_intercept (const Rayon& ray) const = 0;
// Point d'interception. Aucune garantie, non défini par défaut.
virtual std::optional<point_t> point_interception (std::shared_ptr<void> intercept_struct) const { return std::nullopt; }
// Extension spatiale approximative pessimiste de l'objet à fin d'optimisation
// (ne pas avoir à appeller un coûteux `essai_intercept(ray)` lorsque qu'on est
// certain que le rayon ne sera pas intercepté par l'objet)
struct extension_t { point_t pos; float rayon; };
virtual extension_t objet_extension () const = 0;
// Ré-émission du rayon, devant utiliser la structure `.intercept_struct` renvoyée par `essai_intercept(ray)`
virtual std::vector<Rayon> re_emit (const Rayon& ray, std::shared_ptr<void> intercept) = 0;
// Rendu graphique de l'objet
virtual void dessiner (sf::RenderWindow& window, bool emphasize) const = 0;
// Rendu graphique de l'interception d'un rayon (voir re_emit)
virtual void dessiner_interception (sf::RenderWindow& window, const Rayon& ray, std::shared_ptr<void> intercept) const = 0;
};
#endif

View File

@ -0,0 +1,50 @@
#include "ObjetDiffusant.h"
#include <cmath>
decltype(ObjetCourbe_Diffusant::BRDF) ObjetCourbe_Diffusant::BRDF_Lambert = [] (float theta_i, float theta_r) -> float {
return 1;
};
// Diffusion du rayon incident
//
std::vector<Rayon> ObjetCourbe_Diffusant::re_emit (const Rayon& ray, std::shared_ptr<void> interception) {
intercept_courbe_t& intercept = *(intercept_courbe_t*)interception.get();
std::vector<Rayon> rayons;
// nombre de rayons ré-émis selon l'intensité du rayon incident
size_t n_re_emit = std::max<size_t>(1, lroundf(n_re_emit_par_intens * ray.spectre.intensite_tot()));
for (size_t k = 0; k < n_re_emit; k++) {
float ang_refl = 0;
switch (diff_met) {
case diffus_methode_t::AleaUnif:
ang_refl = M_PI/2 * (1-2*rand01()); break; // angles aléatoires équirépartis
case diffus_methode_t::Equirep:
ang_refl = M_PI/2 * (1-2*(k+1)/(float)(n_re_emit+1)); break; // angles déterministes équirépartis
default:
throw std::runtime_error("TODO");
}
Rayon ray_refl;
ray_refl.orig = intercept.p_incid;
ray_refl.dir_angle = intercept.ang_normale + ang_refl;
// pondération de l'intensité par la BRDF en fonction de l'angle incident et réfléchi
ray_refl.spectre = ray.spectre;
if (not BRDF_lambda) {
float ampl = albedo / n_re_emit * BRDF( intercept.ang_incid, ang_refl );
ray_refl.spectre.for_each([&] (float, pola_t, float& I) {
I *= ampl;
});
} else {
ray_refl.spectre.for_each([&] (float lambda, pola_t, float& I) {
I *= albedo / n_re_emit * BRDF_lambda( intercept.ang_incid, ang_refl, lambda );
});
}
rayons.push_back(std::move(ray_refl));
}
return rayons;
}

View File

@ -0,0 +1,53 @@
/*******************************************************************************
* Surfaces diffusantes à distribution de réflectivité (BRDF) arbitraire.
*******************************************************************************/
#ifndef _LIGHTRAYS_DIFFUS_H_
#define _LIGHTRAYS_DIFFUS_H_
#include "ObjetsCourbes.h"
class ObjetCourbe_Diffusant : virtual public ObjetCourbe {
public:
// Méthode de tirage de rayon (même résultats en moyenne, mais performances et fluctuations différentes)
enum diffus_methode_t {
Equirep, // Équirépartition des rayons sur tous les angles i_r avec modulation d'amplitude par la BRDF
AleaUnif, // Émission aléatoire uniforme avec modulation d'amplitude par la BRDF
MonteCarlo, // Tirage de rayons d'intensité constante avec une distribution respectant la BRDF (Metropolis)
} diff_met;
// Bidirectional reflectance distribution function, possiblement dépendante de la couleur
std::function<float(float theta_i, float theta_r)> BRDF; // utilisée si `BRDF_lambda` nulle
std::function<float(float theta_i, float theta_r, float lambda)> BRDF_lambda; // utilisée si non nulle
// Albedo, entre 0 et 1 (simple facteur d'intensité)
float albedo;
// Nombre moyen de rayons ré-émis par rayon incident par unité d'intensité. Doit être grand si diffus_methode_t::Equirep utilisée.
float n_re_emit_par_intens;
static decltype(BRDF) BRDF_Lambert; // Diffusion lambertienne (isotrope <=> loi en cos(θ) <=> BRDF = 1)
static decltype(BRDF) BRDF_Oren_Nayar (float sigma); // Diffusion de Oren Nayar
ObjetCourbe_Diffusant (decltype(BRDF) BRDF) : diff_met(AleaUnif), BRDF(BRDF), BRDF_lambda(nullptr), albedo(1), n_re_emit_par_intens(1) {}
ObjetCourbe_Diffusant& operator= (const ObjetCourbe_Diffusant&) = default;
ObjetCourbe_Diffusant (const ObjetCourbe_Diffusant&) = default;
virtual ~ObjetCourbe_Diffusant () {};
// Diffusion du rayon incident
virtual std::vector<Rayon> re_emit (const Rayon& ray, std::shared_ptr<void> intercept) override final;
};
class ObjetArc_Diffusant : virtual public ObjetCourbe_Diffusant, virtual public ObjetArc {
public:
// relai des arguments aux constructeurs de ObjetArc
template<typename... Args> ObjetArc_Diffusant (decltype(BRDF) BRDF, Args&&... x) : ObjetCourbe_Diffusant(BRDF), ObjetArc(x...) {}
virtual ~ObjetArc_Diffusant () {}
};
class ObjetLigne_Diffusant : virtual public ObjetCourbe_Diffusant, virtual public ObjetLigne {
public:
// relai des arguments aux constructeurs de ObjetLigne
template<typename... Args> ObjetLigne_Diffusant (decltype(BRDF) BRDF, Args&&... x) : ObjetCourbe_Diffusant(BRDF), ObjetLigne(x...) {}
virtual ~ObjetLigne_Diffusant () {};
};
#endif

View File

@ -0,0 +1,94 @@
#include "ObjetMilieux.h"
#include <cmath>
void ObjetComposite_LignesMilieu::re_positionne (point_t o) {
std::optional<vec_t> trsl = std::nullopt;
for (auto& obj : this->comp) {
ObjetLigne_Milieux& ligne = *dynamic_cast<ObjetLigne_Milieux*>(&(*obj));
if (not trsl.has_value())
trsl = o - ligne.a;
ligne.a = ligne.a + *trsl;
ligne.b = ligne.b + *trsl;
}
}
// Réflexion et réfraction sur un dioptre : lois de Snell-Descartes
// et coefficients de Fresnel. Voir lois.pdf pour plus de détails.
// Deux cas : indice de réfraction indep. de λ (peu cher) et dépendant
// de λ (cher car séparation en N_COULEURS différentes)
//
std::vector<Rayon> ObjetCourbe_Milieux::re_emit (const Rayon& ray, std::shared_ptr<void> interception) {
intercept_courbe_t& intercept = *(intercept_courbe_t*)interception.get();
std::vector<Rayon> rays_emis;
Rayon ray_refl;
ray_refl.orig = intercept.p_incid;
ray_refl.dir_angle = intercept.ang_normale - intercept.ang_incid; // i_refl = i par rapport à la normale
ray_refl.spectre = ray.spectre;
auto refracte = [&intercept,&rays_emis] (Rayon& ray_refl, float n_out, float n_in) {
float gamma = intercept.sens_reg ? n_out/n_in : n_in/n_out; // n1/n2
float s = gamma * sinf(intercept.ang_incid);
if (fabsf(s) <= 1) { // on a un rayon transmis (i <= i_critique)
Rayon ray_trsm;
ray_trsm.orig = intercept.p_incid;
ray_trsm.dir_angle = (intercept.ang_normale + M_PI) + asinf(s);
float b = sqrtf( 1 - s*s );
float cosi = cosf(intercept.ang_incid);
float a_TE = gamma * cosi, a_TM = cosi / gamma;
float r_coeff[2];
r_coeff [PolTE] = (a_TE - b) / (a_TE + b);
r_coeff [PolTM] = (a_TM - b) / (a_TM + b);
Specte::for_each_manual([&] (uint8_t i, float lambda, pola_t pol) {
float R = r_coeff[pol] * r_coeff[pol];
ray_trsm.spectre.comps[i] = (1-R) * ray_refl.spectre.comps[i];
ray_refl.spectre.comps[i] = R * ray_refl.spectre.comps[i];
});
rays_emis.push_back(ray_trsm);
}
// sinon, pas de rayon transmis (i > i_critique), et ray_refl est déjà prêt à être envoyé
rays_emis.push_back(ray_refl);
};
// indice de réfraction fixe
if (not (bool)n_lambda) {
refracte (ray_refl, /*n_out*/1., /*n_in*/this->n_fixe);
}
// indice de réfraction dépendant de la longueur d'onde
// -> séparation de toutes les composantes en rayons monochromatiques, car les directions sont différentes
else {
for (color_id_t i = 0; i < N_COULEURS; i++) {
Rayon ray_refl_mono = ray_refl;
ray_refl_mono.spectre.for_each_cid([&] (color_id_t cid, pola_t pol, float& I) {
if (cid != i)
I = 0;
});
float n_in = n_lambda(lambda_color[i]); // variable
refracte (ray_refl_mono, /*n_out*/1., n_in);
}
}
return rays_emis;
}
#include "sfml_c01.hpp"
void ObjetLigne_Milieux::dessiner (sf::RenderWindow& window, bool emphasize) const {
ObjetLigne::dessiner(window, emphasize);
sf::ConvexShape rect_milieu(4);
vec_t v_perp = vecteur_u_perp() * 0.05;
rect_milieu.setPoint(0, sf::c01::toWin(a));
rect_milieu.setPoint(1, sf::c01::toWin(b));
rect_milieu.setPoint(2, sf::c01::toWin(b+v_perp));
rect_milieu.setPoint(3, sf::c01::toWin(a+v_perp));
rect_milieu.setFillColor(sf::Color(255,255,255,20));
window.draw(rect_milieu);
}

92
LightRays/ObjetMilieux.h Normal file
View File

@ -0,0 +1,92 @@
/*******************************************************************************
* Objets optiques définissant différents milieux : dioptres entre deux milieux
* d'indices différents : loi de Snell-Descartes et coefficients de Fresnel
*******************************************************************************/
// Notes : l'incide de réfraction `n` est réel, donc pas d'absorption, pas de comportement métalique.
// De plus, les objets sont supposés être des dioptres entre du vide (n=1) et un milieu (n≠1).
// Pour faire des interfaces entre milieux, ça nécessiterait pas mal de travail.
#warning To do
#ifndef _LIGHTRAYS_OBJET_MILIEU_H_
#define _LIGHTRAYS_OBJET_MILIEU_H_
#include "ObjetsCourbes.h"
//------------------------------------------------------------------------------
// Dioptres de type courbe (1D) entre le vide et un milieu d'indice n, fixe
// (`n_fixe`) ou dépendnant de λ (`n_lambda`). Implémentation des lois de
// Snell-Descartes et des coefficients de Fresnel en intensité.
class ObjetCourbe_Milieux : virtual public ObjetCourbe {
public:
std::function<float(float)> n_lambda; // n(λ) /!\ coûteux -> N_COULEURS rayons réfractés à lancer
float n_fixe; // n indépendant de λ, considéré si `indice_refr_lambda` est nul
ObjetCourbe_Milieux (float incide_refr_fixe) : ObjetCourbe(), n_lambda(nullptr), n_fixe(incide_refr_fixe) {}
ObjetCourbe_Milieux (std::function<float(float)> indice_refr_lambda) : ObjetCourbe(), n_lambda(indice_refr_lambda) {}
ObjetCourbe_Milieux& operator= (const ObjetCourbe_Milieux&) = default;
ObjetCourbe_Milieux (const ObjetCourbe_Milieux&) = default;
virtual ~ObjetCourbe_Milieux () {}
// Ré-émission du rayons intercepté en un rayon réfléchi et un rayon réfracté
virtual std::vector<Rayon> re_emit (const Rayon& ray, std::shared_ptr<void> intercept) override final;
};
//------------------------------------------------------------------------------
// Dioptre linéaire : ObjetLigne + ObjetCourbe_Milieux
class ObjetLigne_Milieux : virtual public ObjetCourbe_Milieux, virtual public ObjetLigne {
public:
// relai des arguments aux contructeurs de ObjetLigne et ObjetCourbe_Milieux
// (toutes les combinaisons sont possibles)
template<typename incide_refr_t, typename... Args> ObjetLigne_Milieux (incide_refr_t incide_refr, Args&&... x) :
ObjetCourbe_Milieux(incide_refr), ObjetLigne(x...) {}
virtual ~ObjetLigne_Milieux () {}
// Dessin (intérieur coloré)
virtual void dessiner (sf::RenderWindow& window, bool emphasize) const override;
};
//------------------------------------------------------------------------------
// Dioptre en forme d'arc de cercle : ObjetArc + ObjetCourbe_Milieux
class ObjetArc_Milieux : virtual public ObjetCourbe_Milieux, virtual public ObjetArc {
public:
// relai des arguments aux contructeurs de ObjetArc et ObjetCourbe_Milieux
template<typename incide_refr_t, typename... Args> ObjetArc_Milieux (incide_refr_t incide_refr, Args&&... x) :
ObjetCourbe_Milieux(incide_refr), ObjetArc(x...) {}
virtual ~ObjetArc_Milieux () {}
};
//------------------------------------------------------------------------------
// Utilitaire : Objet composite fermé, délimité par des lignes
// `ObjetLigne_Milieux`, à partir d'une liste de points `points`,
// et d'indice de réfraction intérieur `incide_refr`.
class ObjetComposite_LignesMilieu : virtual public ObjetComposite {
private:
template<typename incide_refr_t>
static std::vector< std::unique_ptr<ObjetCourbe> > construct_liste (std::vector<point_t> points, incide_refr_t n) {
std::vector< std::unique_ptr<ObjetCourbe> > objs;
size_t N = points.size();
for (size_t i = 0; i < N; i++)
objs.emplace_back(new ObjetLigne_Milieux(n, points[ i ], points[ (i+1)%N ]));
return objs;
}
public:
// construction à partir de la liste de points
template<typename incide_refr_t> ObjetComposite_LignesMilieu (std::vector<point_t> points, incide_refr_t incide_refr) :
ObjetComposite(construct_liste(points,incide_refr)) {}
virtual ~ObjetComposite_LignesMilieu () {}
// simple translation : positionnement du premier point de la chaine en `o`
void re_positionne (point_t o);
};
#endif

302
LightRays/ObjetsCourbes.cpp Normal file
View File

@ -0,0 +1,302 @@
#include "ObjetsCourbes.h"
#include <cmath>
#include <stdexcept>
///------------------------ ObjetCourbe ------------------------///
#define INTERCEPTION_DIST_MINIMALE 0.0001
// Relai de ObjetCourbe::essai_intercept_courbe
//
Objet::intercept_t ObjetCourbe::essai_intercept (const Rayon& ray) const {
auto intercept = this->essai_intercept_courbe(ray);
if (intercept.has_value()) {
float dist2 = (ray.orig - (*intercept)->p_incid).norm2();
// on introduit une distance minimale qu'un rayon peut parcourit avant d'être intercepté
// par une courbe pour éviter qu'un objet ré-intercepte immédiatement le rayon qu'il vient
// d'émettre, ce qui arriverait souvent en simple précision
if (dist2 < INTERCEPTION_DIST_MINIMALE*INTERCEPTION_DIST_MINIMALE)
return { .dist2 = Inf, .intercept_struct = nullptr };
else
return { .dist2 = dist2,
.intercept_struct = std::static_pointer_cast<void>(*intercept) };
} else
return { .dist2 = Inf, .intercept_struct = nullptr };
}
std::optional<point_t> ObjetCourbe::point_interception (std::shared_ptr<void> intercept_struct) const {
if (intercept_struct) {
return ((intercept_courbe_t*)intercept_struct.get())->p_incid;
} else
return std::nullopt;
}
///------------------------ ObjetLigne ------------------------///
ObjetLigne::ObjetLigne (point_t a, float l, float ang) :
a( a ),
b( a + l * vec_t{ cosf(ang), sinf(ang) } ) {}
vec_t ObjetLigne::vecteur_u_perp () const {
vec_t v_perp = (b - a).rotate_p90();
return v_perp / !v_perp;
}
// Routine d'intersection segment avec demi-droite
//
std::optional<ObjetLigne::intersection_segdd_t> ObjetLigne::intersection_segment_demidroite (point_t a, point_t b, point_t dd_orig, float angle) {
// cas particulier d'alignement non pris en compte
vec_t u_dd = { .x = cosf(angle),
.y = sinf(angle) };
vec_t v_seg = a - b;
float s, t;
mat22_sol(v_seg.x, -u_dd.x,
v_seg.y, -u_dd.y,
dd_orig.x - b.x, dd_orig.y - b.y,
s, t);
if ((0 <= s and s <= 1) and t >= 0) {
return intersection_segdd_t{ .v_seg = v_seg, .u_dd = u_dd, .s_seg = s, .t_dd = t };
} else
return std::nullopt;
}
// Test d'interception du rayon sur la ligne.
//
std::optional<std::shared_ptr<ObjetCourbe::intercept_courbe_t>> ObjetLigne::essai_intercept_courbe (const Rayon& ray) const {
// `intersection_segment_demidroite` n'est pas directement intégrée ici car elle sert ailleurs
auto isect = ObjetLigne::intersection_segment_demidroite(a, b, ray.orig, ray.dir_angle);
if (not isect.has_value())
return std::nullopt;
std::shared_ptr<intercept_ligne_t> intercept = std::make_shared<intercept_ligne_t>();
// point d'incidence
intercept->p_incid = ray.orig + isect->t_dd * isect->u_dd;
intercept->s_incid = isect->s_seg;
// angle d'incidence
float seg_angle = atan2f(isect->v_seg.y, isect->v_seg.x); // pourrait être calculé une bonne fois pour toutes
float alpha = angle_mod2pi_11(ray.dir_angle);
float i = alpha - (seg_angle - M_PI/2);
if (fabsf(angle_mod2pi_11(i)) < M_PI/2) {
intercept->ang_normale = seg_angle + M_PI/2;
intercept->ang_incid = i;
intercept->sens_reg = true;
} else {
intercept->ang_incid = i - M_PI;
if (intercept->ang_incid < -M_PI/2) intercept->ang_incid += 2*M_PI;
intercept->ang_normale = seg_angle - M_PI/2;
intercept->sens_reg = false;
}
return std::shared_ptr<intercept_courbe_t>(intercept);
}
///------------------------ ObjetArc ------------------------///
// Construction de l'arc de cercle à partir de deux points et du rayon
//
ObjetArc::ObjetArc (point_t a, point_t b, float radius, bool inv_interieur) : c({0,0}), R(radius), ang(0,0), inv_int(inv_interieur) {
// translation
vec_t ab = b - a;
// vérification
float ab2 = ab.norm2();
if (ab2 > 4*R*R)
throw std::domain_error("ObjetArc(A,B,R) : points A et B trop éloignés");
// rotation
float theta0 = atan2f(ab.y, ab.x) - M_PI/2;
// calcul dans le repère 'prime'
vec_t c_ = { .x = -sqrtf( R*R - ab2/4 ),
.y = sqrtf(ab2)/2 };
float theta_ = atanf( c_.y / c_.x );
angle_interv_t ang_ ( theta_, -theta_ );
// dé-rotation
ang = ang_ + theta0;
c_ = c_.rotate(theta0);
// dé-translation
c = a + c_;
}
std::pair<point_t,point_t> ObjetArc::objet_extremit () const {
vec_t u_a, u_b;
std::tie(u_a,u_b) = this->ang.vec_a_b();
return { c + R * u_a,
c + R * u_b };
}
// Routine d'interception du rayon sur l'arc de cercle.
//
std::optional<std::shared_ptr<ObjetArc::intercept_courbe_t>> ObjetArc::essai_intercept_courbe (const Rayon& ray) const {
/// intersection arc de cercle / demi-droite
// (notations de `lois.pdf`)
vec_t oc = c - ray.orig;
float b = !oc / R; // d/R
float base_ang = atan2f(oc.y, oc.x);
// angles relatifs à l'axe OC
float alpha = angle_mod2pi_11( ray.dir_angle - base_ang );
angle_interv_t thetaAB = ang + -base_ang;
// intersection avec le cecle si en dessous de l'angle critique
float y = b * sinf(alpha);
if ((b > 1.00001 and fabsf(alpha) >= M_PI/2) or fabsf(y) > 1)
return std::nullopt;
// angles repérant les points d'intersection avec le cercle
float arcsiny = asinf(y);
float theta1 = alpha - arcsiny + M_PI,
theta2 = alpha + arcsiny;
/// si on est bien sur notre arc de cercle
std::shared_ptr<intercept_arc_t> intercept = std::make_shared<intercept_arc_t>();
// θ1 n'est accessible que si la rayon vient de l'extérieur
if ( b > 1.00001 and thetaAB.inclus(theta1) ) {
intercept->sens_reg = !inv_int; // ext vers int du cerlce
intercept->theta_incid = intercept->ang_normale = theta1 + base_ang;
intercept->ang_incid = M_PI - theta1 + alpha; // c'est un angle relatif, pas besoin de base_ang
}
// test de θ1 pour b<1 ou si θ1 a échoué pour b>1
else if ( thetaAB.inclus(theta2) ) {
intercept->sens_reg = inv_int; // int vers ext du cercle
intercept->theta_incid = theta2 + base_ang;
intercept->ang_normale = intercept->theta_incid + M_PI; // normale vers l'intérieur du cercle
intercept->ang_incid = alpha - theta2;
}
else
return std::nullopt;
// calcul du point d'incidence
intercept->p_incid = c + R * vec_t{ .x = cosf(intercept->theta_incid),
.y = sinf(intercept->theta_incid) };
return std::shared_ptr<intercept_courbe_t>(intercept);
}
///------------------------ ObjetComposite ------------------------///
// Calcul de l'extension approximative d'un objet composite :
// • position = barycentre
// • extension = distance (position objet - barycentre) maximale,
// + extension (objet) maximale
// (non-optimal, mais se comporte bien dans la plupart des cas)
//
Objet::extension_t ObjetComposite::objet_extension () const {
std::vector<vec_t> pos (comp.size());
vec_t bary = {0,0};
float rayon_max = 0;
for (const auto& obj : comp) {
extension_t ext = obj->objet_extension();
pos.push_back( (vec_t)ext.pos );
bary += (vec_t)ext.pos;
rayon_max = std::max(rayon_max, ext.rayon);
}
bary /= comp.size();
float pos_max = 0;
for (vec_t p : pos)
pos_max = std::max(pos_max, !(p - bary));
return { .pos = {bary.x,bary.y}, .rayon = rayon_max + pos_max };
}
// Relai du test d'interception sur chaque sous-objet, et interception
// par l'objet le plus proche sur le chemin du rayon.
//
Objet::intercept_t ObjetComposite::essai_intercept (const Rayon& ray) const {
intercept_composite_t interception { .courbe_intercept = comp.end() };
float dist2_min = Inf;
// Test d'interception du rayon contre toutes les courbes composantes;
// la première interception en terme de distance entre l'origine du rayon
// et le point d'incidence est choisie (recherche de minimum)
for (auto it = comp.begin(); it != comp.end(); it++) {
std::optional<std::shared_ptr<ObjetCourbe::intercept_courbe_t>> intercept
= (*it)->essai_intercept_courbe(ray);
if (intercept.has_value()) {
float dist2 = (ray.orig - (*intercept)->p_incid).norm2();
if (dist2 < INTERCEPTION_DIST_MINIMALE*INTERCEPTION_DIST_MINIMALE)
continue;
if (dist2 < dist2_min) {
interception.courbe_intercept = it;
interception.intercept_struct = *intercept;
dist2_min = dist2;
}
}
}
return { .dist2 = dist2_min,
.intercept_struct = (interception.courbe_intercept == comp.end()) ?
nullptr :
std::make_shared<intercept_composite_t>(interception)
};
}
std::optional<point_t> ObjetComposite::point_interception (std::shared_ptr<void> interception) const {
if (interception) {
const intercept_composite_t& intercept = *(intercept_composite_t*)interception.get();
if (intercept.courbe_intercept != comp.end())
return intercept.intercept_struct->p_incid;
}
return std::nullopt;
}
// Simple ré-émission par la courbe qui a intercepté le rayon
//
std::vector<Rayon> ObjetComposite::re_emit (const Rayon& ray, std::shared_ptr<void> interception) {
const intercept_composite_t& intercept = *(intercept_composite_t*)interception.get();
return (*intercept.courbe_intercept)->re_emit(ray, intercept.intercept_struct);
}
// Test de fermeture
//
void ObjetComposite::test_fermeture () const {
if (comp.size() == 0)
throw std::logic_error("ObjetComposite : vide");
auto verif_egaux = [] (point_t p, point_t p_bis) {
if ((p - p_bis).norm2() > 1e-10)
throw std::logic_error("ObjetComposite : courbes non bout-à-bout");
};
point_t beg, cur;
std::tie(beg, cur) = this->comp[0]->objet_extremit();
for (size_t i = 1; i < this->comp.size(); i++) {
point_t cur_bis, next;
std::tie(cur_bis, next) = this->comp[i]->objet_extremit();
verif_egaux(cur, cur_bis);
cur = next;
}
verif_egaux(beg, cur);
}
///------------------------ Affichages ------------------------///
#include "sfml_c01.hpp"
void ObjetLigne::dessiner (sf::RenderWindow& window, bool emphasize) const {
auto line = sf::c01::buildLine(this->a, this->b, emphasize ? sf::Color::White : sf::Color(200,200,200));
window.draw(line);
}
void ObjetCourbe::dessiner_interception (sf::RenderWindow& window, const Rayon& ray, std::shared_ptr<void> interception) const {
const intercept_courbe_t& intercept = *(intercept_courbe_t*)interception.get();
// dessin du rayon source -> objet
auto c = ray.spectre.rgb256_noir_intensite(false, std::nullopt);
auto line = sf::c01::buildLine(ray.orig,
intercept.p_incid,
sf::Color(std::get<0>(c), std::get<1>(c), std::get<2>(c), 255));
window.draw(line);
// dessin de la normale au point d'interception
vec_t v = 0.03 * vec_t{ .x = cosf(intercept.ang_normale), .y = sinf(intercept.ang_normale) };
line = sf::c01::buildLine(intercept.p_incid+(-0.2)*v,
intercept.p_incid+(+0.8)*v,
intercept.sens_reg ? sf::Color(255,200,200) : sf::Color(200,200,255));
window.draw(line);
}
void ObjetArc::dessiner (sf::RenderWindow& window, bool emphasize) const {
size_t N = R * 100 * ang.longueur();
if (N < 3) N = 3;
std::vector<point_t> pts (N+1);
for (size_t k = 0; k <= N; k++) {
float theta = ang.beg() + ang.longueur() * (float) k / N;
pts[k] = c + R * vec_t{ .x = cosf(theta),
.y = sinf(theta) };
}
auto arc = sf::c01::buildLineStrip(pts, sf::Color::White);
window.draw(arc);
}
void ObjetComposite::dessiner (sf::RenderWindow& window, bool emphasize) const {
for (const auto& p : this->comp)
p->dessiner(window, emphasize);
}
void ObjetComposite::dessiner_interception (sf::RenderWindow& window, const Rayon& ray, std::shared_ptr<void> interception) const {
const intercept_composite_t& intercept = *(intercept_composite_t*)interception.get();
(*intercept.courbe_intercept)->dessiner_interception(window, ray, intercept.intercept_struct);
}

161
LightRays/ObjetsCourbes.h Normal file
View File

@ -0,0 +1,161 @@
/*******************************************************************************
* Objets optiques génériques de type courbes : ces classes virtuelles définissent
* la géométrie des objets (lignes, arcs de cercle, composites) pour mettre en
* commun les routines géométriques d'interception et de -émission de rayons lumineux.
*******************************************************************************/
#ifndef _LIGHTRAYS_OBJETS_COURBES_H_
#define _LIGHTRAYS_OBJETS_COURBES_H_
#include "Objet.h"
//------------------------------------------------------------------------------
// Objet optique courbe. Déclare la structure d'interception commune donnant
// le point d'intersection, l'angle d'incidence (angle du rayon à la normale),
// l'angle absolu `ang_normale` du vecteur normal à la courbe (par rapport à
// l'horizontale), et le sens d'incidence du rayon `sens_reg` (l'objet étant
// orienté; `true` si même sens que sur les figures du document).
class ObjetCourbe : virtual public Objet {
public:
struct intercept_courbe_t {
point_t p_incid;
float ang_incid;
float ang_normale;
bool sens_reg;
};
// Pour faciliter l'utilisation des `ObjetCourbe`, la méthode `essai_intercept_courbe` revoie
// directement un pointeur de `intercept_courbe_t`, au lieu de la structure opaque renvoyée par `essai_intercept`.
virtual std::optional<std::shared_ptr<intercept_courbe_t>> essai_intercept_courbe (const Rayon& ray) const = 0;
// `essai_intercept` est alors une simple redirection vers `essai_intercept_courbe` et calcul de distance
virtual Objet::intercept_t essai_intercept (const Rayon& ray) const override final;
// Le point d'interception est toujours défini pour les `ObjetCourbe`
virtual std::optional<point_t> point_interception (std::shared_ptr<void> intercept_struct) const override;
// Extrémités de la courbe. Utilisé pour vérifier qu'un `ObjetComposite` est fermé.
virtual std::pair<point_t,point_t> objet_extremit () const = 0;
// Dessin de l'interception : affiche le rayon et la normale
virtual void dessiner_interception (sf::RenderWindow& window, const Rayon& ray, std::shared_ptr<void> intercept) const override;
};
//------------------------------------------------------------------------------
// Objet optique de forme linéaire : segment du point `a` au point `b`. Routine
// d'intersection rayon-segment implémentée ici. `essai_intercept_courbe` renvoie
// une stucture `intercept_t` donnant, en plus de `intercept_curve_t`, l'abscisse
// du point d'interception `s_incid` ∈ [0,1] sur le segment a-b
class ObjetLigne : virtual public ObjetCourbe {
public:
point_t a, b;
ObjetLigne (point_t pos_a, point_t pos_b) : a(pos_a), b(pos_b) {} // point a et b
ObjetLigne (point_t pos_a, float l_b, float ang_b); // point a, longueur, et angle (horizontale,a,b)
ObjetLigne& operator= (const ObjetLigne&) = default;
ObjetLigne (const ObjetLigne&) = default;
virtual ~ObjetLigne () {}
// Routine d'intersection segment [seg_a,seg_b] avec demi-droite définie par son origine `o_droite` et un angle `ang_droite`.
// Retrourne (si intersection) le vecteur segment et le vecteur unitaire rayon (pour opti) et l'abscisse `s_seg` sur le segment et `t_dd` de la demi-droite du point d'intersection
struct intersection_segdd_t { vec_t v_seg; vec_t u_dd; float s_seg; float t_dd; };
static std::optional<intersection_segdd_t> intersection_segment_demidroite (point_t seg_a, point_t seg_b, point_t o_droite, float ang_droite);
// Test d'interception du rayon sur la ligne.
// Si interception, renvoie un pointeur de intercept_ligne_t
struct intercept_ligne_t : public ObjetCourbe::intercept_courbe_t {
float s_incid;
};
std::optional<std::shared_ptr<intercept_courbe_t>> essai_intercept_courbe (const Rayon& ray) const override final;
// Extension et extrémités du segment
virtual extension_t objet_extension () const override { return { .pos = a + (b-a)/2, .rayon = !(b-a) }; }
virtual std::pair<point_t,point_t> objet_extremit () const override { return {a, b}; }
// Dessin
virtual void dessiner (sf::RenderWindow& window, bool emphasize) const override;
vec_t vecteur_u_perp () const; // vecteur unitaire perpendiculaire au segment
};
//------------------------------------------------------------------------------
// Objet optique en forme d'arc de cercle, de centre `c`, de rayon `R`, entre les
// deux angles par rapport à l'horizontale définis par l'intervalle angulaire
// `ang` dans le sens trigonométrique. Routine d'intersection rayon-arc de cercle
// implémentée ici `courbe_intercept` renvoie une stucture `intercept_arc_t`
// donnant, en plus de `intercept_curve_t`, l'angle absolu (par rapport à
// l'horizontale) `theta_incid` du point d'incidence (dans l'intervalle `ang`).
// Si `inv_int`=true, l'intérieur du cercle est marqué comme étant le milieu extérieur.
class ObjetArc : virtual public ObjetCourbe {
public:
point_t c;
float R;
angle_interv_t ang;
bool inv_int;
// construction par le centre, le rayon et l'intervalle angulaire
ObjetArc (point_t centre, float radius, angle_interv_t ang_interval, bool inv_interieur) : c(centre), R(radius), ang(ang_interval), inv_int(inv_interieur) {}
// construction du petit arc de cercle de rayon R passant par les points `a` et `b`
ObjetArc (point_t a, point_t b, float radius, bool inv_interieur);
ObjetArc& operator= (const ObjetArc&) = default;
ObjetArc (const ObjetArc&) = default;
virtual ~ObjetArc () {}
// Routine d'interception du rayon sur l'arc de cercle.
// Si interception, renvoie un pointeur de intercept_courbe_t
struct intercept_arc_t : public ObjetCourbe::intercept_courbe_t {
float theta_incid;
};
std::optional<std::shared_ptr<intercept_courbe_t>> essai_intercept_courbe (const Rayon& ray) const override final;
// Extension et extrémités de l'arc
virtual extension_t objet_extension () const override { return { .pos = c, .rayon = R }; }
virtual std::pair<point_t,point_t> objet_extremit () const override;
// Dessin
virtual void dessiner (sf::RenderWindow& window, bool emphasize) const override;
};
//------------------------------------------------------------------------------
// Objet optique fermé composé, délimité par des objets de type `ObjetCourbe`
// Le test de fermeture a seulement le statut d'une assertion à la construction,
// et n'est pas imposé comme un invariant (`comp` est public).
// Les méthodes d'interception, de ré-émission et de dessin sont, en gros,
// juste relayés aux sous-objets. Joue le rôle d'un conteneur.
class ObjetComposite : virtual public Objet {
protected:
// Liste des courbes composant cet objet
const std::vector< std::unique_ptr<ObjetCourbe> > comp;
void test_fermeture () const;
public:
// Construction à partir d'une liste de courbes
ObjetComposite (std::vector<std::unique_ptr<ObjetCourbe>>&& comp) : Objet(), comp(std::move(comp)) { test_fermeture(); }
ObjetComposite& operator= (const ObjetArc&) = delete; // pas de copie polymorphique des sous-objets
ObjetComposite (const ObjetArc&) = delete;
virtual ~ObjetComposite () {}
// Interception : si il y a, le rayon est simplement intercepté par l'objet le plus proche sur son chemin
struct intercept_composite_t {
decltype(comp)::const_iterator courbe_intercept; // itérateur de la courbe interceptée; `comp.end()` si pas d'interception
std::shared_ptr<ObjetCourbe::intercept_courbe_t> intercept_struct; // structure d'interception de cette courbe
};
virtual Objet::intercept_t essai_intercept (const Rayon& ray) const override;
virtual std::optional<point_t> point_interception (std::shared_ptr<void> intercept_struct) const override;
virtual extension_t objet_extension () const override;
// Simple ré-émission par le sous-objet qui a intercepté
virtual std::vector<Rayon> re_emit (const Rayon& ray, std::shared_ptr<void> intercept) override;
// Dessin
virtual void dessiner (sf::RenderWindow& window, bool emphasize) const override;
virtual void dessiner_interception (sf::RenderWindow& window, const Rayon& ray, std::shared_ptr<void> intercept) const override;
};
#endif

View File

@ -0,0 +1,77 @@
#include "ObjetsOptiques.h"
#include "sfml_c01.hpp"
#include <cmath>
Objet_MatriceTrsfUnidir::Objet_MatriceTrsfUnidir (point_t centre, float diam, float angv, mat_trsf_t m) : ObjetLigne({0,0}, {0,0}), mat_trsf(m) {
vec_t v = diam/2 * vec_t{ -sinf(angv), cosf(angv) };
a = centre + v;
b = centre + -v;
}
// Dessin d'un Objet_MatriceTrsfUnidir : on rajoute une ligne qui marque le côté sortant
//
void Objet_MatriceTrsfUnidir::dessiner (sf::RenderWindow& window, bool emphasize) const {
ObjetLigne::dessiner(window, emphasize);
point_t o = milieu_2points(a,b);
window.draw(sf::c01::buildLine(o, o + 0.05 * vecteur_u_perp(), sf::Color(100,100,100)));
}
// Objet_MatriceTrsfUnidir : Application de la matrice ABCD sur le rayon intercepté, dans le sens direct uniquement
//
std::vector<Rayon> Objet_MatriceTrsfUnidir::re_emit (const Rayon& ray, std::shared_ptr<void> interception) {
intercept_ligne_t& intercept = *(intercept_ligne_t*)interception.get();
std::vector<Rayon> rays;
if (intercept.sens_reg) {
float diam = !(a-b);
float y = (1 - 2 * intercept.s_incid) * diam/2; // élévation incidente
float yp = tanf(intercept.ang_incid); // pente incidente
float y2 = mat_trsf.A * y + mat_trsf.B * yp; // élévation en sortie
float y2p = mat_trsf.C * y + mat_trsf.D * yp; // pente en sortie
Rayon raytrsf = ray;
raytrsf.orig = milieu_2points(a,b) + y2 * (b-a)/diam;
raytrsf.dir_angle = atanf(y2p);
rays.push_back(std::move(raytrsf));
}
return rays;
}
// Objet_Filtre : filtrage du rayon incident : simple multiplication composante par composante du spectre par le spectre de transmission
//
std::vector<Rayon> Objet_Filtre::re_emit (const Rayon& ray, std::shared_ptr<void> interception) {
intercept_courbe_t& intercept = *(intercept_courbe_t*)interception.get();
Rayon ray_filtre = ray;
ray_filtre.orig = intercept.p_incid;
Specte::for_each_manual([&] (size_t i, float lambda, pola_t pol) -> void {
ray_filtre.spectre.comps[i] *= transm.comps[i];
});
return { std::move(ray_filtre) };
}
// Miroir : simple réflexion par rapport à la normale au point incident
//
std::vector<Rayon> ObjetCourbe_Miroir::re_emit (const Rayon& ray, std::shared_ptr<void> interception) {
intercept_courbe_t& intercept = *(intercept_courbe_t*)interception.get();
std::vector<Rayon> ray_emis;
Rayon ray_refl;
ray_refl.orig = intercept.p_incid;
ray_refl.dir_angle = intercept.ang_normale - intercept.ang_incid; // i_refl = i par rapport à la normale
ray_refl.spectre = ray.spectre;
ray_emis.push_back(std::move(ray_refl));
return ray_emis;
}
// Bilan d'énergie : accumulation des flux entrants et sortants
//
std::vector<Rayon> Objet_BilanEnergie::re_emit (const Rayon& ray, std::shared_ptr<void> interception) {
intercept_courbe_t& intercept = *(intercept_courbe_t*)interception.get();
Rayon rayon = ray;
rayon.orig = intercept.p_incid;
if (intercept.sens_reg) {
n_ray_in += 1;
flux_in += rayon.spectre.intensite_tot();
} else {
n_ray_out += 1;
flux_out += rayon.spectre.intensite_tot();
}
return { std::move(rayon) };
}

146
LightRays/ObjetsOptiques.h Normal file
View File

@ -0,0 +1,146 @@
/*******************************************************************************
* Objets d'optique géométrique divers : miroirs, bloqueur, filtre,
* matrice ABCD, bilan d'énergie
*******************************************************************************/
#ifndef _LIGHTRAYS_OPTIQUES_H_
#define _LIGHTRAYS_OPTIQUES_H_
#include "ObjetsCourbes.h"
//------------------------------------------------------------------------------
// Objet "matrice ABCD" unidirectionnel : objet linéaire transmettant les rayons
// inteceptés du côté direct, après multiplication des paramètres [y,y']
// (élévation par rapport au centre, pente par rapport à la normale) par la
// matrice de transert ABCD spécifiée.
class Objet_MatriceTrsfUnidir : virtual public ObjetLigne {
public:
// matrice de transfert
struct mat_trsf_t { float A, B, C, D; } mat_trsf;
// matrice de transfert d'une lentille
static mat_trsf_t mat_trsf_lentille (float f) { return {1, 0, -1/f, 1}; }
// matrice de transfert d'une propagation libre
static mat_trsf_t mat_trsf_propag (float l) { return {1, l, 0, 1}; }
// Constructeur : l'objet est défini par son centre optique, son diamètre, l'angle à la
// verticale (sens trigo), et par la matrice de transfert ABCD (y relatif au plan et
// au centre, et y' par rapport à la normale du plan). Sens de propagation direct vers +x
// (si |angle| < π/2). Les rayons arrivant dans le sens inverse ne sont pas traités.
Objet_MatriceTrsfUnidir (point_t centre, float diam, float ang_vertical, mat_trsf_t m);
Objet_MatriceTrsfUnidir& operator= (const Objet_MatriceTrsfUnidir&) = default;
Objet_MatriceTrsfUnidir (const Objet_MatriceTrsfUnidir&) = default;
virtual ~Objet_MatriceTrsfUnidir () {}
virtual std::vector<Rayon> re_emit (const Rayon& ray, std::shared_ptr<void> intercept) override;
virtual void dessiner (sf::RenderWindow& window, bool emphasize) const override final;
};
//------------------------------------------------------------------------------
// Bloqueur : simple objet linéaire absorbant tous les rayons interceptés.
class Objet_Bloqueur : virtual public ObjetLigne {
public:
Objet_Bloqueur (point_t pos_a, point_t pos_b) : ObjetLigne(pos_a,pos_b) {}
virtual ~Objet_Bloqueur () {}
virtual std::vector<Rayon> re_emit (const Rayon& ray, std::shared_ptr<void> intercept) override final {
return {}; // oublie simplement le rayon
}
};
//------------------------------------------------------------------------------
// Filtre : objet linéaire transmettant les rayons interceptés, mais modifiant
// leur spectre, multipliant chaque composante du spectre par un taux de
// transmission en intensité. Peut servir de filtre de couleur, de polariseur…
class Objet_Filtre : virtual public ObjetLigne {
public:
// Spectre de transmission du filtre, chaque composante de 0 à 1
Specte transm;
// Spectre de transmission (en intensité) complet spécifié.
// Pour chaque composante de `sp_transmission`, 0 -> composante du rayon absorbée, et 1 -> composante du rayon transmise totalement
template<typename... Args> Objet_Filtre (Specte sp_transmission, Args&&... x) : ObjetLigne(x...), transm(sp_transmission) {}
// Seule couleur transmise (quelque soit la polarisation) spécifiée
template<typename... Args> Objet_Filtre (color_id_t couleur_transmise, Args&&... x) : ObjetLigne(x...), transm(Specte::monochromatique(1, couleur_transmise)) {}
Objet_Filtre& operator= (const Objet_Filtre&) = default;
Objet_Filtre (const Objet_Filtre&) = default;
virtual ~Objet_Filtre () {}
// Transmission du rayon filtré
virtual std::vector<Rayon> re_emit (const Rayon& ray, std::shared_ptr<void> intercept) override;
};
//------------------------------------------------------------------------------
// Miroir courbe générique : simple réflexion des rayons tel que l'angle
// d'incidence est égal à l'angle du rayon réfléchi à la normale.
class ObjetCourbe_Miroir : virtual public ObjetCourbe {
public:
ObjetCourbe_Miroir () : ObjetCourbe() {}
virtual ~ObjetCourbe_Miroir () {}
// Réflexion parfaite du rayon
virtual std::vector<Rayon> re_emit (const Rayon& ray, std::shared_ptr<void> intercept) override final;
};
//------------------------------------------------------------------------------
// Miroir plan : ObjetCourbe_Miroir + ObjetLigne
class ObjetLigne_Miroir : virtual public ObjetCourbe_Miroir, virtual public ObjetLigne {
public:
// relai des arguments aux contructeurs de ObjetLigne
ObjetLigne_Miroir (point_t a, point_t b) : ObjetCourbe_Miroir(), ObjetLigne(a, b) {}
ObjetLigne_Miroir (point_t a, float l, float ang) : ObjetCourbe_Miroir(), ObjetLigne(a, l, ang) {}
virtual ~ObjetLigne_Miroir () {}
};
//------------------------------------------------------------------------------
// Miroir en arc de cercle : ObjetCourbe_Miroir + ObjetArc
class ObjetArc_Miroir : virtual public ObjetCourbe_Miroir, virtual public ObjetArc {
public:
// relai des arguments aux contructeurs de ObjetArc
ObjetArc_Miroir (point_t c, float R, angle_interv_t ang, bool inv_int) : ObjetCourbe_Miroir(), ObjetArc(c, R, ang, inv_int) {}
ObjetArc_Miroir (point_t a, point_t b, float R, bool inv_int) : ObjetCourbe_Miroir(), ObjetArc(a, b, R, inv_int) {}
virtual ~ObjetArc_Miroir () {}
};
//------------------------------------------------------------------------------
// Objet "bilan d'énergie" : intercepte les rayons sur un cercle et somme les
// flux entrants et sortants. Utile pour vérifier la conservation de l'intensité
// par un objet. Les rayons interceptés sont ré-émis à l'identique.
class Objet_BilanEnergie : virtual public ObjetArc {
private:
float flux_in, flux_out;
size_t n_ray_in, n_ray_out;
size_t n_acc;
public:
Objet_BilanEnergie (point_t centre, float radius) :
ObjetArc(centre, radius, angle_interv_t::cercle_entier, false),
flux_in(0), flux_out(0), n_ray_in(0), n_ray_out(0), n_acc(0) {}
Objet_BilanEnergie& operator= (const Objet_MatriceTrsfUnidir&) = delete;
Objet_BilanEnergie (const Objet_MatriceTrsfUnidir&) = delete;
virtual ~Objet_BilanEnergie () {}
// intercepte les rayons et accumule les flux entrants et sortants
virtual std::vector<Rayon> re_emit (const Rayon& ray, std::shared_ptr<void> intercept) override final;
// typiquement appelé à chaque frame, pour moyenner les valeurs sur plusieurs frames
void commit () { n_acc++; }
void reset () { flux_in = flux_out = 0; n_ray_in = n_ray_out = n_acc = 0; }
// statistiques de flux : intensité entrante, intensité sortante, nombre de rayons entrants et sortants
struct stats_par_frame_t {
float flux_in, flux_out, n_ray_in, n_ray_out;
};
stats_par_frame_t bilan () { return { flux_in/n_acc, flux_out/n_acc, n_ray_in/(float)n_acc, n_ray_out/(float)n_acc }; }
};
#endif

113
LightRays/Rayon.cpp Normal file
View File

@ -0,0 +1,113 @@
#include "Rayon.h"
#include "cmath"
// Spectre dont les composantes sont ajustées pour avoir à peu près du blanc en affichage RBG
const Specte spectre_blanc = Specte::polychromatique({{0.8,0.8,1.7,2.2}});
Specte Specte::monochromatique (float A, color_id_t color_id, std::optional<pola_t> pola_sel) {
if (color_id >= N_COULEURS)
throw std::out_of_range("identifiant couleur invalide");
Specte specte;
Specte::for_each_manual([&] (size_t i, float lambda, pola_t pol) {
if (pola_sel.has_value() and *pola_sel != pol) {
specte.comps[i] = 0;
} else {
if (color_id == i/2)
specte.comps[i] = A;
else
specte.comps[i] = 0;
}
});
return specte;
}
Specte Specte::polychromatique (std::array<float,N_COULEURS> I, std::optional<pola_t> pola_sel) {
Specte specte;
Specte::for_each_manual([&] (size_t i, float lambda, pola_t pol) {
if (pola_sel.has_value() and *pola_sel != pol) {
specte.comps[i] = 0;
} else {
specte.comps[i] = I[i/2];
}
});
return specte;
}
// Convert a given wavelength of light to an approximate RGB color value.
// The wavelength must be given in mm in the range from 380nm through 750nm.
// Based on code by Dan Bruton : http://www.physics.sfasu.edu/astro/color/spectra.html
std::tuple<float,float,float> wavelenght_to_rgb (float lamda, float gamma) {
float wavelength = lamda * 1e6;
float R = 0., G = 0., B = 0., attenuation;
if (wavelength >= 380 and wavelength <= 440) {
attenuation = 0.3 + 0.7 * (wavelength - 380) / (440 - 380);
R = powf( (-(wavelength - 440) / (440 - 380)) * attenuation, gamma);
B = powf( 1.0 * attenuation, gamma);
} else if (wavelength >= 440 and wavelength <= 490) {
G = powf( (wavelength - 440) / (490 - 440), gamma);;
B = 1.0;
} else if (wavelength >= 490 and wavelength <= 510) {
G = 1.0;
B = powf( -(wavelength - 510) / (510 - 490), gamma);
} else if (wavelength >= 510 and wavelength <= 580) {
R = powf( (wavelength - 510) / (580 - 510), gamma);
G = 1.0;
} else if (wavelength >= 580 and wavelength <= 645) {
R = 1.0;
G = powf( -(wavelength - 645) / (645 - 580), gamma);
} else if (wavelength >= 645 and wavelength <= 750) {
attenuation = 0.3 + 0.7 * (750 - wavelength) / (750 - 645);
R = powf( 1.0 * attenuation, gamma);
}
return { R, G, B };
}
struct rgb_cache_t {
std::array< std::tuple<float,float,float>, 2*N_COULEURS> comp_couleurs_rgb;
rgb_cache_t () {
Specte::for_each_manual([&] (uint8_t i_comp_array, float lambda, pola_t pol) {
comp_couleurs_rgb[i_comp_array] = wavelenght_to_rgb( lambda );
});
}
} rgb_cache;
std::tuple<uint8_t,uint8_t,uint8_t,bool> Specte::rgb256_noir_intensite (bool chroma_only, std::optional<pola_t> pola_sel) const {
float I_r = 0, I_g = 0, I_b = 0;
Specte::for_each_manual([&] (uint8_t i_comp_array, float lambda, pola_t pol) {
if (pola_sel.has_value() and *pola_sel != pol)
return;
float rc, gc, bc; std::tie(rc,gc,bc) = /* wavelenght_to_rgb(lambda) */ rgb_cache.comp_couleurs_rgb[i_comp_array];
I_r += rc * comps[i_comp_array];
I_g += gc * comps[i_comp_array];
I_b += bc * comps[i_comp_array];
});
I_r /= 2*N_COULEURS;
I_g /= 2*N_COULEURS;
I_b /= 2*N_COULEURS;
float max_I = std::max({I_r, I_g, I_b});
bool sat = (max_I > 1.);
if (sat or chroma_only) {
I_r /= max_I; I_g /= max_I; I_b /= max_I;
}
return { 255*I_r, 255*I_g, 255*I_b, sat };
}
float Specte::intensite_tot (pola_t pola_sel) const {
float I_tot = 0;
Specte::for_each_manual([&] (uint8_t i_comp_array, float, pola_t pol) {
if (pola_sel != pol)
return;
I_tot += comps[i_comp_array];
});
I_tot /= 2*N_COULEURS;
return I_tot;
}
float Specte::intensite_tot () const {
float I_tot = 0;
Specte::for_each_manual([&] (uint8_t i_comp_array, float, pola_t) {
I_tot += comps[i_comp_array];
});
I_tot /= 2*N_COULEURS;
return I_tot;
}

81
LightRays/Rayon.h Normal file
View File

@ -0,0 +1,81 @@
/*******************************************************************************
* Définission des rayons et des spectres, fonctions utilitaires sur les rayons.
*******************************************************************************/
#ifndef _LIGHTRAYS_RAYON_H_
#define _LIGHTRAYS_RAYON_H_
#include <array>
#include <functional>
#include <tuple>
#include "Util.h"
#define N_COULEURS 4
// Longueurs d'ondes des `N_COULEURS` composantes des rayons
constexpr float lambda_color [N_COULEURS] = {
7e-4, 6e-4, 5e-4, 4e-4
};
// La position dans ce tableau définit un `color_id_t`
typedef uint8_t color_id_t;
// Conversion longueur d'onde -> RGB pour affichage
std::tuple<float,float,float> wavelenght_to_rgb (float lamda_mm, float gamma = 0.8);
// Polarisation d'un rayonnement.
// Puisque l'on ne s'intéresse qu'à l'optique des réfractions et réflexions, seul
// compte le caractère TE ou TM. Et puisque l'on est en 2D, ce caractère est invariant
// dans une réflexion ou une réfration, où le rayon reste dans le plan 2D.
enum pola_t { PolTE = 0, PolTM = 1 };
// Spectre en intensité/puissance (suivant le contexte) d'un rayonnement, discrétisé en `N_COULEURS`
// (×2 pour la polarisation TE/TM) composantes de longueur d'onde définies `lambda_color`.
// La manipulation de `AmplComp::comps` doit se faire systématiquement avec for_each ou for_each_manual { ampl[i_comp_array] }.
//
struct Specte {
std::array<float,2*N_COULEURS> comps;
inline static void for_each_manual (std::function<void(uint8_t i_comp_array, float lambda, pola_t pol)> f) {
for (uint8_t i = 0; i < 2*N_COULEURS; i++)
f(i, lambda_color[i/2], i%2==0 ? pola_t::PolTE : pola_t::PolTM);
}
inline void for_each (std::function<void(float lambda, pola_t pol, float& I)> f) { // version mutable avec lambda
for_each_manual([&] (uint8_t i, float lambda, pola_t pol) { f(lambda, pol, this->comps[i]); });
}
inline void for_each_cid (std::function<void(color_id_t cid, pola_t pol, float& I)> f) { // version mutable avec id de couleur
for_each_manual([&] (uint8_t i, float, pola_t pol) { f(i/2, pol, this->comps[i]); });
}
inline void for_each (std::function<void(float lambda, pola_t pol, float I)> f) const { // version constante avec lambda
for_each_manual([&] (uint8_t i, float lambda, pola_t pol) { f(lambda, pol, this->comps[i]); });
}
static Specte monochromatique (float I, color_id_t id, std::optional<pola_t> pol = std::nullopt);
static Specte polychromatique (std::array<float,N_COULEURS> I, std::optional<pola_t> pol = std::nullopt);
// Sommation des intensités de chaque composante (intégration sur tout le spectre),
// et division par le nombre de composantes (2×`N_COULEURS`).
// Optionellement, sélectionne seulement les composantes d'une polarisation donnée.
float intensite_tot () const;
float intensite_tot (pola_t pola_sel) const;
// Conversion du spectre en couleur RGB pour affichage sur fond noir, par rapport à une intensité
// de saturation de 1.0 _par composante_. Si `chroma_only` est vrai, ignore l'intensité lumineuse.
// Le booléen indique si il y a saturation.
// Optionellement, sélectionne seulement les composantes d'une polarisation donnée.
// [TODO] Pour un affichage sur fond quelconque, il faudrait convertir la luminance en transparence.
std::tuple<uint8_t,uint8_t,uint8_t,bool> rgb256_noir_intensite (bool chroma_only, std::optional<pola_t> pola_sel = std::nullopt) const;
};
// Spectre tel que l'affichage RGB est à peu près blanc
extern const Specte spectre_blanc;
// Définition d'un rayon : son origine, son angle à l'horizontale
// et son spectre en intensité associé.
//
struct Rayon {
point_t orig;
float dir_angle;
Specte spectre;
};
#endif

140
LightRays/Scene.cpp Normal file
View File

@ -0,0 +1,140 @@
#include "Scene.h"
#include <cmath>
#include "sfml_c01.hpp"
// Test d'interception du rayon contre toutes les objets de la scène puis renvoi des
// rayons ré-émis; la première interception sur le trajet du rayon est choisie.
// Voir ObjetComposite::essai_intercept_composite pour un code similaire.
//
std::vector<Rayon> Scene::interception_re_emission (const Rayon& ray) {
decltype(objets)::const_iterator objet_intercept = objets.end();
std::shared_ptr<void> intercept_struct;
float dist2_min = Inf;
for (auto it = objets.begin(); it != objets.end(); it++) {
// Objet::extension_t ex = (*it)->objet_extension();
// Todo. Pour que ça puisse apporter quelque chose, il faut que ça soit calculé à l'avance et faire une grille et un test très rapide
#warning To do
Objet::intercept_t intercept = (*it)->essai_intercept(ray);
if (intercept.dist2 < dist2_min) {
dist2_min = intercept.dist2;
objet_intercept = it;
intercept_struct = intercept.intercept_struct;
}
}
if (objet_intercept == objets.end())
return {};
else {
if (propag_intercept_dessin_window != nullptr)
(*objet_intercept)->dessiner_interception(*propag_intercept_dessin_window, ray, intercept_struct);
if (propag_rayons_dessin_window != nullptr) {
auto p = (*objet_intercept)->point_interception(intercept_struct);
if (p.has_value()) {
float c = propag_rayons_dessin_gain * ray.spectre.intensite_tot();
auto line = sf::c01::buildLine(ray.orig, *p, sf::Color(255, 255, 255, (uint8_t)std::min(255.f,c)));
propag_rayons_dessin_window->draw(line);
}
}
if (propag_intercept_cb)
propag_intercept_cb(**objet_intercept, ray, intercept_struct);
return (*objet_intercept)->re_emit(ray, intercept_struct);
}
}
// Fonction récurrente : interception par les objets de la scène puis ré-émission
// avec la méthode `interception_re_emission`.
//
void Scene::propagation_recur (const Rayon& ray, uint16_t profondeur_recur) {
stats_sum_prof_recur += profondeur_recur;
if (profondeur_recur >= propag_profondeur_recur_max) {
stats_n_rayons_profmax++;
return;
}
stats_n_rayons++;
std::vector<Rayon> rays = this->interception_re_emission(ray);
profondeur_recur++;
for (const Rayon& ray : rays) {
if (ray.spectre.intensite_tot() < intens_cutoff) {
stats_n_rayons_discarded++;
continue;
}
if (propag_emit_cb)
propag_emit_cb(ray, profondeur_recur);
this->propagation_recur(ray, profondeur_recur);
}
}
// Émet les rayons de toutes les sources de la scène et appelle `propagation_recur`.
//
void Scene::emission_propagation () {
for (auto& source : sources) {
std::vector<Rayon> rays = source->genere_rayons();
stats_n_rayons_emis += rays.size();
for (const Rayon& ray : rays) {
if (propag_emit_cb)
propag_emit_cb(ray, 0);
this->propagation_recur(ray, 0);
}
}
}
///------- Méthodes utilitaires et Scene_ObjetsBougeables -------///
void Scene::ecrans_do (std::function<void (Ecran_Base &)> f) {
for (auto obj : objets) {
Ecran_Base* ecran = dynamic_cast<Ecran_Base*>(obj.get());
if (ecran != nullptr)
f(*ecran);
}
}
bool Scene_ObjetsBougeables::objetsBougeables_event_SFML (const sf::Event& event) {
if (objet_bougeant == nullptr) {
if (event.type == sf::Event::KeyPressed and event.key.code == sf::Keyboard::LShift)
bouge_action_alt = true;
if (event.type == sf::Event::KeyReleased and event.key.code == sf::Keyboard::LShift)
bouge_action_alt = false;
if (event.type == sf::Event::MouseButtonReleased)
objet_bougeant = bougeable_nearest;
} else {
if (event.type == sf::Event::MouseButtonReleased) {
bouge_action_alt = false;
objet_bougeant = nullptr;
}
}
if (event.type == sf::Event::MouseMoved) {
mouse = sf::c01::fromWin(sf::Vector2f(event.mouseMove.x,event.mouseMove.y));
if (objet_bougeant == nullptr) {
if (bouge_actions.empty())
bougeable_nearest = nullptr;
else {
bougeable_nearest = &bouge_actions.front();
float dist2_min = Inf;
for (bouge_action_t& obj : bouge_actions) {
float dist2 = !(obj.pos - mouse);
if (dist2 < dist2_min) {
dist2_min = dist2;
bougeable_nearest = &obj;
}
}
}
} else {
vec_t v = mouse - objet_bougeant->pos;
float dir_angle = atan2f(v.y, v.x);
point_t new_pos = objet_bougeant->action_bouge(mouse, dir_angle, bouge_action_alt);
objet_bougeant->pos = new_pos;
return true;
}
}
return false;
}
void Scene_ObjetsBougeables::dessiner_pointeur_nearest_bougeable (sf::RenderWindow& win) {
if (objet_bougeant == nullptr and bougeable_nearest != nullptr) {
auto pointeur = sf::c01::buildCircleShapeCR(bougeable_nearest->pos, 0.005);
pointeur.setFillColor(sf::Color::Blue);
win.draw(pointeur);
}
}

129
LightRays/Scene.h Normal file
View File

@ -0,0 +1,129 @@
/*******************************************************************************
* Scène : objets, routines de propagation-interception-réémission des rayons,
* et sources. Aucune loi d'optique n'est implémentée ici, il ne s'agit
* que d'un conteneur, d'une récursion, et de méthodes utilitaires.
*******************************************************************************/
#ifndef _LIGHTRAYS_SCENE_H_
#define _LIGHTRAYS_SCENE_H_
#include <vector>
#include <memory>
#include "Objet.h"
#include "Source.h"
#include "Ecran.h"
#include <SFML/Window/Event.hpp>
#include <SFML/Graphics/RenderWindow.hpp>
class Scene {
public:
// Liste des objets de la scène
// (liste de pointeurs car les objets sont traités polymorphiquement)
std::vector< std::shared_ptr<Objet> > objets;
// Raccourci pour création d'objet de type `ObjT` : simple transfert d'argument de constructeur
template <class ObjT, typename... Args>
std::shared_ptr<ObjT> creer_objet (Args&& ...args) {
auto obj = std::make_shared<ObjT>(std::forward<Args>(args)...);
this->objets.push_back(obj);
return obj;
}
// Liste des sources de la scène
std::vector< std::shared_ptr<Source> > sources;
///--------- Routines de propagation des rayons ---------///
// Fenêtre de dessin des interceptions et/ou rayons (voir Scene::interception_re_emission)
sf::RenderWindow* propag_intercept_dessin_window = nullptr;
sf::RenderWindow* propag_rayons_dessin_window = nullptr;
float propag_rayons_dessin_gain = 10.;
// Callback appelé lors de l'interception d'un rayon par un objet. Utilisation typique : déboguage.
// `intercept_struct` est le Objet::intercept_t::intercept_struct renvoyé par Objet::essai_intercept
std::function< void (Objet&, const Rayon&, std::shared_ptr<void> intercept_struct) > propag_intercept_cb = nullptr;
// Callback appellé pour chaque rayon émis ou ré-émis
std::function< void (const Rayon&, uint16_t prof_recur) > propag_emit_cb = nullptr;
// Statistiques
uint64_t stats_n_rayons, stats_n_rayons_profmax, stats_n_rayons_discarded, stats_sum_prof_recur, stats_n_rayons_emis;
// Test d'interception du rayon contre toutes les objets de la scène puis ré-émission; la première
// interception sur le trajet du rayon depuis son origine est choisie. Renvoie tous les rayons ré-émis.
// Si `propagation_params.window` ≠ null, dessine l'interception du rayon avec `objet.dessiner_interception`,
// et appelle `propag_intercept_cb` (par exemple pour un dessin du rayon de la source à l'objet) si ≠ null.
// Si `propag_rayons_dessin_window` ≠ null, dessine les rayons en blanc transparent (∝ intensité) grâce
// à `objet.point_interception`. Méthode surtout interne, appelé par `propagation_recur`.
std::vector<Rayon> interception_re_emission (const Rayon& ray);
/// Propagation d'un rayon : récursion de l'interception/ré-émission
// Intensité en dessous de laquelle un rayon est ignoré. Fort impact sur la performance
float intens_cutoff = 1e-2;
// Profondeur de récursion (= nombre de ré-émission d'un rayon initial) maximale
// Ne devrait jouer que pour des réflexions infinies sans perte (où l'intensité ne passe jamais en dessous de `intens_cutoff`)
uint16_t propag_profondeur_recur_max = 20;
// Fonction récurrente : interception par les objets de la scène puis ré-émission;
// utilise `interception_re_emission`, `intens_cutoff` et `propag_profondeur_recur_max`.
// Appelle `propag_emit_cb` si ≠ null. Méthode surtout interne, appelé par `emission_propagation`.
void propagation_recur (const Rayon& ray, uint16_t profondeur_recur);
// Fonction principale : émet les rayons de toutes les sources de la scène et appelle `propagation_recur`.
void emission_propagation ();
///--------- Affichage et interface utilisateur ---------///
// Dessin de tous les objets et sources de la scène.
void dessiner_scene (sf::RenderWindow& window) const {
for (auto& objet : objets)
objet->dessiner(window, false);
for (auto& source : sources)
source->dessiner(window);
}
// Appelle f() sur tous les objets de type Ecran_Base
void ecrans_do (std::function<void(Ecran_Base&)> f);
};
///----------------------------------------------------------------------
/// Scène avec objets "bougeables" : routines utiles pour (par exemple)
/// déplacer/tourner les objets de la scène avec la souris, suivant des
/// actions spécifés par l'utilisateur (`bouge_action_t`).
class Scene_ObjetsBougeables : public Scene {
public:
// Ancre à la postion `pos` déclanchant l'action `action_bouge` lorsque cliqué,
// avec la postion de la souris et son angle à l'horizontale. Si le clic est fait
// avec la touche Maj, `alt=true`, sinon `false`.
struct bouge_action_t {
point_t pos;
std::function< point_t (point_t mouse, float angle, bool alt) > action_bouge;
};
std::vector<bouge_action_t> bouge_actions;
void ajouter_bouge_action (point_t pos_initiale, decltype(bouge_action_t::action_bouge) action) {
bouge_actions.push_back({pos_initiale, action});
}
// Position courante de la souris
point_t mouse = {0,0};
// Dessine un point bleu sur l'ancre la plus proche de la souris
void dessiner_pointeur_nearest_bougeable (sf::RenderWindow& win);
// À appeller lorsqu'un évènement souris/clavier/clic SFML est déclanché
// Lorsqu'un objet/ancre a été déplacée, renvoie `true` (e.g. pour reset d'écrans)
bool objetsBougeables_event_SFML (const sf::Event& event);
private:
bool bouge_action_alt = false;
bouge_action_t* bougeable_nearest = nullptr;
bouge_action_t* objet_bougeant = nullptr;
public:
Scene_ObjetsBougeables () = default;
Scene_ObjetsBougeables (const Scene_ObjetsBougeables&) = delete;
};
#endif

256
LightRays/SceneTest.cpp Normal file
View File

@ -0,0 +1,256 @@
#include "SceneTest.h"
#include <sstream>
#include <iomanip>
#include <unistd.h>
// Création de la scène, des fenêtres SFML, et optionnellement
// de l'écran, de la fenêtre, et de la lentille image
//
Scene_TestCommon::Scene_TestCommon (std::wstring nom, bool creer_ecran_image, bool creer_lentille_image) :
win_scene(nullptr), nom(nom), reset_ecrans(false), frame_i(0), gel(false), affiche_text(true), win_pixels(nullptr) {
intens_cutoff = 1e-3;
propag_profondeur_recur_max = 50;
// Création des fenêtres SFML
sf::ContextSettings settings;
settings.antialiasingLevel = 8;
win_scene = new sf::RenderWindow(sf::VideoMode(1100,720), std::wstring(L"Scène test - ")+nom, sf::Style::Close, settings);
win_scene->setPosition({0,0});
if (creer_ecran_image) {
win_pixels = new sf::RenderWindow(sf::VideoMode(100,400), L"CCD", sf::Style::Titlebar, settings);
win_pixels->setPosition({1100,0});
}
win_scene->requestFocus();
propag_rayons_dessin_window = win_scene;
// Écran et lentille image
if (creer_ecran_image) {
if (creer_lentille_image) {
lentille = this->creer_objet<Objet_MatriceTrsfUnidir>( point_t{1.2,0.5}, 0.2, 0, Objet_MatriceTrsfUnidir::mat_trsf_t{1,0,0,1} );
this->lentille_mise_au_point(0.2);
}
ecran_image = this->creer_objet<EcranLigne_Multi>( point_t{1.5,0.45}, point_t{1.5,0.55}, (uint16_t)200, 0.0001 );
}
// Texte d'aide
static_text = {
L"[S] (dés)affiche rayons couleur intercept",
L"[D] (dés)affiche tous rayons transparence lumino, [G]/[F] augmente/diminue lumino rayons",
L"[Up]/[Down] augmente/diminue gain écrans, [R] reset écrans",
L"[Space] gel scène, [H] cacher texte",
L"[(Maj) clic] bouge ancre plus proche"
};
if (not font.loadFromFile(FONT_PATH))
throw std::runtime_error("Échec de chargement de fonte");
}
// Mise au point de la lentille image
//
void Scene_TestCommon::lentille_mise_au_point (float x_obj) {
if (lentille) {
float f = -1 / ( 1/(1.5-1.2) + 1/(1.2-x_obj) );
lentille->mat_trsf = Objet_MatriceTrsfUnidir::mat_trsf_t{1,0,-1/f,1};
}
}
// Diaphragme autour de la lentille pour garder le CCD à l'ombre
//
void Scene_TestCommon::creer_bloqueurs_autour_lentille (float taille) {
this->creer_objet<Objet_Bloqueur>(point_t{1.2f,0.6f}, point_t{1.2f,0.6f+taille});
this->creer_objet<Objet_Bloqueur>(point_t{1.2f,0.4f-taille}, point_t{1.2f,0.4f});
}
// Destruction des fenêtres SFML
//
Scene_TestCommon::~Scene_TestCommon () {
delete win_scene;
if (win_pixels != nullptr)
delete win_pixels;
}
// Boucle principale SFML :
// - traite les évènements
// - effectue la propagation des rayons
// - dessine la scène, gère et affiche les statistiques, gère les écrans
//
void Scene_TestCommon::boucle (std::function<void(sf::Event)> f_event,
std::function<void(void)> f_pre_propag,
std::function<void(void)> f_post_propag) {
while (win_scene->isOpen()) {
// Gestion des évènements clavier et souris
sf::Event event;
while (win_scene->pollEvent(event)) {
if (event.type == sf::Event::Closed)
win_scene->close();
// Souris
bool objet_moved = this->objetsBougeables_event_SFML(event);
if (objet_moved)
reset_ecrans = true;
// Commandes clavier
if (event.type == sf::Event::KeyPressed and event.key.code == sf::Keyboard::R)
reset_ecrans = true;
if (event.type == sf::Event::KeyPressed and event.key.code == sf::Keyboard::S) {
if (propag_intercept_dessin_window == nullptr)
propag_intercept_dessin_window = win_scene;
else
propag_intercept_dessin_window = nullptr;
}
if (event.type == sf::Event::KeyPressed and event.key.code == sf::Keyboard::D) {
if (propag_rayons_dessin_window == nullptr)
propag_rayons_dessin_window = win_scene;
else
propag_rayons_dessin_window = nullptr;
}
if (event.type == sf::Event::KeyPressed and event.key.code == sf::Keyboard::G)
propag_rayons_dessin_gain *= 1.1;
if (event.type == sf::Event::KeyPressed and event.key.code == sf::Keyboard::F)
propag_rayons_dessin_gain *= 0.9;
if (event.type == sf::Event::KeyPressed and event.key.code == sf::Keyboard::Up)
this->ecrans_do([] (Ecran_Base& e) { e.luminosite *= 1.1; });
if (event.type == sf::Event::KeyPressed and event.key.code == sf::Keyboard::Down)
this->ecrans_do([] (Ecran_Base& e) { e.luminosite *= 0.9; });
if (event.type == sf::Event::KeyPressed and event.key.code == sf::Keyboard::Space)
gel = !gel;
if (event.type == sf::Event::KeyPressed and event.key.code == sf::Keyboard::H)
affiche_text = !affiche_text;
if (f_event)
f_event(event);
}
if (gel) {
usleep(1000);
continue;
}
// Dessin des objets de la scène avant propagation
win_scene->clear(sf::Color::Black);
this->dessiner_scene(*win_scene);
if (f_pre_propag)
f_pre_propag();
// Reset statistiques et écrans
stats_n_rayons_emis = 0;
stats_n_rayons = 0;
stats_sum_prof_recur = 0;
stats_n_rayons_profmax = 0;
stats_n_rayons_discarded = 0;
if (reset_ecrans) {
this->ecrans_do([] (Ecran_Base& e) { e.reset(); });
reset_ecrans = false;
frame_i = 0;
}
// Émission et propagation des rayons
this->emission_propagation();
this->ecrans_do([] (Ecran_Base& e) { e.commit(); });
if (f_post_propag)
f_post_propag();
// Affichage de la matrice de pixels dans la fenêtre image
if (win_pixels != nullptr) {
std::vector<EcranLigne_Multi::pixel_t> pix = ecran_image->matrice_pixels();
sf::RectangleShape rect;
rect.setSize(sf::Vector2f( 100, 400./pix.size() ));
for (size_t k = 0; k < pix.size(); k++) {
auto color = sf::Color(pix[k].r, pix[k].g, pix[k].b);
rect.setPosition(sf::Vector2f( 0, k * 400./pix.size() ));
rect.setFillColor(color);
win_pixels->draw(rect);
}
win_pixels->display();
while (win_pixels->pollEvent(event)) {}
}
// Pointeurs des objets bougeables
this->dessiner_pointeur_nearest_bougeable(*win_scene);
// Affichage de l'aide et des statistiques
if (affiche_text) {
std::wstringstream text;
for (auto& s : static_text)
text << s << std::endl;
text << std::endl;
text << frame_i << L" frame accumulées" << std::endl;
text << stats_n_rayons_emis << " rayons primaires, " << stats_n_rayons << " rayons tot, " << std::fixed << std::setprecision(1) << (stats_sum_prof_recur/(float)stats_n_rayons) << " prof recur moy, " << stats_n_rayons_discarded << L" rayons jetés, " << stats_n_rayons_profmax << " max prof" << std::endl;
text << std::setprecision(3) << "pointeur : (" << mouse.x << "," << mouse.y << ")";
win_scene->draw( sf::c01::buildText(font, point_t{0.1f,0.015f*(8+static_text.size())}, {text.str()}, sf::Color::White) );
win_scene->draw( sf::c01::buildText(font, point_t{1,0.1}, {this->nom}, sf::Color(255,255,255,160), 22) );
if (lentille != nullptr)
win_scene->draw( sf::c01::buildText(font, this->lentille->b+vec2_t{-0.03,0}, {L"Lentille"}, sf::Color::White) );
}
// Affichage SFML
win_scene->display();
frame_i++;
}
}
// Création d'une source d'un unique rayon rouge, déplaçable et tournable.
//
std::shared_ptr<Source_UniqueRayon> Scene_TestCommon::creer_source_unique_rayon (point_t pos, float dir_angle, float ampl) {
auto source = std::make_shared<Source_UniqueRayon>(pos, dir_angle, (color_id_t)0, ampl);
this->ajouter_bouge_action(source->position, [source] (point_t mouse, float angle, bool alt) -> point_t {
if (alt) source->dir_angle = angle;
else source->position = mouse;
return source->position;
});
this->sources.push_back(source);
return source;
}
// Création d'une source ponctuelle blanche omnidirectionnelle sur un secteur angulaire [ang_base-ang_ext, ang_base+ang_ext]
//
std::shared_ptr<Source_PonctOmni> Scene_TestCommon::creer_source_omni_secteur (point_t pos, float ang_ext, float ang_base, float dens_ray) {
auto source = std::make_shared<Source_PonctOmni>(pos, spectre_blanc);
source->dens_ang = dens_ray;
source->dir_alea = true;
this->ajouter_bouge_action(source->position, [ang_ext,source] (point_t mouse, float angle, bool alt) -> point_t {
if (alt) source->secteur = angle_interv_t(-ang_ext,+ang_ext) + angle;
else source->position = mouse;
return source->position;
});
source->secteur = angle_interv_t(-ang_ext,+ang_ext) + ang_base;
this->sources.push_back(source);
return source;
}
// Création d'une source linéaire lambertienne bleue qui prend tout le côté gauche de la scène.
//
std::shared_ptr<Source_LinLambertien> Scene_TestCommon::creer_ciel_bleu (float dens_lin) {
auto ciel = std::make_shared<Source_LinLambertien>(
/*a*/point_t{0.05,0.05}, /*segment*/vec_t{0,0.90},
Specte::polychromatique({{0,0,1.1,2}})
);
ciel->dens_lin = dens_lin;
this->sources.push_back(ciel);
this->ajouter_bouge_action(ciel->a, [ciel] (point_t mouse, float, bool) -> point_t {
ciel->a = mouse;
return mouse;
});
return ciel;
}
// Création d'écrans d'épaisseur `a` tout autour de la scène.
//
void Scene_TestCommon::creer_ecrans_autour (float lumino, float a) {
float b = (float)(win_scene->getSize().x)/SFMLC01_WINDOW_UNIT;
float bin_density = 50;
this->creer_objet<EcranLigne_Multi>( point_t{ a, a}, point_t{b-a, a}, bin_density, lumino )->epaisseur_affich = a;
this->creer_objet<EcranLigne_Multi>( point_t{b-a, a}, point_t{b-a, 1-a}, bin_density, lumino )->epaisseur_affich = a;
this->creer_objet<EcranLigne_Multi>( point_t{b-a, 1-a}, point_t{ a, 1-a}, bin_density, lumino )->epaisseur_affich = a;
this->creer_objet<EcranLigne_Multi>( point_t{ a, 1-a}, point_t{ a, a}, bin_density, lumino )->epaisseur_affich = a;
}

66
LightRays/SceneTest.h Normal file
View File

@ -0,0 +1,66 @@
/*******************************************************************************
* Scène "test" : regroupe tout le code qu'on trouverait typiquement dans
* les main(), mais pas assez générique pour être dans l'objet `Scene`.
*******************************************************************************/
// Aucune encapsulation ici, juste des fonctions utilitaires
// Création des fenêtres SFML, création de quelques objets courants & sources,
// (optionellement) lentille + écran + fenêtre "CCD" pour la formation d'images
// écrans autour de la scène.
// Hérite de Scene_ObjetsBougeables.
#ifndef _LIGHTRAYS_SCENE_TEST_H_
#define _LIGHTRAYS_SCENE_TEST_H_
#include <memory>
#include <cmath>
#include "Source.h"
#include "ObjetsOptiques.h"
#include "Ecran.h"
#include "Scene.h"
#include <SFML/Graphics.hpp>
#include "sfml_c01.hpp"
#include <string>
#include <vector>
class Scene_TestCommon : public Scene_ObjetsBougeables {
public:
sf::RenderWindow* win_scene; // fenêtre SFML principale, affichage de la scène
sf::Font font; // fonte SFML
std::wstring nom; // nom de la scène
std::vector<std::wstring> static_text; // texte supplémentaire affiché avec l'aide
bool reset_ecrans; // drapeau : si `true`, réinitialise les écrans et repasse à false
uint64_t frame_i; // numéro de frame depuis le dernier reset
bool gel; // si `vrai`, gèle la scène et l'affichage
bool affiche_text; // si `vrai`, affiche l'aide et le texte supplémentaire
sf::RenderWindow* win_pixels; // fenêtre SFML secondaire optionnelle pour affichage de l'image sur l'écran `ecran_image`
std::shared_ptr<EcranLigne_Multi> ecran_image; // écran de formation d'images (si `win_pixels!=null`)
std::shared_ptr<Objet_MatriceTrsfUnidir> lentille; // lentille convergente pour former les images, optionnelle (et si `win_pixels!=null`)
void lentille_mise_au_point (float x_obj); // mise au point de la lentille sur le plan x = `x_obj`
void creer_bloqueurs_autour_lentille (float taille);
// Création de la scène, des fenêtres SFML, optionnellement de l'écran et de la fenêtre image
// (si `creer_ecran_image` vrai) et de la lentille (si `creer_lentille_image` vrai).
Scene_TestCommon (std::wstring nom, bool creer_ecran_image, bool creer_lentille_image);
~Scene_TestCommon ();
// Boucle principale SFML :
// - traite les évènements (clavier souris) et (optionnel) appelle f_event() à chaque évènement
// - effectue la propagation des rayons avec Scene::emission_propagation; et avant cela,
// appelle (optionnel) `f_pre_propag`; et après cela, appelle (optionnel) `f_post_propag`
// - dessine la scène
// - gère et affiche les statistiques, gère les écrans
void boucle (std::function<void(sf::Event)> f_event,
std::function<void(void)> f_pre_propag,
std::function<void(void)> f_post_propag);
// Création rapide de sources, voir implémentation
std::shared_ptr<Source_UniqueRayon> creer_source_unique_rayon (point_t pos, float dir_angle, float ampl);
std::shared_ptr<Source_PonctOmni> creer_source_omni_secteur (point_t pos, float ang_ext, float ang_base, float dens_ray);
std::shared_ptr<Source_LinLambertien> creer_ciel_bleu (float dens_lin = 1000);
// Création d'écrans autour de la scène
void creer_ecrans_autour (float lumino = 0.0001, float epaiss = 0.01);
};
#endif

130
LightRays/Source.cpp Normal file
View File

@ -0,0 +1,130 @@
#include "Source.h"
#include <cmath>
const decltype(Source_Omni::directivite) Source_Omni::directivite_unif = [] (float) -> float { return 1.f; };
///------------------------ Émission des sources ------------------------///
std::vector<Rayon> Source_UniqueRayon::genere_rayons () {
return { Rayon{
.orig = position,
.dir_angle = dir_angle,
.spectre = spectre
} };
};
std::vector<Rayon> Source_LinParallels::genere_rayons () {
std::vector<Rayon> rayons;
size_t n_rayons = lroundf(dens_lin * !vec);
float seg_dir_angle = atan2f(vec.y, vec.x) - M_PI/2;
for (size_t k = 0; k < n_rayons; k++) {
Rayon ray = {
.orig = a + vec * (float)k / n_rayons,
.dir_angle = seg_dir_angle + dir_angle_rel,
.spectre = spectre
};
rayons.push_back(std::move(ray));
}
return rayons;
}
std::vector<Rayon> Source_PonctOmni::genere_rayons () {
std::vector<Rayon> rayons;
size_t n_rayons = lroundf(dens_ang);
for (size_t k = 0; k < n_rayons; k++) {
Rayon rayon = {
.orig = position,
.dir_angle = dir_alea ?
(float)(2*M_PI) * rand01() :
(float)(2*M_PI * k) / n_rayons,
.spectre = spectre
};
if (secteur.has_value() and not secteur->inclus(rayon.dir_angle))
continue;
rayon.spectre.for_each([&] (float, pola_t, float& I) {
I *= this->directivite( rayon.dir_angle );
});
rayons.push_back(std::move(rayon));
}
return rayons;
}
std::vector<Rayon> Source_SecteurDisqueLambertien::genere_rayons () {
std::vector<Rayon> rayons;
size_t n_rayons = lroundf(dens_ang * ang.longueur()/(2*M_PI));
for (size_t k = 0; k < n_rayons; k++) {
Rayon ray = { .spectre = spectre };
ray.dir_angle = ang.beg() + ang.longueur() * ( dir_alea ? rand01() : ((float)k / n_rayons) );
ray.orig = position + R * vec_t{ .x = cosf(ray.dir_angle), .y = sinf(ray.dir_angle) };
float incr_angle = M_PI/2 * (1-2*rand01());
ray.spectre.for_each([&] (float, pola_t, float& I) {
I *= this->directivite( ray.dir_angle ) * cosf(incr_angle);
});
ray.dir_angle += incr_angle;
rayons.push_back(std::move(ray));
}
return rayons;
}
std::vector<Rayon> Source_LinLambertien::genere_rayons () {
std::vector<Rayon> rayons;
size_t n_rayons = lroundf(dens_lin * !vec);
float dir_angle = atan2f(vec.y, vec.x) - M_PI/2;
for (size_t k = 0; k < n_rayons; k++) {
float incr_angle = M_PI/2 * (1-2*rand01());
Rayon ray = {
.orig = a + vec * (float)k / n_rayons,
.dir_angle = dir_angle + incr_angle,
.spectre = spectre
};
ray.spectre.for_each([&] (float, pola_t, float& I) {
I *= cosf(incr_angle);
});
rayons.push_back(std::move(ray));
}
return rayons;
};
///------------------------ Affichage ------------------------///
#include "sfml_c01.hpp"
void Source_UniqueRayon::dessiner (sf::RenderWindow& win) const {
win.draw( sf::c01::buildLine(position,
position + 0.01 * vec_t{ .x = cosf(dir_angle), .y = sinf(dir_angle) },
sf::Color::Yellow) );
}
void Source_PonctOmni::dessiner (sf::RenderWindow& win) const {
auto c = sf::c01::buildCircleShapeCR(position, 0.005);
c.setFillColor(sf::Color::Yellow);
win.draw(c);
}
void Source_SecteurDisqueLambertien::dessiner (sf::RenderWindow& win) const {
size_t N = R * 100 * ang.longueur();
if (N < 3) N = 3;
sf::ConvexShape secteur (N+2);
for (size_t k = 0; k <= N; k++) {
float theta = ang.beg() + ang.longueur() * (float) k / N;
secteur.setPoint(k, sf::c01::toWin(
position + R * vec_t{ .x = cosf(theta),
.y = sinf(theta) }
));
}
secteur.setPoint(N+1, sf::c01::toWin(position));
secteur.setFillColor(sf::Color::Yellow);
win.draw(secteur);
}
void Source_LinLambertien::dessiner (sf::RenderWindow& win) const {
win.draw(
sf::c01::buildLine(a, a+vec, sf::Color::Yellow)
);
}
void Source_LinParallels::dessiner (sf::RenderWindow& win) const {
win.draw(
sf::c01::buildLine(a, a+vec, sf::Color::Yellow)
);
}

168
LightRays/Source.h Normal file
View File

@ -0,0 +1,168 @@
/*******************************************************************************
* Classe de base des sources. Divers types de sources.
*******************************************************************************/
#ifndef _LIGHTRAYS_SOURCE_H_
#define _LIGHTRAYS_SOURCE_H_
#include "Rayon.h"
#include <vector>
#include <SFML/Graphics/RenderWindow.hpp>
//------------------------------------------------------------------------------
// Classe de base, abstraite, des sources. À priori, seulement définie par
// son spectre.
class Source {
public:
Specte spectre; // spectre de la source en intensité
// source de spectre arbitraire
Source (Specte spectre) : spectre(spectre) {}
// source monochromatique polarisée
Source (color_id_t couleur, float I, pola_t pol) : spectre( Specte::monochromatique(I, couleur, pol) ) {}
// source monochromatique non-polarisée
Source (color_id_t couleur, float I) : spectre( Specte::monochromatique(I, couleur) ) {};
Source& operator= (const Source&) = default;
Source (const Source&) = default;
virtual ~Source () {}
// génération des rayons
virtual std::vector<Rayon> genere_rayons () = 0;
// dessin de la source
virtual void dessiner (sf::RenderWindow& window) const = 0;
};
//------------------------------------------------------------------------------
// Source émettant un unique rayon à partir du point `position` et à un angle
// `dir_angle` à l'horizontale. "Rayon laser".
class Source_UniqueRayon : public Source {
public:
point_t position;
float dir_angle;
// relai aux constructeurs de `Source`
template<typename... Args> Source_UniqueRayon (point_t pos, float dir, Args&&... x) :
Source(x...), position(pos), dir_angle(dir) {}
Source_UniqueRayon& operator= (const Source_UniqueRayon&) = default;
Source_UniqueRayon (const Source_UniqueRayon&) = default;
virtual ~Source_UniqueRayon () {}
// création de l'unique rayon
virtual std::vector<Rayon> genere_rayons () override;
void dessiner (sf::RenderWindow& window) const override;
};
//------------------------------------------------------------------------------
// Source étendue émettant un plusieurs rayon distribués sur une ligne et à un
// angle fixe. Peut être vu comme une source ponctuelle à l'infini, ou un
// ensemble de `Source_UniqueRayon`.
class Source_LinParallels : public Source {
public:
point_t a; // point A du segment
vec_t vec; // vecteur AB du segment
float dens_lin; // nombre de rayons par unité de longueur
float dir_angle_rel; // angle par rapport au vecteur AB
template<typename... Args> Source_LinParallels (point_t pos_seg_a, point_t pos_seg_b, float dir_angle, Args&&... x) :
Source(x...), a(pos_seg_a), vec(pos_seg_b-pos_seg_a), dens_lin(100), dir_angle_rel(dir_angle) {}
template<typename... Args> Source_LinParallels (point_t pos_seg_a, vec_t pos_ab_vecteur, float dir_angle, Args&&... x) :
Source(x...), a(pos_seg_a), vec(pos_ab_vecteur), dens_lin(100), dir_angle_rel(dir_angle) {}
Source_LinParallels& operator= (const Source_LinParallels&) = default;
Source_LinParallels (const Source_LinParallels&) = default;
virtual ~Source_LinParallels () {}
virtual std::vector<Rayon> genere_rayons () override;
void dessiner (sf::RenderWindow& window) const override;
};
//------------------------------------------------------------------------------
// Classe de base virtuelle des sources omnidirectionnelle
// (ou restreinte à un secteur angulaire `secteur`) à directivité arbitraire.
class Source_Omni : public Source {
public:
point_t position; // position de la source
float dens_ang; // nombre de rayons pour 2π
bool dir_alea; // émission dans des directions aléatoires ou équiréparties/déterministe
std::function< float(float theta) > directivite; // directivité (normalisée à 1)
const static decltype(directivite) directivite_unif; // directivité unirforme
std::optional<angle_interv_t> secteur; // secteur angulaire d'émission
template<typename... Args> Source_Omni (point_t pos, Args&&... x) :
Source(x...), position(pos), dens_ang(100), dir_alea(true), directivite(directivite_unif) {}
Source_Omni& operator= (const Source_Omni&) = default;
Source_Omni (const Source_Omni&) = default;
virtual ~Source_Omni () {}
};
//------------------------------------------------------------------------------
// Source ponctuelle omnidirectionnelle (ou restreinte à un secteur angulaire)
class Source_PonctOmni : public Source_Omni {
public:
virtual std::vector<Rayon> genere_rayons () override;
void dessiner (sf::RenderWindow& window) const override;
template<typename... Args> Source_PonctOmni (point_t pos, Args&&... x) : Source_Omni(pos, x...) {}
virtual ~Source_PonctOmni () {}
};
//------------------------------------------------------------------------------
// Source étendue en secteur de disque ("projecteur") avec une émission
// lambertienne à chaque point de sa surface (loi en cosinus). Si R très petit,
// équivalent à Source_PonctOmni.
class Source_SecteurDisqueLambertien : public Source_Omni {
public:
float R;
angle_interv_t ang;
template<typename... Args> Source_SecteurDisqueLambertien (float radius, angle_interv_t ang_interval, Args&&... x) :
Source_Omni(x...), R(radius), ang(ang_interval) {}
Source_SecteurDisqueLambertien& operator= (const Source_SecteurDisqueLambertien&) = default;
Source_SecteurDisqueLambertien (const Source_SecteurDisqueLambertien&) = default;
virtual ~Source_SecteurDisqueLambertien () {}
virtual std::vector<Rayon> genere_rayons () override;
void dessiner (sf::RenderWindow& window) const override;
};
//------------------------------------------------------------------------------
// Source étendue linéaire ("écran lumineux") avec une émission lambertienne
// à chaque point de sa surface (loi en cosinus), et d'un seul côté du segment.
class Source_LinLambertien : public Source {
public:
point_t a;
vec_t vec;
float dens_lin;
template<typename... Args> Source_LinLambertien (point_t pos_seg_a, point_t pos_seg_b, Args&&... x) :
Source(x...), a(pos_seg_a), vec(pos_seg_b-pos_seg_a), dens_lin(100) {}
template<typename... Args> Source_LinLambertien (point_t pos_seg_a, vec_t pos_ab_vecteur, Args&&... x) :
Source(x...), a(pos_seg_a), vec(pos_ab_vecteur), dens_lin(100) {}
Source_LinLambertien& operator= (const Source_LinLambertien&) = default;
Source_LinLambertien (const Source_LinLambertien&) = default;
virtual ~Source_LinLambertien () {}
virtual std::vector<Rayon> genere_rayons () override;
void dessiner (sf::RenderWindow& window) const override;
};
#endif

97
LightRays/Util.cpp Normal file
View File

@ -0,0 +1,97 @@
#include "Util.h"
#include <cmath>
#include <stdexcept>
#include <cassert>
float vec_t::operator! () const {
return hypotf(x, y);
}
vec_t vec_t::rotate (float theta) const {
float c = cosf(theta), s = sinf(theta);
return { .x = c * x - s * y,
.y = s * x + c * y };
}
point_t milieu_2points (point_t a, point_t b) {
return { .x = (a.x + b.x)/2, .y = (a.y + b.y)/2 };
}
std::vector<point_t> translate_points (vec_t by, std::vector<point_t> pts) {
for (point_t& p : pts)
p = p + by;
return pts;
}
std::vector<point_t> rotate_points (point_t around, float angle, std::vector<point_t> pts) {
for (point_t& p : pts)
p = around + (p - around).rotate(angle);
return pts;
}
std::vector<point_t> homothetie_points (point_t around, float coeff, std::vector<point_t> pts) {
for (point_t& p : pts)
p = around + coeff * (p - around);
return pts;
}
// ⎡a b⎤ ⎡x⎤ ⎡e⎤
// ⎣c d⎦ ⎣y⎦ = ⎣f⎦
//
void mat22_sol (float a, float b, float c, float d, float e, float f, float& x, float& y) {
float det = a * d - b * c;
x = (e * d - b * f) / det;
y = (a * f - e * c) / det;
}
const angle_interv_t angle_interv_t::cercle_entier (2*M_PI);
angle_interv_t::angle_interv_t (float theta_beg, float b) : a(theta_beg), l(0) {
if (fabsf(b-a) > 2*M_PI+1e-6)
throw std::domain_error("angle interval > 2π");
if (b < 0 and a >= 0)
b += 2*M_PI;
l = b - a;
a = angle_mod2pi_02(a);
// assert(0 <= a and a <= 2*M_PI and 0 <= l and l <= 2*M_PI);
}
angle_interv_t::angle_interv_t (float lenght) : a(0), l(lenght) {
if (l < 0 or l > 2*M_PI+1e-6)
throw std::domain_error("invalid angle interval length");
}
angle_interv_t angle_interv_t::operator+ (float delta_theta) const {
angle_interv_t o = *this;
o.a = angle_mod2pi_02(a + delta_theta);
return o;
}
float angle_mod2pi_11 (float theta) {
while (theta > M_PI)
theta -= 2*M_PI;
while (theta < -M_PI)
theta += 2*M_PI;
return theta;
}
float angle_mod2pi_02 (float theta) {
return theta - 2*M_PI* floorf( theta/(2*M_PI) );
}
bool angle_interv_t::inclus (float theta) const {
theta = angle_mod2pi_02(theta);
if (a <= theta and theta <= a+l)
return true;
theta += 2*M_PI;
return (a <= theta and theta <= a+l);
}
std::pair<vec_t,vec_t> angle_interv_t::vec_a_b () const {
return { vec_t{ .x = cosf(a), .y = sinf(a) },
vec_t{ .x = cosf(a+l), .y = sinf(a+l) } };
}
float rand01 () {
return rand()/(float)RAND_MAX;
}

98
LightRays/Util.h Normal file
View File

@ -0,0 +1,98 @@
#ifndef _LIGHTRAYS_UTIL_H_
#define _LIGHTRAYS_UTIL_H_
#include <utility>
#include <vector>
#ifdef NOSTDOPTIONAL
#include <boost/optional.hpp>
namespace std {
using boost::optional;
const boost::none_t nullopt = boost::none;
}
#else
#include <optional>
#endif
#define Inf std::numeric_limits<float>::infinity()
#define NaN std::numeric_limits<float>::signaling_NaN()
/// Vecteur 2D
struct vec_t {
float x, y;
void operator/= (float k) { x /= k; y /= k; }
void operator*= (float k) { x *= k; y *= k; }
void operator+= (vec_t o) { x += o.x; y += o.y; }
void operator-= (vec_t o) { x -= o.x; y -= o.y; }
vec_t operator* (float k) const { return vec_t{ k*x, k*y }; }
vec_t operator/ (float k) const { return vec_t{ x/k, y/k }; }
vec_t operator+ (vec_t o) const { return vec_t{ x+o.x, y+o.y }; }
vec_t operator- () const { return vec_t{ -x, -y }; }
vec_t operator- (vec_t o) const { return vec_t{ x-o.x, y-o.y }; }
float operator| (vec_t o) const { return x*o.x + y*o.y ; } // produit scalaire
float norm2 () const { return x*x + y*y; } // norme au carré
float operator! () const; // norme
vec_t rotate (float theta) const; // rotation d'un angle `theta`
vec_t rotate_p90 () const { return vec_t { -y, +x }; }
vec_t rotate_m90 () const { return vec_t { +y, -x }; }
};
inline vec_t operator* (double k, const vec_t& v) { return v*k; }
/// Point 2D
struct point_t {
float x, y;
vec_t operator- (point_t o) const { return vec_t{ x-o.x, y-o.y }; } // vecteur entre deux points
explicit operator vec_t () const { return vec_t{ x, y }; }
point_t operator+ (vec_t v) const { return point_t{ x+v.x, y+v.y }; } // translation du point par un vecteur
};
// Milieu entre deux points
point_t milieu_2points (point_t a, point_t b);
// Transation d'un ensemble de points
std::vector<point_t> translate_points (vec_t by, std::vector<point_t>);
// Rotation d'un ensemble de points autour d'un centre `around` d'un angle `angle`
std::vector<point_t> rotate_points (point_t around, float angle, std::vector<point_t>);
// Homothétie d'un ensemble de points autout d'un centre `around` par un facter `coeff`
std::vector<point_t> homothetie_points (point_t around, float coeff, std::vector<point_t>);
/// Intervalle d'angle dans /2π
struct angle_interv_t {
private:
float a, l; // angle de début (0 ≤ a ≤ 2π) et longueur de l'intervalle (0 ≤ l ≤ 2π)
public:
angle_interv_t (const angle_interv_t&) = default;
static const angle_interv_t cercle_entier;
// construction à partir de l'angle de début et de fin
// si (theta_beg > 0 > theta_end) numériquement, interprète l'intervalle comme [theta_beg,theta_end+2π]
// si |theta_end-theta_beg| > 2π, lances une exception (n'est pas censé arriver dans ce programme)
angle_interv_t (float theta_beg, float theta_end);
// construction d'un intervalle de longeur donnée, partant de θ=0
angle_interv_t (float lenght);
angle_interv_t operator+ (float delta_theta) const; // rotation de l'intervalle
float longueur () const { return l; } // longueur de l'intervalle, entre 0 et 2π
bool inclus (float theta) const; // test si un angle (dans /2π) est inclus dans l'intervalle
float beg () const { return a; } // angle de début, entre 0 et 2π
float end () const { return a+l; } // angle de fin, entre 0 et 4π
std::pair<vec_t,vec_t> vec_a_b () const; // vecteurs unité définissant les angles début et fin
};
// réduit un angle à [-π,+π]
float angle_mod2pi_11 (float);
// réduit un angle à [0,2π]
float angle_mod2pi_02 (float);
/// Divers
// Réslution du système linéaire
// ⎡a b⎤ ⎡x⎤ ⎡e⎤
// ⎣c d⎦ ⎣y⎦ = ⎣f⎦
void mat22_sol (float a, float b, float c, float d, float e, float f, float& x, float& y);
// Nombre au hasard entre 0 et 1
float rand01 ();
#endif

View File

@ -0,0 +1,84 @@
/********************************************************************************
* Scène de test d'un brouillard diffusant, avec source "laser" et "en secteur",
* avec différentes méthodes de diffusion et densité du brouillard variable
********************************************************************************/
#include "SceneTest.h"
#include "Brouillard.h"
#include "ObjetDiffusant.h"
int main (int, char const**) {
Scene_TestCommon scene (L"Scène test brouillard", /*écran image*/true, /*lentille image*/true);
scene.creer_ecrans_autour(/*lumino*/0.0005);
scene.propag_rayons_dessin_gain = 30;
scene.ecrans_do([] (Ecran_Base& e) { e.luminosite *= 20; });
// Création des sources (et d'un fond bleu parce que pourquoi pas)
auto source_unique = scene.creer_source_unique_rayon(point_t{1,0.445}, /*dir*/0.99*M_PI, /*ampl*/400.);
auto source_omni = scene.creer_source_omni_secteur(point_t{0.7,0.60}, /*ang_ext*/0.1, /*ang_base*/1.05*M_PI, /*dens_ray*/10000);
auto ciel = scene.creer_ciel_bleu(/*dens lin*/300);
// Ancre de mise au point de la lentille à la souris, sur le bord droit du brouillard par défaut
scene.ajouter_bouge_action(point_t{0.5,0.5}, [&] (point_t mouse, float angle, bool alt) -> point_t {
scene.lentille_mise_au_point(mouse.x);
return point_t{mouse.x,0.5};
});
scene.lentille_mise_au_point(0.5);
// Création du brouillard lui même avec sa fonction densité(x,y)
float dens_brouillard = 100;
const float taille_brouill = 0.25;
const float resol_brouill = 0.01;
const size_t sz_brouill = taille_brouill/resol_brouill;
auto brouillard = scene.creer_objet<Objet_Brouillard>(
/* bottom right */ /* résolutions */ /* nombre de voxels */
point_t{0.3,0.4}, resol_brouill, resol_brouill, sz_brouill, sz_brouill,
/* fonction retournant la densité du brouillard en fonction de la position */
[&] (uint ix, uint iy) -> float {
float x = (ix-sz_brouill/2.)/(sz_brouill/2.);
float y = (iy-sz_brouill/2.)/(sz_brouill/2.);
return dens_brouillard * std::max(0.f, 1 - expf(x*x + y*y - 1));
});
brouillard->n_re_emit_par_intens = 2;
brouillard->intens_cutoff = 0.2;
bool diffus_meth_tot = true;
scene.static_text.insert(scene.static_text.begin(), {
L"[T/Y] brouillard moins/plus diffusant",
L"[M] change méthode diffusion (totale/partielle)",
L"[P] parcours brouillard déterministe ou non",
L""
});
scene.boucle(
/*f_event*/ [&] (sf::Event event) {
if (event.type == sf::Event::KeyPressed and event.key.code == sf::Keyboard::T) { // diminution de la densité du brouillard
dens_brouillard *= 0.9;
scene.reset_ecrans = true;
}
if (event.type == sf::Event::KeyPressed and event.key.code == sf::Keyboard::Y) { // augmentation de la densité du brouillard
dens_brouillard *= 1.1;
scene.reset_ecrans = true;
}
if (event.type == sf::Event::KeyPressed and event.key.code == sf::Keyboard::M) { // changement de méthode de diffusion
if (diffus_meth_tot) {
diffus_meth_tot = false;
brouillard->diffus_partielle_syst_libreparcours = resol_brouill;
brouillard->n_re_emit_par_intens = 1;
} else {
diffus_meth_tot = true;
brouillard->diffus_partielle_syst_libreparcours = Inf;
brouillard->n_re_emit_par_intens = 3;
}
scene.reset_ecrans = true;
}
if (event.type == sf::Event::KeyPressed and event.key.code == sf::Keyboard::P) { // changement de parcours du brouillard par les rayons, déterministe ou non
brouillard->parcours_deterministe = !brouillard->parcours_deterministe;
scene.reset_ecrans = true;
}
}, /*f_pre_propag*/ nullptr, /*f_post_propag*/ nullptr);
return 0;
}

111
LightRays/main_debug.cpp Normal file
View File

@ -0,0 +1,111 @@
#include "SceneTest.h"
#include <fmt/core.h>
int main (int, char const**) {
Scene_TestCommon scene (L"Debug", /*ecran_image*/true, /*lentille image*/true);
scene.creer_ecrans_autour();
auto source_omni_ponct = std::make_shared<Source_PonctOmni>(
point_t{0.34306,0.48611}, Specte::polychromatique({{0.8,0.8,1.7,2.2}})
);
auto color = source_omni_ponct->spectre.rgb256_noir_intensite(false);
fmt::print("({}, {}, {})", std::get<0>(color),std::get<1>(color),std::get<2>(color));
source_omni_ponct->dens_ang = 500;
source_omni_ponct->dir_alea = false;
scene.sources.push_back(source_omni_ponct);
scene.ajouter_bouge_action(source_omni_ponct->position, [&] (point_t mouse, float angle, bool alt) -> point_t {
source_omni_ponct->position = mouse;
return mouse;
});
auto source_sect = std::make_shared<Source_SecteurDisqueLambertien>(
0.03, angle_interv_t(-M_PI/10, +M_PI/10)+M_PI, point_t{0.1,0.5}, Specte::polychromatique({{0.8,0.8,1.7,2.2}})
);
source_sect->dens_ang = 30000;
source_sect->dir_alea = true;
scene.sources.push_back(source_sect);
scene.ajouter_bouge_action(source_sect->position, [&] (point_t mouse, float angle, bool alt) -> point_t {
if (alt) {
float l = source_sect->ang.longueur();
source_sect->ang = angle_interv_t(-l/2, +l/2) + angle;
}
else source_sect->position = mouse;
return source_sect->position;
});
// scene.propag_rayons_dessin_window = nullptr;
// scene.propag_intercept_dessin_window = scene.win_scene;
scene.static_text.insert(scene.static_text.begin(), {
L"[X] debug rayon",
});
struct propag_debug_rayons_t {
point_t milieu_rayon;
Rayon ray;
ObjetArc::intercept_courbe_t intercept;
};
std::vector<propag_debug_rayons_t> propag_debug_rayons;
bool propag_debug = false;
auto propag_intercept_debug_info_mouse = [&] (Objet& o, const Rayon& ray, std::shared_ptr<void> intercept_struct) {
if (dynamic_cast<ObjetCourbe*>(&o) != nullptr) { // si c'est un ObjetCourbe (sinon le point d'interception n'est pas défini)
ObjetArc::intercept_courbe_t& intercept = *std::static_pointer_cast<ObjetArc::intercept_courbe_t>(intercept_struct);
propag_debug_rayons.push_back(propag_debug_rayons_t{
.milieu_rayon = milieu_2points(ray.orig, intercept.p_incid),
.ray = ray,
.intercept = intercept
});
}
/* auto p = o.point_interception(intercept_struct);
if (p.has_value()) {
propag_debug_rayons.push_back(propag_debug_rayons_t{
.milieu_rayon = milieu_2points(ray.orig, *p),
.ray = ray,
});
}*/
};
scene.boucle(
/*f_event*/ [&] (sf::Event event) {
if (event.type == sf::Event::KeyPressed and event.key.code == sf::Keyboard::X) {
if (propag_debug) {
propag_debug = false;
scene.propag_intercept_cb = nullptr;
} else {
propag_debug = true;
scene.propag_intercept_cb = propag_intercept_debug_info_mouse;
}
}
},
/*f_pre_propag*/ [&] () {
},
/*f_post_propag*/ [&] () {
if (propag_debug and not propag_debug_rayons.empty()) {
auto closest_rayon = propag_debug_rayons.begin();
float closest_dist2 = Inf;
for (auto it = propag_debug_rayons.begin(); it != propag_debug_rayons.end(); it++) {
float dist2 = (scene.mouse - it->milieu_rayon).norm2();
if (dist2 < closest_dist2) {
closest_dist2 = dist2;
closest_rayon = it;
}
}
const Rayon& ray = closest_rayon->ray;
auto point_milieu = sf::c01::buildCircleShapeCR(closest_rayon->milieu_rayon, 0.003);
point_milieu.setFillColor(sf::Color::White);
scene.win_scene->draw(point_milieu);
auto text = sf::c01::buildText(scene.font, closest_rayon->milieu_rayon, {
fmt::format(L"orig : ( {:.3f}, {:.3f} )", ray.orig.x, ray.orig.y),
fmt::format(L"angle : {:.2f}π", ray.dir_angle/M_PI),
fmt::format(L"incid : ( {:.3f}, {:.3f} )", closest_rayon->intercept.p_incid.x, closest_rayon->intercept.p_incid.y),
fmt::format(L"angle incid : {:.2f}π/2 {}", closest_rayon->intercept.ang_incid/(M_PI/2), closest_rayon->intercept.sens_reg ? L"reg" : L"inv"),
fmt::format(L"I n°1 TE : {:.3e}", ray.spectre.comps[2]),
fmt::format(L"I n°1 TM : {:.3e}", ray.spectre.comps[3]),
}, sf::Color::White);
scene.win_scene->draw(text);
}
});
return 0;
}

View File

@ -0,0 +1,140 @@
/********************************************************************************
* Scène de test d'une surface diffusante avec une composante de rélfexion
* lambertienne et une composante spéculaire en cos(θ)^n. Bilan d'énergie
********************************************************************************/
#include "SceneTest.h"
#include "ObjetDiffusant.h"
#include <sstream>
#include <iomanip>
int main (int, char const**) {
Scene_TestCommon scene (L"Test objets diffusants", /*ecran image*/true, /*lentille image*/true);
scene.creer_ecrans_autour(/*lumino*/ 0.0005, /*épaisseur*/ 0.025);
float source_ext_ang = 0.1;
auto source_omni = scene.creer_source_omni_secteur(point_t{0.3,0.66}, source_ext_ang, /*ang_base*/-0.22*M_PI, /*dens_ray*/60000);
// création du plan diffusant
auto panel_diffus = std::make_shared<ObjetLigne_Diffusant>(ObjetCourbe_Diffusant::BRDF_Lambert, point_t{0.45,0.51}, point_t{0.55,0.49});
point_t panel_m = milieu_2points(panel_diffus->a,panel_diffus->b);
scene.objets.push_back(panel_diffus);
// mise au point au milieu du plan
scene.lentille_mise_au_point(panel_m.x);
// déplacement/rotation du plan à la souris
scene.ajouter_bouge_action(panel_m, [&] (point_t mouse, float angle, bool alt) -> point_t {
vec_t v = panel_diffus->b - panel_diffus->a;
if (alt) {
v = !v * vec2_t{ cosf(angle), sinf(angle) };
} else {
panel_m = mouse;
}
panel_diffus->a = panel_m + v/2;
panel_diffus->b = panel_m + -v/2;
return panel_m;
});
// composante de réflexion en cos^n, où on se rapporche d'une réflexion très directive lorsque n augmente (spéculaire)
int diffus_n = 3;
// coeff de normalisation tel que l'intégrale de `cos^n / c` fasse 1
// on vérifie qu'on a bien conservation de l'énergie avec `objet_bilan` ci-dessous
auto diffus_c_calc = [] (int n) -> float {
float c = 2;
while (n >= 3) {
c *= (n-1.f) / n;
n -= 2;
}
return c;
};
float diffus_c = diffus_c_calc(diffus_n);
float refl_spec = 1;
// distribution angulaire de réflectance et ses deux composantes
panel_diffus->BRDF_lambda = [&] (float theta_i, float theta_r, float lambda) -> float {
// spectre de réflexion diffuse centrée autour de 450nm
float f_col = (lambda-4.5e-4)/1e-4; f_col = expf(-f_col*f_col);
return f_col * (1-refl_spec) + refl_spec * M_PI * powf( std::max(0.f, cosf(theta_i+theta_r)), diffus_n) / diffus_c;
// réflexion lambertienne réflexion spéculaire (réflectivité de lobe cos^n)
};
// À améliorer : Il n'y a pas conservation de l'énergie lorsque le rayon incident n'est pas normal,
// car il y a troncature de l'angle du rayon réfléchi sur [-pi/2,+pi/2] !
// Bilan d'énergie pour vérifier si `diffus_c_calc` est correct.
auto objet_bilan = scene.creer_objet<Objet_BilanEnergie>(point_t{0.5,0.5}, 0.2);
// Diaphragme
scene.creer_objet<Objet_Bloqueur>(point_t{1.3,0.53}, point_t{1.3,0.6});
scene.creer_objet<Objet_Bloqueur>(point_t{1.3,0.47}, point_t{1.3,0.4});
scene.static_text.insert(scene.static_text.begin(), {
L"Surface diffusante avec une composante de rélfexion lambertienne (uniforme) et une composante spéculaire en cos(θ)^n",
L"[W/X] augmente/diminue la directivité de la partie spéculaire de la réflexion",
L"[C/V] diminue/augmente la partie spéculaire de la réflexion, vs lambertienne",
L"[E/Z] élargit/rétrécit la source",
L"[K] mode déterministe",
L""
});
scene.boucle(
/*f_event*/ [&] (sf::Event event) {
if (event.type == sf::Event::KeyPressed and event.key.code == sf::Keyboard::W) { // réflexion spéculaire plus piquée
diffus_n = 2*diffus_n+1;
diffus_c = diffus_c_calc(diffus_n);
scene.reset_ecrans = true;
}
if (event.type == sf::Event::KeyPressed and event.key.code == sf::Keyboard::X) { // réflexion spéculaire moins piquée
if (diffus_n > 1)
diffus_n = (diffus_n-1)/2;
diffus_c = diffus_c_calc(diffus_n);
scene.reset_ecrans = true;
}
if (event.type == sf::Event::KeyPressed and event.key.code == sf::Keyboard::V) { // plus de réflexion spéculaire
refl_spec = std::min<float>(1, refl_spec+0.02);
scene.reset_ecrans = true;
}
if (event.type == sf::Event::KeyPressed and event.key.code == sf::Keyboard::C) { // moins de réflexion spéculaire
refl_spec = std::max<float>(0, refl_spec-0.02);
scene.reset_ecrans = true;
}
if (event.type == sf::Event::KeyPressed and event.key.code == sf::Keyboard::E) { // étend la source
source_ext_ang *= 1.1;
scene.reset_ecrans = true;
}
if (event.type == sf::Event::KeyPressed and event.key.code == sf::Keyboard::Z) { // rétrécit la source
source_ext_ang *= 0.9;
scene.reset_ecrans = true;
}
if (event.type == sf::Event::KeyPressed and event.key.code == sf::Keyboard::K) { // mode déterministe (équirépartition des rayons émis)
panel_diffus->diff_met = ObjetLigne_Diffusant::Equirep;
panel_diffus->n_re_emit_par_intens = 300;
source_omni->dens_ang = 600;
source_omni->dir_alea = false;
scene.ecrans_do([] (Ecran_Base& e) { e.luminosite *= 80; });
scene.propag_rayons_dessin_gain *= 80;
scene.reset_ecrans = true;
}
},
/*f_pre_propag*/ [&] () {
// réinitialisation de `objet_bilan`
if (scene.reset_ecrans)
objet_bilan->reset();
// on fait toujours pointer la souce vers le centre du plan diffusant
vec_t v = panel_m - source_omni->position;
source_omni->secteur = angle_interv_t(-source_ext_ang,+source_ext_ang) + atan2f(v.y,v.x);
},
/*f_post_propag*/ [&] () {
// affichage du bilan d'énergie
objet_bilan->commit();
auto stat = objet_bilan->bilan();
float diff = stat.flux_in - stat.flux_out;
std::wstringstream s;
s << L"bilan énergie :" << std::endl;
s << std::setprecision(1) << std::fixed;
s << L"flux in " << stat.flux_in << ", flux out " << stat.flux_out << std::endl;
s << std::setprecision(1);
s << L"loss " << diff << ", loss rel " << 100*diff/stat.flux_in << std::endl;
auto text = sf::c01::buildText(scene.font, (objet_bilan->c + objet_bilan->R*vec_t{+0.8,+0.9}), {s.str()}, sf::Color::White);
scene.win_scene->draw(text);
});
return 0;
}

View File

@ -0,0 +1,98 @@
/********************************************************************************
* Scène de test des objets réfléchissants & réfractants (ObjetMilieux) :
* réflexion interne totale, séparation des couleurs (indice variable), lentille
* réelle "épaisse", miroirs, etc
********************************************************************************/
#include "SceneTest.h"
#include "ObjetMilieux.h"
int main (int, char const**) {
Scene_TestCommon scene (L"Réfraction et milieux", /*ecran image*/true, /*lentille image*/false);
scene.creer_ecrans_autour(/*lumino*/0.001, /*épaisseur*/0.02);
/// --------- Sources ---------
auto source_unique = scene.creer_source_unique_rayon(point_t{0.1,0.3}, /*dir*/0, /*ampl*/400.);
source_unique->spectre = Specte::polychromatique({{8,8,17,22}});
auto source_omni = scene.creer_source_omni_secteur(point_t{0.34722,0.50972}, /*ang_ext*/0.1, /*ang_base*/0, 10000);
/// --------- Lame ---------
std::vector<point_t> pts = { {0,0}, {0.2,0}, {0.2,0.03}, {0,0.03} };
pts = rotate_points({0.2,0.1}, 0.4*M_PI, pts);
pts = translate_points({0.2,0.35}, pts);
auto lame = std::make_shared<ObjetComposite_LignesMilieu>(pts, 2.4);
scene.objets.push_back(lame);
scene.ajouter_bouge_action(pts[0], [&] (point_t mouse, float angle, bool alt) -> point_t {
lame->re_positionne(mouse);
return mouse;
});
/// --------- Dioptre à indice variable ---------
std::function<float(float)> indice_refr_lambda = [] (float lambda) {
return 1 + 0.1 * lambda / lambda_color[N_COULEURS/2];
};
auto dioptre = std::make_shared<ObjetLigne_Milieux>(indice_refr_lambda, point_t{0.8,0.25}, point_t{0.85,0.35});
scene.objets.push_back(dioptre);
scene.ajouter_bouge_action(dioptre->a, [&] (point_t mouse, float angle, bool alt) -> point_t {
if (!alt) { dioptre->b = dioptre->b + (mouse - dioptre->a); dioptre->a = mouse; }
else dioptre->b = mouse;
return dioptre->a;
});
/// --------- Lentille réelle convergente ---------
// Les deux dioptres en arc de cercle formant la lentille convergente
scene.creer_objet<ObjetArc_Milieux>( 1.7, point_t{1.2,0.4}, point_t{1.2,0.6}, 0.3, false );
scene.creer_objet<ObjetArc_Milieux>( 1.7, point_t{1.2,0.6}, point_t{1.2,0.4}, 0.3, false );
// Diaphragme
scene.creer_objet<Objet_Bloqueur>(point_t{1.3,0.52}, point_t{1.3,0.6});
scene.creer_objet<Objet_Bloqueur>(point_t{1.3,0.48}, point_t{1.3,0.4});
/// --------- Miroir ---------
auto miroir = std::make_shared<ObjetLigne_Miroir>(point_t{0.2,0.45}, point_t{0.2,0.55});
scene.objets.push_back(miroir);
scene.ajouter_bouge_action(miroir->a, [&] (point_t mouse, float angle, bool alt) -> point_t {
if (!alt) { miroir->b = miroir->b + (mouse - miroir->a); miroir->a = mouse; }
else miroir->b = mouse;
return miroir->a;
});
/// --------- Filtre ---------
auto filtre_vert = scene.creer_objet<Objet_Filtre>((color_id_t)2, point_t{0.1,0.8}, point_t{0.2,0.8});
scene.ajouter_bouge_action(filtre_vert->a, [&] (point_t mouse, float angle, bool alt) -> point_t {
if (!alt) { filtre_vert->b = filtre_vert->b + (mouse - filtre_vert->a); filtre_vert->a = mouse; }
else filtre_vert->b = mouse;
return filtre_vert->a;
});
/// --------- Prisme ---------
const std::vector<point_t> pts_triangle = { {1,0}, {cosf(2*M_PI/3),sinf(2*M_PI/3)}, {cosf(-2*M_PI/3),sinf(-2*M_PI/3)} };
point_t prisme_position = {0.5, 0.8};
float prisme_angle = 0;
float prisme_taille = 0.1;
auto prisme_scene_it = scene.objets.insert(scene.objets.end(), nullptr);
auto prisme_move_action = [&] (point_t mouse, float angle, bool alt) -> point_t {
if (alt) prisme_angle = angle;
else prisme_position = mouse;
auto pts_tri = homothetie_points({0,0}, prisme_taille, pts_triangle);
pts_tri = rotate_points({0,0}, prisme_angle, pts_tri);
pts_tri = translate_points((vec_t)prisme_position, pts_tri);
*prisme_scene_it = std::make_shared<ObjetComposite_LignesMilieu>(pts_tri, indice_refr_lambda);
return prisme_position;
};
prisme_move_action(prisme_position, 0, false);
scene.ajouter_bouge_action(prisme_position, prisme_move_action);
scene.boucle(nullptr, nullptr, nullptr);
return 0;
}

50
LightRays/main_store.cpp Normal file
View File

@ -0,0 +1,50 @@
/********************************************************************************
* Scène de test d'un store constitué d'arcs de cercles diffusants
********************************************************************************/
#include "SceneTest.h"
#include "ObjetDiffusant.h"
int main (int, char const**) {
Scene_TestCommon scene (L"Soleil et store", /*ecran_image*/false, /*lentille image*/false);
scene.creer_ecrans_autour(/*lumino*/0.001, /*épaisseur*/0.02);
auto ciel = scene.creer_ciel_bleu();
// Soleil modélisé par des rayons parallèles : source de type `Source_LinParallels`
auto soleil = std::make_shared<Source_LinParallels>(point_t{0.1,0.65}, vec_t{0,0.3}, /*dir_angle*/-0.15*M_PI, spectre_blanc);
soleil->dens_lin = 6000;
scene.sources.push_back(soleil);
// Store : ensemble d'arcs de cercles diffusants régulièrement espacés
std::vector<std::shared_ptr<ObjetArc_Diffusant>> store;
for (float y = 0.4; y < 0.95; y += 0.03) {
auto arc = scene.creer_objet<ObjetArc_Diffusant>(
ObjetCourbe_Diffusant::BRDF_Lambert,
/*centre*/point_t{0.3,y}, /*rayon*/0.04, angle_interv_t(M_PI/4,M_PI/2), /*inv_int*/false
);
arc->n_re_emit_par_intens = 1;
store.push_back(arc);
}
// Rotation des éléments du store avec la souris
scene.ajouter_bouge_action(point_t{0.32,0.5}, [&] (point_t mouse, float angle, bool alt) -> point_t {
for (auto arc : store)
arc->ang = angle_interv_t(M_PI/4,M_PI/2) + (angle - M_PI/2);
return point_t{0.32,0.5};
});
// Un sol diffusant lambertien
auto sol = scene.creer_objet<ObjetLigne_Diffusant>(ObjetCourbe_Diffusant::BRDF_Lambert, point_t{0.3,0.3}, point_t{2,0.3});
sol->n_re_emit_par_intens = 1;
scene.static_text.insert(scene.static_text.begin(), {
L"Store constitué d'arcs de cercles diffusants, dont l'angle est ajoutable en cliquant sur son ancre.",
L"Le but est de minimiser la lumière éblouissante du soleil tout en maximisant la lumière diffusée (e.g. sur le plafond)",
L""
});
scene.boucle(nullptr, nullptr, nullptr);
return 0;
}

1
LightRays/sfml_c01.hpp Symbolic link
View File

@ -0,0 +1 @@
/Users/xif/Desktop/Code/Libs/sfml_c01.hpp

BIN
lois.pdf Normal file

Binary file not shown.

427
lois.tm Normal file

File diff suppressed because one or more lines are too long

BIN
rapport.pdf Normal file

Binary file not shown.

366
rapport.tm Normal file

File diff suppressed because one or more lines are too long

BIN
screens/milieux2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB