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;
<