Initial commit
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# Project exclude paths
|
||||||
|
/out/
|
8
.idea/.gitignore
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
6
.idea/compiler.xml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="JavacSettings">
|
||||||
|
<option name="ADDITIONAL_OPTIONS_STRING" value="-parameters" />
|
||||||
|
</component>
|
||||||
|
</project>
|
24
.idea/inspectionProfiles/Project_Default.xml
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<profile version="1.0">
|
||||||
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="CodeBlock2Expr" enabled="true" level="TEXT ATTRIBUTES" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="UnnecessaryBlockStatement" enabled="true" level="TEXT ATTRIBUTES" enabled_by_default="true" editorAttributes="NOT_USED_ELEMENT_ATTRIBUTES">
|
||||||
|
<option name="ignoreSwitchBranches" value="false" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="UnusedReturnValue" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="highestModifier" value="packageLocal" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="unused" enabled="true" level="WARNING" enabled_by_default="true" klass="packageLocal" inner_class="packageLocal" field="packageLocal" method="packageLocal" checkParameterExcludingHierarchy="false">
|
||||||
|
<option name="LOCAL_VARIABLE" value="true" />
|
||||||
|
<option name="FIELD" value="true" />
|
||||||
|
<option name="METHOD" value="true" />
|
||||||
|
<option name="CLASS" value="true" />
|
||||||
|
<option name="PARAMETER" value="true" />
|
||||||
|
<option name="REPORT_PARAMETER_FOR_PUBLIC_METHODS" value="true" />
|
||||||
|
<option name="ADD_MAINS_TO_ENTRIES" value="true" />
|
||||||
|
<option name="ADD_APPLET_TO_ENTRIES" value="true" />
|
||||||
|
<option name="ADD_SERVLET_TO_ENTRIES" value="true" />
|
||||||
|
<option name="ADD_NONJAVA_TO_ENTRIES" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
</profile>
|
||||||
|
</component>
|
9
.idea/libraries/caffeine_3_1_1.xml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<component name="libraryTable">
|
||||||
|
<library name="caffeine-3.1.1">
|
||||||
|
<CLASSES>
|
||||||
|
<root url="jar://$PROJECT_DIR$/lib/caffeine-3.1.1.jar!/" />
|
||||||
|
</CLASSES>
|
||||||
|
<JAVADOC />
|
||||||
|
<SOURCES />
|
||||||
|
</library>
|
||||||
|
</component>
|
9
.idea/libraries/checker_qual_3_22_0.xml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<component name="libraryTable">
|
||||||
|
<library name="checker-qual-3.22.0">
|
||||||
|
<CLASSES>
|
||||||
|
<root url="jar://$PROJECT_DIR$/lib/checker-qual-3.22.0.jar!/" />
|
||||||
|
</CLASSES>
|
||||||
|
<JAVADOC />
|
||||||
|
<SOURCES />
|
||||||
|
</library>
|
||||||
|
</component>
|
9
.idea/libraries/error_prone_annotations_2_14_0.xml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<component name="libraryTable">
|
||||||
|
<library name="error_prone_annotations-2.14.0">
|
||||||
|
<CLASSES>
|
||||||
|
<root url="jar://$PROJECT_DIR$/lib/error_prone_annotations-2.14.0.jar!/" />
|
||||||
|
</CLASSES>
|
||||||
|
<JAVADOC />
|
||||||
|
<SOURCES />
|
||||||
|
</library>
|
||||||
|
</component>
|
16
.idea/libraries/google_guava.xml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<component name="libraryTable">
|
||||||
|
<library name="google.guava" type="repository">
|
||||||
|
<properties maven-id="com.google.guava:guava:RELEASE" />
|
||||||
|
<CLASSES>
|
||||||
|
<root url="jar://$PROJECT_DIR$/lib/guava-31.1-jre.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/lib/failureaccess-1.0.1.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/lib/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/lib/jsr305-3.0.2.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/lib/checker-qual-3.12.0.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/lib/error_prone_annotations-2.11.0.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/lib/j2objc-annotations-1.3.jar!/" />
|
||||||
|
</CLASSES>
|
||||||
|
<JAVADOC />
|
||||||
|
<SOURCES />
|
||||||
|
</library>
|
||||||
|
</component>
|
14
.idea/libraries/kordamp_ikonli_core.xml
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<component name="libraryTable">
|
||||||
|
<library name="kordamp.ikonli.core" type="repository">
|
||||||
|
<properties maven-id="org.kordamp.ikonli:ikonli-core:RELEASE" />
|
||||||
|
<CLASSES>
|
||||||
|
<root url="jar://$PROJECT_DIR$/lib/ikonli-core-12.3.1.jar!/" />
|
||||||
|
</CLASSES>
|
||||||
|
<JAVADOC>
|
||||||
|
<root url="jar://$PROJECT_DIR$/lib/ikonli-core-12.3.1-javadoc.jar!/" />
|
||||||
|
</JAVADOC>
|
||||||
|
<SOURCES>
|
||||||
|
<root url="jar://$PROJECT_DIR$/lib/ikonli-core-12.3.1-sources.jar!/" />
|
||||||
|
</SOURCES>
|
||||||
|
</library>
|
||||||
|
</component>
|
11
.idea/libraries/kordamp_ikonli_javafx.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<component name="libraryTable">
|
||||||
|
<library name="kordamp.ikonli.javafx" type="repository">
|
||||||
|
<properties maven-id="org.kordamp.ikonli:ikonli-javafx:RELEASE" />
|
||||||
|
<CLASSES>
|
||||||
|
<root url="jar://$PROJECT_DIR$/lib/ikonli-javafx-12.3.1.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/lib/ikonli-core-12.3.1.jar!/" />
|
||||||
|
</CLASSES>
|
||||||
|
<JAVADOC />
|
||||||
|
<SOURCES />
|
||||||
|
</library>
|
||||||
|
</component>
|
11
.idea/libraries/kordamp_ikonli_materialdesign2_pack.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<component name="libraryTable">
|
||||||
|
<library name="kordamp.ikonli.materialdesign2.pack" type="repository">
|
||||||
|
<properties maven-id="org.kordamp.ikonli:ikonli-materialdesign2-pack:RELEASE" />
|
||||||
|
<CLASSES>
|
||||||
|
<root url="jar://$PROJECT_DIR$/lib/ikonli-materialdesign2-pack-12.3.1.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/lib/ikonli-core-12.3.1.jar!/" />
|
||||||
|
</CLASSES>
|
||||||
|
<JAVADOC />
|
||||||
|
<SOURCES />
|
||||||
|
</library>
|
||||||
|
</component>
|
9
.idea/libraries/mars_bits_2022_11_15.xml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<component name="libraryTable">
|
||||||
|
<library name="mars-bits-2022-11-15">
|
||||||
|
<CLASSES>
|
||||||
|
<root url="jar://$PROJECT_DIR$/lib/mars-bits-2022-11-15.jar!/" />
|
||||||
|
</CLASSES>
|
||||||
|
<JAVADOC />
|
||||||
|
<SOURCES />
|
||||||
|
</library>
|
||||||
|
</component>
|
9
.idea/libraries/object_instruments_2022_11_15.xml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<component name="libraryTable">
|
||||||
|
<library name="object-instruments-2022-11-15">
|
||||||
|
<CLASSES>
|
||||||
|
<root url="jar://$PROJECT_DIR$/lib/object-instruments-2022-11-15.jar!/" />
|
||||||
|
</CLASSES>
|
||||||
|
<JAVADOC />
|
||||||
|
<SOURCES />
|
||||||
|
</library>
|
||||||
|
</component>
|
6
.idea/misc.xml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_19" default="true" project-jdk-name="liberica-19" project-jdk-type="JavaSDK">
|
||||||
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
|
</component>
|
||||||
|
</project>
|
8
.idea/modules.xml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/GfxLab.iml" filepath="$PROJECT_DIR$/GfxLab.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
124
.idea/uiDesigner.xml
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Palette2">
|
||||||
|
<group name="Swing">
|
||||||
|
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
|
||||||
|
</item>
|
||||||
|
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
|
||||||
|
<initial-values>
|
||||||
|
<property name="text" value="Button" />
|
||||||
|
</initial-values>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
||||||
|
<initial-values>
|
||||||
|
<property name="text" value="RadioButton" />
|
||||||
|
</initial-values>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
||||||
|
<initial-values>
|
||||||
|
<property name="text" value="CheckBox" />
|
||||||
|
</initial-values>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
|
||||||
|
<initial-values>
|
||||||
|
<property name="text" value="Label" />
|
||||||
|
</initial-values>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||||
|
<preferred-size width="150" height="-1" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||||
|
<preferred-size width="150" height="-1" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||||
|
<preferred-size width="150" height="-1" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||||
|
<preferred-size width="150" height="50" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||||
|
<preferred-size width="150" height="50" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||||
|
<preferred-size width="150" height="50" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||||
|
<preferred-size width="150" height="50" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
|
||||||
|
<preferred-size width="150" height="50" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||||
|
<preferred-size width="150" height="50" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
||||||
|
<preferred-size width="200" height="200" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
||||||
|
<preferred-size width="200" height="200" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
|
||||||
|
<preferred-size width="-1" height="20" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
|
||||||
|
</item>
|
||||||
|
</group>
|
||||||
|
</component>
|
||||||
|
</project>
|
21
GfxLab.iml
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="JAVA_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/resources" type="java-resource" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
<orderEntry type="library" name="mars-bits-2022-11-15" level="project" />
|
||||||
|
<orderEntry type="library" name="object-instruments-2022-11-15" level="project" />
|
||||||
|
<orderEntry type="library" name="error_prone_annotations-2.14.0" level="project" />
|
||||||
|
<orderEntry type="library" name="caffeine-3.1.1" level="project" />
|
||||||
|
<orderEntry type="library" name="checker-qual-3.22.0" level="project" />
|
||||||
|
<orderEntry type="library" name="google.guava" level="project" />
|
||||||
|
<orderEntry type="library" name="kordamp.ikonli.materialdesign2.pack" level="project" />
|
||||||
|
<orderEntry type="library" name="kordamp.ikonli.core" level="project" />
|
||||||
|
<orderEntry type="library" name="kordamp.ikonli.javafx" level="project" />
|
||||||
|
</component>
|
||||||
|
</module>
|
12
README.md
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
# GfxLab
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
- Uključite JAR fajlove iz lib foldera u projekat (ako se to ne desi automatski).
|
||||||
|
- JavaFX biblioteke (base, controls, graphics, swing) dolaze u verzijama za različite platforme (win, linux, mac, mac-aarch64). Ubacite samo one koje odgovaraju vašoj platformi, a izbacite ostale.
|
||||||
|
|
||||||
|
- Pokrećete klasu `gui.App`.
|
||||||
|
|
||||||
|
- Nameštate šta želite da prikažete u klasi `playground.GfxLab`.
|
||||||
|
|
||||||
|
- Sve što budemo razvijali u toku nastave biće u paketu `graphics3d`.
|
BIN
lib/caffeine-3.1.1.jar
Normal file
BIN
lib/checker-qual-3.12.0.jar
Normal file
BIN
lib/checker-qual-3.22.0.jar
Normal file
BIN
lib/error_prone_annotations-2.11.0.jar
Normal file
BIN
lib/error_prone_annotations-2.14.0.jar
Normal file
BIN
lib/failureaccess-1.0.1.jar
Normal file
BIN
lib/guava-31.1-jre.jar
Normal file
BIN
lib/ikonli-core-12.3.1-javadoc.jar
Normal file
BIN
lib/ikonli-core-12.3.1-javadoc1.jar
Normal file
BIN
lib/ikonli-core-12.3.1-sources.jar
Normal file
BIN
lib/ikonli-core-12.3.1-sources1.jar
Normal file
BIN
lib/ikonli-core-12.3.1.jar
Normal file
BIN
lib/ikonli-core-12.3.11.jar
Normal file
BIN
lib/ikonli-core-12.3.12.jar
Normal file
BIN
lib/ikonli-javafx-12.3.1.jar
Normal file
BIN
lib/ikonli-materialdesign2-pack-12.3.1.jar
Normal file
BIN
lib/j2objc-annotations-1.3.jar
Normal file
BIN
lib/jsr305-3.0.2.jar
Normal file
BIN
lib/mars-bits-2022-11-15.jar
Normal file
BIN
lib/object-instruments-2022-11-15.jar
Normal file
BIN
resources/xyz/marsavic/gfxlab/resources/icons/mars 016.png
Normal file
After Width: | Height: | Size: 466 B |
BIN
resources/xyz/marsavic/gfxlab/resources/icons/mars 024.png
Normal file
After Width: | Height: | Size: 641 B |
BIN
resources/xyz/marsavic/gfxlab/resources/icons/mars 032.png
Normal file
After Width: | Height: | Size: 826 B |
BIN
resources/xyz/marsavic/gfxlab/resources/icons/mars 048.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
resources/xyz/marsavic/gfxlab/resources/icons/mars 064.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
resources/xyz/marsavic/gfxlab/resources/icons/mars 128.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
BIN
resources/xyz/marsavic/gfxlab/resources/icons/mars 256.png
Normal file
After Width: | Height: | Size: 6.4 KiB |
18
src/module-info.java
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
open module xyz.marsavic.GfxLab {
|
||||||
|
|
||||||
|
requires xyz.marsavic.MarsBits.main;
|
||||||
|
requires xyz.marsavic.objectinstruments;
|
||||||
|
|
||||||
|
requires javafx.controls;
|
||||||
|
requires javafx.graphics;
|
||||||
|
requires javafx.swing;
|
||||||
|
|
||||||
|
requires java.management;
|
||||||
|
|
||||||
|
requires org.kordamp.ikonli.core;
|
||||||
|
requires org.kordamp.ikonli.materialdesign2;
|
||||||
|
requires org.kordamp.ikonli.javafx;
|
||||||
|
|
||||||
|
requires com.github.benmanes.caffeine;
|
||||||
|
|
||||||
|
}
|
322
src/xyz/marsavic/gfxlab/Color.java
Normal file
|
@ -0,0 +1,322 @@
|
||||||
|
package xyz.marsavic.gfxlab;
|
||||||
|
|
||||||
|
import xyz.marsavic.utils.Numeric;
|
||||||
|
|
||||||
|
import java.util.function.DoubleUnaryOperator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Colors in linear sRGB color space.
|
||||||
|
*/
|
||||||
|
public class Color {
|
||||||
|
public static final Color BLACK = rgb(0, 0, 0);
|
||||||
|
public static final Color WHITE = rgb(1, 1, 1);
|
||||||
|
public static final Color DEBUG = rgb(1, 0, 0.5);
|
||||||
|
|
||||||
|
|
||||||
|
final double r, g, b;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private Color(double r, double g, double b) {
|
||||||
|
this.r = r;
|
||||||
|
this.g = g;
|
||||||
|
this.b = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Color rgb(double r, double g, double b) {
|
||||||
|
return new Color(r, g, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Color rgb(Vec3 v) {
|
||||||
|
return rgb(v.x(), v.y(), v.z());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Color gray(double k) {
|
||||||
|
return rgb(k, k, k);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Color hsb(double h, double s, double b) {
|
||||||
|
h = Numeric.mod(h);
|
||||||
|
int base = (int) (h * 6.0);
|
||||||
|
double f = h * 6.0 - base;
|
||||||
|
double p = b * (1.0 - s);
|
||||||
|
double q = b * (1.0 - s * f);
|
||||||
|
double t = b * (1.0 - s * (1.0 - f));
|
||||||
|
return switch (base) {
|
||||||
|
case 0 -> Color.rgb(b, t, p);
|
||||||
|
case 1 -> Color.rgb(q, b, p);
|
||||||
|
case 2 -> Color.rgb(p, b, t);
|
||||||
|
case 3 -> Color.rgb(p, q, b);
|
||||||
|
case 4 -> Color.rgb(t, p, b);
|
||||||
|
case 5 -> Color.rgb(b, p, q);
|
||||||
|
default -> null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Color hsb(Vec3 v) {
|
||||||
|
return hsb(v.x(), v.y(), v.z());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Color oklab(double l, double a, double b) {
|
||||||
|
double l_ = l + 0.3963377774f * a + 0.2158037573f * b;
|
||||||
|
double m_ = l - 0.1055613458f * a - 0.0638541728f * b;
|
||||||
|
double s_ = l - 0.0894841775f * a - 1.2914855480f * b;
|
||||||
|
|
||||||
|
double cl = l_ * l_ * l_;
|
||||||
|
double cm = m_ * m_ * m_;
|
||||||
|
double cs = s_ * s_ * s_;
|
||||||
|
|
||||||
|
double cr = 4.0767245293f * cl - 3.3072168827f * cm + 0.2307590544f * cs;
|
||||||
|
double cg = -1.2681437731f * cl + 2.6093323231f * cm - 0.3411344290f * cs;
|
||||||
|
double cb = -0.0041119885f * cl - 0.7034763098f * cm + 1.7068625689f * cs;
|
||||||
|
|
||||||
|
return
|
||||||
|
(
|
||||||
|
cr < 0 || cr > 1 ||
|
||||||
|
cg < 0 || cg > 1 ||
|
||||||
|
cb < 0 || cb > 1
|
||||||
|
) ? Color.BLACK :
|
||||||
|
Color.rgb(cr, cg, cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Color oklab(Vec3 v) {
|
||||||
|
return oklab(v.x(), v.y(), v.z());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Color oklabPolar(double h, double c, double l) {
|
||||||
|
return oklab(
|
||||||
|
l,
|
||||||
|
c * Numeric.cosT(h),
|
||||||
|
c * Numeric.sinT(h)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Color oklabPolar(Vec3 v) {
|
||||||
|
return oklabPolar(v.x(), v.y(), v.z());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Color code(int code) {
|
||||||
|
return rgb(
|
||||||
|
byteToValue((code >> 16) & 0xFF),
|
||||||
|
byteToValue((code >> 8) & 0xFF),
|
||||||
|
byteToValue((code ) & 0xFF)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public Color add(Color o) {
|
||||||
|
return rgb(r + o.r, g + o.g, b + o.b);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Color sub(Color o) {
|
||||||
|
return rgb(r - o.r, g - o.g, b - o.b);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Color mul(double c) {
|
||||||
|
return rgb(r * c, g * c, b * c);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Color mul(Color o) {
|
||||||
|
return rgb(r * o.r, g * o.g, b * o.b);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Color div(double c) {
|
||||||
|
return rgb(r / c, g / c, b / c);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Color pow(double c) {
|
||||||
|
return rgb(
|
||||||
|
Math.pow(r, c),
|
||||||
|
Math.pow(g, c),
|
||||||
|
Math.pow(b, c)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Color f(DoubleUnaryOperator f) {
|
||||||
|
return rgb(
|
||||||
|
f.applyAsDouble(r),
|
||||||
|
f.applyAsDouble(g),
|
||||||
|
f.applyAsDouble(b)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public double luminance() {
|
||||||
|
return
|
||||||
|
0.212655 * r +
|
||||||
|
0.715158 * g +
|
||||||
|
0.072187 * b;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For colors with components in [0, 1].
|
||||||
|
*/
|
||||||
|
public double perceivedLightness() {
|
||||||
|
double y = luminance(); // y should be between 0.0 and 1.0
|
||||||
|
|
||||||
|
if (y <= 216.0 / 24389.0 ) { // The CIE standard states 0.008856, but ( 6/29)^3 = 216/24389 is the intent for 0.008856451679036.
|
||||||
|
return y * (24389.0 / 27.0); // The CIE standard states 903.3 , but (29/ 3)^3 = 24389/ 27 is the intent for 903.296296296296296.
|
||||||
|
} else {
|
||||||
|
return Math.cbrt(y) * 116.0 - 16.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Vec3 hsb() {
|
||||||
|
// TODO Test, this was never tested.
|
||||||
|
double brightness = Math.max(Math.max(r, g), b);
|
||||||
|
double delta = brightness - Math.min(Math.min(r, g), b);
|
||||||
|
double saturation = brightness == 0 ? 0 : delta / brightness;
|
||||||
|
double hue;
|
||||||
|
|
||||||
|
if (saturation == 0) {
|
||||||
|
hue = 0;
|
||||||
|
} else {
|
||||||
|
double redC = (brightness - r) / delta;
|
||||||
|
double greenC = (brightness - g) / delta;
|
||||||
|
double blueC = (brightness - b) / delta;
|
||||||
|
if (r == brightness) {
|
||||||
|
hue = blueC - greenC;
|
||||||
|
} else if (g == brightness) {
|
||||||
|
hue = 2.0 + redC - blueC;
|
||||||
|
} else {
|
||||||
|
hue = 4.0 + greenC - redC;
|
||||||
|
}
|
||||||
|
hue = hue / 6.0;
|
||||||
|
if (hue < 0) {
|
||||||
|
hue = hue + 1.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Vec3.xyz(hue, saturation, brightness);
|
||||||
|
}
|
||||||
|
|
||||||
|
public double max() {
|
||||||
|
return Math.max(r, Math.max(g, b));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public int code() {
|
||||||
|
return
|
||||||
|
(0xFF000000) |
|
||||||
|
(valueToByte(r) << 16) |
|
||||||
|
(valueToByte(g) << 8) |
|
||||||
|
(valueToByte(b) );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public int codeClamp() {
|
||||||
|
return
|
||||||
|
(0xFF000000) |
|
||||||
|
(valueToByteClamp(r) << 16) |
|
||||||
|
(valueToByteClamp(g) << 8) |
|
||||||
|
(valueToByteClamp(b) );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
|
||||||
|
Color color = (Color) o;
|
||||||
|
|
||||||
|
if (Double.compare(color.r, r) != 0) return false;
|
||||||
|
if (Double.compare(color.g, g) != 0) return false;
|
||||||
|
return Double.compare(color.b, b) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
long temp =
|
||||||
|
Double.doubleToLongBits(r) +
|
||||||
|
31 * Double.doubleToLongBits(g) +
|
||||||
|
997 * Double.doubleToLongBits(b);
|
||||||
|
return (int) (temp ^ (temp >>> 32));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean zero() {
|
||||||
|
return (r == 0.0 && g == 0.0 && b == 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean notZero() {
|
||||||
|
return (r != 0.0 || g != 0.0 || b != 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean one() {
|
||||||
|
return (r == 1.0 && g == 1.0 && b == 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean notOne() {
|
||||||
|
return (r != 1.0 || g != 1.0 || b != 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format("(r: %6.3f, g: %6.3f, b: %6.3f | Y: %6.3f)", r, g, b, luminance());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ====================================================================================================
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sRGB gamma function. Approx. pow(v, 2.2).
|
||||||
|
*/
|
||||||
|
public static double inverseGamma(double v) {
|
||||||
|
if (v <= 0.04045) {
|
||||||
|
return v / 12.92;
|
||||||
|
} else {
|
||||||
|
return Math.pow((v + 0.055) / 1.055, 2.4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inverse of sRGB gamma function. Approx. pow(v, 1 / 2.2).
|
||||||
|
*/
|
||||||
|
public static double gamma(double v) {
|
||||||
|
if (v <= 0.0031308) {
|
||||||
|
return v * 12.92;
|
||||||
|
} else {
|
||||||
|
return 1.055 * Math.pow(v, 1.0 / 2.4) - 0.055;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static int valueToByte(double x) {
|
||||||
|
return (int) (gamma(x) * 255 + 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static int valueToByteClamp(double x) {
|
||||||
|
return Math.min(valueToByte(x), 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static double byteToValue(int x) {
|
||||||
|
return inverseGamma(x / 255.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
13
src/xyz/marsavic/gfxlab/ColorFunction.java
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package xyz.marsavic.gfxlab;
|
||||||
|
|
||||||
|
import xyz.marsavic.functions.interfaces.F1;
|
||||||
|
import xyz.marsavic.geometry.Vector;
|
||||||
|
|
||||||
|
|
||||||
|
public interface ColorFunction extends F1<Color, Vec3> {
|
||||||
|
|
||||||
|
default Color at(double t, Vector p) {
|
||||||
|
return at(Vec3.xp(t, p));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
17
src/xyz/marsavic/gfxlab/ColorFunctionT.java
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
package xyz.marsavic.gfxlab;
|
||||||
|
|
||||||
|
import xyz.marsavic.geometry.Vector;
|
||||||
|
|
||||||
|
|
||||||
|
public interface ColorFunctionT extends ColorFunction {
|
||||||
|
|
||||||
|
/** p is a point in (x, y) space. */
|
||||||
|
Color at(double t, Vector p);
|
||||||
|
|
||||||
|
/** p is a point in (t, x, y) space. */
|
||||||
|
@Override
|
||||||
|
default Color at(Vec3 p) {
|
||||||
|
return at(p.x(), p.p12());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
135
src/xyz/marsavic/gfxlab/Matrix.java
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
package xyz.marsavic.gfxlab;
|
||||||
|
|
||||||
|
import xyz.marsavic.functions.interfaces.F1;
|
||||||
|
import xyz.marsavic.geometry.Vector;
|
||||||
|
import xyz.marsavic.gfxlab.gui.UtilsGL;
|
||||||
|
|
||||||
|
|
||||||
|
public interface Matrix<E> {
|
||||||
|
|
||||||
|
Vector size();
|
||||||
|
|
||||||
|
|
||||||
|
E get(int x, int y);
|
||||||
|
|
||||||
|
|
||||||
|
default E get(Vector p) {
|
||||||
|
return get(p.xInt(), p.yInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void set(int x, int y, E value);
|
||||||
|
|
||||||
|
|
||||||
|
default void set(Vector p, E value) {
|
||||||
|
set(p.xInt(), p.yInt(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
default void fill(E value) {
|
||||||
|
int sizeX = size().xInt();
|
||||||
|
UtilsGL.parallelY(size(), y -> {
|
||||||
|
for (int x = 0; x < sizeX; x++) {
|
||||||
|
set(x, y, value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
default void fill(F1<E, Vector> f) {
|
||||||
|
UtilsGL.parallel(size(), p -> set(p, f.at(p))); // OPT?
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
default void copyFrom(Matrix<E> o) {
|
||||||
|
Vector size = Matrix.assertEqualSizes(this, o);
|
||||||
|
|
||||||
|
int sizeX = size.xInt();
|
||||||
|
UtilsGL.parallelY(size, y -> {
|
||||||
|
for (int x = 0; x < sizeX; x++) {
|
||||||
|
set(x, y, o.get(x, y));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// A pretty equivalent: UtilsGL.parallelYVec(size, p -> set(p, o.get(p)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...........................
|
||||||
|
|
||||||
|
|
||||||
|
static Vector assertEqualSizes(Matrix<?> a, Matrix<?> b) {
|
||||||
|
if (!b.size().equals(a.size())) {
|
||||||
|
throw new IllegalArgumentException("Matrix sizes are not equal.");
|
||||||
|
}
|
||||||
|
return a.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static Matrix<Color> createBlack(Vector size) {
|
||||||
|
return new MatrixData<>(size, Color.BLACK);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void add(Matrix<Color> a, Matrix<Color> b, Matrix<Color> result) {
|
||||||
|
Vector size = Matrix.assertEqualSizes(a, result);
|
||||||
|
|
||||||
|
int sizeX = size.xInt();
|
||||||
|
UtilsGL.parallelY(size, y -> {
|
||||||
|
for (int x = 0; x < sizeX; x++) {
|
||||||
|
result.set(x, y, a.get(x, y).add(b.get(x, y)));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static Matrix<Color> add(Matrix<Color> a, Matrix<Color> b) {
|
||||||
|
Matrix<Color> result = new MatrixData<>(a.size());
|
||||||
|
add(a, b, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void addInPlace(Matrix<Color> toChange, Matrix<Color> byHowMuch) {
|
||||||
|
Vector size = assertEqualSizes(toChange, byHowMuch);
|
||||||
|
|
||||||
|
int sizeX = size.xInt();
|
||||||
|
UtilsGL.parallelY(size, y -> {
|
||||||
|
for (int x = 0; x < sizeX; x++) {
|
||||||
|
toChange.set(x, y, toChange.get(x, y).add(byHowMuch.get(x, y)));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void mul(Matrix<Color> a, double k, Matrix<Color> result) {
|
||||||
|
Vector size = Matrix.assertEqualSizes(a, result);
|
||||||
|
|
||||||
|
int sizeX = size.xInt();
|
||||||
|
UtilsGL.parallelY(size, y -> {
|
||||||
|
for (int x = 0; x < sizeX; x++) {
|
||||||
|
result.set(x, y, a.get(x, y).mul(k));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static Matrix<Color> mul(Matrix<Color> a, double k) {
|
||||||
|
Matrix<Color> result = new MatrixData<>(a.size());
|
||||||
|
mul(a, k, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void mulInPlace(Matrix<Color> toChange, double factor) {
|
||||||
|
Vector size = toChange.size();
|
||||||
|
|
||||||
|
int sizeX = size.xInt();
|
||||||
|
UtilsGL.parallelY(size, y -> {
|
||||||
|
for (int x = 0; x < sizeX; x++) {
|
||||||
|
toChange.set(x, y, toChange.get(x, y).mul(factor));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
53
src/xyz/marsavic/gfxlab/MatrixData.java
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
package xyz.marsavic.gfxlab;
|
||||||
|
|
||||||
|
import xyz.marsavic.geometry.Vector;
|
||||||
|
import xyz.marsavic.gfxlab.gui.UtilsGL;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
|
||||||
|
public class MatrixData<E> implements Matrix<E> {
|
||||||
|
|
||||||
|
private final Vector size;
|
||||||
|
private final E[][] data;
|
||||||
|
|
||||||
|
|
||||||
|
public MatrixData(Vector size, E initialValue) {
|
||||||
|
this.size = size.floor();
|
||||||
|
//noinspection unchecked
|
||||||
|
data = (E[][]) new Object[this.size.yInt()][this.size.xInt()];
|
||||||
|
|
||||||
|
if (initialValue != null) {
|
||||||
|
fill(initialValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public MatrixData(Vector size) {
|
||||||
|
this(size, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Vector size() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public E get(int x, int y) {
|
||||||
|
return data[y][x];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void set(int x, int y, E value) {
|
||||||
|
data[y][x] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fill(E value) {
|
||||||
|
UtilsGL.parallel(data.length, y -> Arrays.fill(data[y], value));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
66
src/xyz/marsavic/gfxlab/MatrixInts.java
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
package xyz.marsavic.gfxlab;
|
||||||
|
|
||||||
|
|
||||||
|
import xyz.marsavic.geometry.Vector;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
|
||||||
|
public final class MatrixInts implements Matrix<Integer> {
|
||||||
|
|
||||||
|
private final int width;
|
||||||
|
private final int[] data; // TODO test an implementation with int[][] and compare performances.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public MatrixInts(Vector size) {
|
||||||
|
this.width = size.xInt();
|
||||||
|
this.data = new int[size.areaInt()];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public int height() {
|
||||||
|
return array().length / width;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public int width() {
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Vector size() {
|
||||||
|
return Vector.xy(width(), height());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer get(int x, int y) {
|
||||||
|
return data[y * width + x];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void set(int x, int y, Integer value) {
|
||||||
|
data[y * width + x] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void copyFrom(MatrixInts source) {
|
||||||
|
Matrix.assertEqualSizes(this, source);
|
||||||
|
System.arraycopy(source.array(), 0, array(), 0, width);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fill(Integer value) {
|
||||||
|
Arrays.fill(data, value); // Optimize: Parallelism on blocks might be faster?
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public int[] array() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
17
src/xyz/marsavic/gfxlab/Renderer.java
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
package xyz.marsavic.gfxlab;
|
||||||
|
|
||||||
|
import xyz.marsavic.functions.interfaces.A1;
|
||||||
|
import xyz.marsavic.functions.interfaces.A2;
|
||||||
|
import xyz.marsavic.gfxlab.gui.UtilsGL;
|
||||||
|
import xyz.marsavic.utils.Numeric;
|
||||||
|
|
||||||
|
public record Renderer(Vec3 size, A2<Matrix<Integer>, Double> aFillFrameInt) {
|
||||||
|
|
||||||
|
public void process(double t, A1<Matrix<Integer>> aProcess) {
|
||||||
|
UtilsGL.matricesInt.borrow(size.p12(), m -> {
|
||||||
|
aFillFrameInt.execute(m, Numeric.mod(t, size.x()));
|
||||||
|
aProcess.execute(m);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
48
src/xyz/marsavic/gfxlab/Transformation.java
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
package xyz.marsavic.gfxlab;
|
||||||
|
|
||||||
|
|
||||||
|
import xyz.marsavic.functions.interfaces.F1;
|
||||||
|
import xyz.marsavic.gfxlab.graphics3d.Affine;
|
||||||
|
import xyz.marsavic.gfxlab.graphics3d.Ray;
|
||||||
|
|
||||||
|
|
||||||
|
public interface Transformation extends F1<Vec3, Vec3> {
|
||||||
|
|
||||||
|
/** The default implementation might not be meaningful with non-affine transformations. */
|
||||||
|
default Ray at(Ray r) {
|
||||||
|
return Ray.pq(at(r.p()), at(r.p().add(r.d())));
|
||||||
|
}
|
||||||
|
|
||||||
|
default Transformation then(Transformation outer) {
|
||||||
|
return p -> outer.at(at(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --------------------
|
||||||
|
|
||||||
|
|
||||||
|
Transformation identity = p -> p;
|
||||||
|
|
||||||
|
|
||||||
|
/** Returns a linear transformation which transforms unit vectors the same way as this transform. */
|
||||||
|
default Affine linearize() {
|
||||||
|
return Affine.unitVectors(
|
||||||
|
at(Vec3.EX),
|
||||||
|
at(Vec3.EY),
|
||||||
|
at(Vec3.EZ)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
default Affine gradient(Vec3 p) {
|
||||||
|
// ?
|
||||||
|
final double eps = 0x1p-16;
|
||||||
|
|
||||||
|
return Affine.unitVectors(
|
||||||
|
(at(p.add(Vec3.EX.mul(eps))).sub(at(p))).div(eps),
|
||||||
|
(at(p.add(Vec3.EY.mul(eps))).sub(at(p))).div(eps),
|
||||||
|
(at(p.add(Vec3.EZ.mul(eps))).sub(at(p))).div(eps)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
31
src/xyz/marsavic/gfxlab/TransformationsFromSize.java
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
package xyz.marsavic.gfxlab;
|
||||||
|
|
||||||
|
import xyz.marsavic.functions.interfaces.F1;
|
||||||
|
|
||||||
|
|
||||||
|
public class TransformationsFromSize {
|
||||||
|
|
||||||
|
public static final F1<Transformation, Vec3> toIdentity = TransformationsFromSize::toIdentity;
|
||||||
|
public static final F1<Transformation, Vec3> toUnitBox = TransformationsFromSize::toUnitBox;
|
||||||
|
public static final F1<Transformation, Vec3> toGeometric = TransformationsFromSize::toGeometric;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static Transformation toIdentity(Vec3 s) {
|
||||||
|
return Transformation.identity;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Transformation toUnitBox(Vec3 s) {
|
||||||
|
return p -> p.div(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static final Vec3 t = Vec3.xyz(-1, -1, 1);
|
||||||
|
|
||||||
|
public static Transformation toGeometric(Vec3 s) {
|
||||||
|
Vec3 c = Vec3.xyz( 2, 2, -2).div(s);
|
||||||
|
return p -> p.mul(c).add(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
457
src/xyz/marsavic/gfxlab/Vec3.java
Normal file
|
@ -0,0 +1,457 @@
|
||||||
|
package xyz.marsavic.gfxlab;
|
||||||
|
|
||||||
|
import xyz.marsavic.geometry.Vector;
|
||||||
|
import xyz.marsavic.gfxlab.elements.Immutable;
|
||||||
|
import xyz.marsavic.random.RNG;
|
||||||
|
import xyz.marsavic.utils.Numeric;
|
||||||
|
|
||||||
|
import java.util.function.DoubleBinaryOperator;
|
||||||
|
import java.util.function.DoubleUnaryOperator;
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
public class Vec3 {
|
||||||
|
public static final Vec3 ZERO = xyz(0, 0, 0);
|
||||||
|
public static final Vec3 ONES = xyz(1, 1, 1);
|
||||||
|
public static final Vec3 EX = xyz(1, 0, 0);
|
||||||
|
public static final Vec3 EY = xyz(0, 1, 0);
|
||||||
|
public static final Vec3 EZ = xyz(0, 0, 1);
|
||||||
|
public static final Vec3 EXY = xyz(1, 1, 0);
|
||||||
|
public static final Vec3 EYZ = xyz(0, 1, 1);
|
||||||
|
public static final Vec3 EZX = xyz(1, 0, 1);
|
||||||
|
public static final Vec3 EXYZ = xyz(1, 1, 1);
|
||||||
|
public static final Vec3[] E = {EX, EY, EZ};
|
||||||
|
|
||||||
|
public static final Vec3 P012 = xyz(0, 1, 2);
|
||||||
|
public static final Vec3 P021 = xyz(0, 2, 1);
|
||||||
|
public static final Vec3 P102 = xyz(1, 0, 2);
|
||||||
|
public static final Vec3 P120 = xyz(1, 2, 0);
|
||||||
|
public static final Vec3 P201 = xyz(2, 0, 1);
|
||||||
|
public static final Vec3 P210 = xyz(2, 1, 0);
|
||||||
|
public static final Vec3[] PERMUTATIONS = {P012, P021, P102, P120, P201, P210};
|
||||||
|
|
||||||
|
private final double x, y, z;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public Vec3(double x, double y, double z) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.z = z;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Vec3 xyz(double x, double y, double z) {
|
||||||
|
return new Vec3(x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Vec3 fromArray(int[] i) {
|
||||||
|
return xyz(i[0], i[1], i[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Vec3 fromArray(double[] i) {
|
||||||
|
return xyz(i[0], i[1], i[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Vec3 xp(double x, Vector p) {
|
||||||
|
return xyz(x, p.x(), p.y());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Vec3 yp(double y, Vector p) {
|
||||||
|
return xyz(p.x(), y, p.y());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Vec3 zp(double z, Vector p) {
|
||||||
|
return xyz(p.x(), p.y(), z);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public double x() {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public double y() {
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public double z() {
|
||||||
|
return z;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public double get(int i) {
|
||||||
|
return switch (i) {
|
||||||
|
case 0 -> x();
|
||||||
|
case 1 -> y();
|
||||||
|
case 2 -> z();
|
||||||
|
default -> throw new IllegalArgumentException();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public double[] toArray() {
|
||||||
|
return new double[] { x(), y(), z() };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public int[] toArrayInt() {
|
||||||
|
return new int[] { (int) x(), (int) y(), (int) z() };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (o == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (o instanceof Vec3 vec3) {
|
||||||
|
return
|
||||||
|
(vec3.x() == x()) &&
|
||||||
|
(vec3.y() == y()) &&
|
||||||
|
(vec3.z() == z())
|
||||||
|
;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
long temp =
|
||||||
|
Double.doubleToLongBits(x) +
|
||||||
|
31 * Double.doubleToLongBits(y) +
|
||||||
|
997 * Double.doubleToLongBits(z);
|
||||||
|
return (int) (temp ^ (temp >>> 32));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vec3 add(Vec3 o) {
|
||||||
|
return xyz(x() + o.x(), y() + o.y(), z() + o.z());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Vec3 sub(Vec3 o) {
|
||||||
|
return xyz(x() - o.x(), y() - o.y(), z() - o.z());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Vec3 mul(double k) {
|
||||||
|
return xyz(x() * k, y() * k, z() * k);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Vec3 mul(Vec3 o) {
|
||||||
|
return xyz(x() * o.x(), y() * o.y(), z() * o.z());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Vec3 div(double k) {
|
||||||
|
return xyz(x() / k, y() / k, z() / k);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Vec3 div(Vec3 o) {
|
||||||
|
return xyz(x() / o.x(), y() / o.y(), z() / o.z());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public double lengthSquared() {
|
||||||
|
return x() * x() + y() * y() + z() * z();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public double length() {
|
||||||
|
return Math.sqrt(lengthSquared());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Vec3 normalized_() {
|
||||||
|
return div(length());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Vec3 normalizedTo(double l) {
|
||||||
|
return mul(l / length());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Vec3 inverse() {
|
||||||
|
return xyz(-x(), -y(), -z());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public double dot(Vec3 o) {
|
||||||
|
return x() * o.x() + y() * o.y() + z() * o.z();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Vec3 cross(Vec3 o) {
|
||||||
|
return xyz(
|
||||||
|
y() * o.z() - z() * o.y(),
|
||||||
|
z() * o.x() - x() * o.z(),
|
||||||
|
x() * o.y() - y() * o.x()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Vec3 projection(Vec3 d) {
|
||||||
|
return d.mul(this.dot(d) / d.lengthSquared());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Vec3 projectionN(Vec3 d_) {
|
||||||
|
return d_.mul(this.dot(d_));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Vec3 rejection(Vec3 d) {
|
||||||
|
return this.sub(projection(d));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Vec3 rejectionN(Vec3 d_) {
|
||||||
|
return this.sub(projectionN(d_));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public double min() {
|
||||||
|
return Math.min(Math.min(x(), y()), z());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public double max() {
|
||||||
|
return Math.max(Math.max(x(), y()), z());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Vec3 minIndicator() {
|
||||||
|
if (x() < y()) {
|
||||||
|
return x() < z() ? Vec3.EX : Vec3.EZ;
|
||||||
|
} else {
|
||||||
|
return y() < z() ? Vec3.EY : Vec3.EZ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Vec3 maxIndicator() {
|
||||||
|
if (x() > y()) {
|
||||||
|
return x() > z() ? Vec3.EX : Vec3.EZ;
|
||||||
|
} else {
|
||||||
|
return y() > z() ? Vec3.EY : Vec3.EZ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public int minIndex() {
|
||||||
|
if (x() < y()) {
|
||||||
|
return x() < z() ? 0 : 2;
|
||||||
|
} else {
|
||||||
|
return y() < z() ? 1 : 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public int maxIndex() {
|
||||||
|
if (x() > y()) {
|
||||||
|
return x() > z() ? 0 : 2;
|
||||||
|
} else {
|
||||||
|
return y() > z() ? 1 : 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Vec3 ranks() {
|
||||||
|
if (x() < y()) {
|
||||||
|
if (y() < z()) {
|
||||||
|
return P012;
|
||||||
|
} else {
|
||||||
|
if (x() < z()) {
|
||||||
|
return P021;
|
||||||
|
} else {
|
||||||
|
return P201;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (x() < z()) {
|
||||||
|
return P102;
|
||||||
|
} else {
|
||||||
|
if (y() < z()) {
|
||||||
|
return P120;
|
||||||
|
} else {
|
||||||
|
return P210;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Vec3 sign() {
|
||||||
|
return xyz(Numeric.sign(x()), Numeric.sign(y()), Numeric.sign(z()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Vec3 abs() {
|
||||||
|
return xyz(Math.abs(x()), Math.abs(y()), Math.abs(z()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean allZero() {
|
||||||
|
return (x() == 0) && (y() == 0) && (z() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean anyZero() {
|
||||||
|
return (x() == 0) || (y() == 0) || (z() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Vec3 floor() {
|
||||||
|
return xyz(Math.floor(x()), Math.floor(y()), Math.floor(z()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vec3 floor(Vec3 d) {
|
||||||
|
return this.div(d).floor().mul(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vec3 ceil() {
|
||||||
|
return xyz(Math.ceil(x()), Math.ceil(y()), Math.ceil(z()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vec3 ceil(Vec3 d) {
|
||||||
|
return this.div(d).ceil().mul(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vec3 round() {
|
||||||
|
return xyz(Math.round(x()), Math.round(y()), Math.round(z()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vec3 round(Vec3 d) {
|
||||||
|
return this.div(d).round().mul(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Snaps each component to floor or ceil, depending on whether the corresponding component of s is not one. */
|
||||||
|
|
||||||
|
public Vec3 snap(Vec3 s) {
|
||||||
|
return Vec3.xyz (
|
||||||
|
s.x() != 1 ? Math.floor(x()) : Math.ceil(x()),
|
||||||
|
s.y() != 1 ? Math.floor(y()) : Math.ceil(y()),
|
||||||
|
s.z() != 1 ? Math.floor(z()) : Math.ceil(z())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public double product() {
|
||||||
|
return x() * y() * z();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Vec3 f(DoubleUnaryOperator f) {
|
||||||
|
return Vec3.f(f, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Vec3 f(DoubleBinaryOperator f, Vec3 v) {
|
||||||
|
return Vec3.f(f, this, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Vec3[] split() {
|
||||||
|
return new Vec3[] {
|
||||||
|
Vec3.xyz(x(), 0, 0),
|
||||||
|
Vec3.xyz(0, y(), 0),
|
||||||
|
Vec3.xyz(0, 0, z()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Vec3 only(int i) {
|
||||||
|
return switch (i) {
|
||||||
|
case 0 -> Vec3.xyz(x(), 0, 0);
|
||||||
|
case 1 -> Vec3.xyz(0, y(), 0);
|
||||||
|
case 2 -> Vec3.xyz(0, 0, z());
|
||||||
|
default -> throw new IllegalArgumentException();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Vector p01() {
|
||||||
|
return Vector.xy(x(), y());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector p12() {
|
||||||
|
return Vector.xy(y(), z());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The result is obtained by applying the affine transform that maps 0 to -1 and 1 to 1. */
|
||||||
|
public Vec3 ZOtoMP() {
|
||||||
|
return mul(2).sub(Vec3.EXYZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The result is obtained by applying the affine transform that maps -1 to 0 and 1 to 1. */
|
||||||
|
public Vec3 MPtoZO() {
|
||||||
|
return add(Vec3.EXYZ).mul(0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format("(%6.2f, %6.2f, %6.2f)", x(), y(), z());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Vec3 f(DoubleUnaryOperator f, Vec3 u) {
|
||||||
|
return Vec3.xyz(
|
||||||
|
f.applyAsDouble(u.x()),
|
||||||
|
f.applyAsDouble(u.y()),
|
||||||
|
f.applyAsDouble(u.z())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Vec3 f(DoubleBinaryOperator f, Vec3 u, Vec3 v) {
|
||||||
|
return Vec3.xyz(
|
||||||
|
f.applyAsDouble(u.x(), v.x()),
|
||||||
|
f.applyAsDouble(u.y(), v.y()),
|
||||||
|
f.applyAsDouble(u.z(), v.z())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Vec3 min(Vec3 a, Vec3 b) {
|
||||||
|
return xyz(
|
||||||
|
Math.min(a.x(), b.x()),
|
||||||
|
Math.min(a.y(), b.y()),
|
||||||
|
Math.min(a.z(), b.z())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Vec3 max(Vec3 a, Vec3 b) {
|
||||||
|
return xyz(
|
||||||
|
Math.max(a.x(), b.x()),
|
||||||
|
Math.max(a.y(), b.y()),
|
||||||
|
Math.max(a.z(), b.z())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Vec3 lerp(Vec3 v0, Vec3 v1, double t) {
|
||||||
|
return v0.mul(1 - t).add(v1.mul(t));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Vec3 random(RNG rng) {
|
||||||
|
return Vec3.xyz(rng.nextDouble(), rng.nextDouble(), rng.nextDouble());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
114
src/xyz/marsavic/gfxlab/elements/Element.java
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
package xyz.marsavic.gfxlab.elements;
|
||||||
|
|
||||||
|
import xyz.marsavic.functions.interfaces.A1;
|
||||||
|
import xyz.marsavic.functions.interfaces.F0;
|
||||||
|
import xyz.marsavic.reactions.values.EventInvalidated;
|
||||||
|
import xyz.marsavic.utils.Caches;
|
||||||
|
import xyz.marsavic.utils.Reflection;
|
||||||
|
import xyz.marsavic.utils.Utils;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If any of element's inputs is changed or invalidated, the element will fire an invalidation event.
|
||||||
|
* All elements must be thread safe. All methods must use lockForReading or lockForWriting.
|
||||||
|
* lockForReading guarantees that nothing will change while inside the lock's action.
|
||||||
|
* lockForWriting guarantees that
|
||||||
|
*
|
||||||
|
* When other objects need to do a block operation by repeatedly calling this element's functions, they should
|
||||||
|
* lock it for reading to ensure the same state throughout the block operation.
|
||||||
|
*/
|
||||||
|
public abstract class Element {
|
||||||
|
|
||||||
|
public class Input<T> {
|
||||||
|
|
||||||
|
private Output<T> output;
|
||||||
|
|
||||||
|
|
||||||
|
private final A1<EventInvalidated> onInvalidated = e -> {
|
||||||
|
onInputInvalidated(this, e);
|
||||||
|
onInputChangedOrInvalidated(this);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// TODO Public constructor is a problem. Anyone can "attach" an input to an element and send invalidation events.
|
||||||
|
public Input(Output<T> output) {
|
||||||
|
connect(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public T get() {
|
||||||
|
return output.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void connect(Output<T> newOutput) {
|
||||||
|
if (newOutput == output) return;
|
||||||
|
|
||||||
|
if (output != null) {
|
||||||
|
output.onInvalidated().remove(onInvalidated);
|
||||||
|
}
|
||||||
|
output = newOutput;
|
||||||
|
output.onInvalidated().add(onInvalidated);
|
||||||
|
|
||||||
|
onInputChanged(this);
|
||||||
|
onInputChangedOrInvalidated(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Convenience method that can be called from constructor and when re-initializing data after the inputs change or
|
||||||
|
* get invalidated. This is the default behaviour unless the method onInputChangedOrInvalidated is overridden. */
|
||||||
|
protected void buildItUp() {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Convenience method that can be called when disposing the object and when re-initializing data after the inputs
|
||||||
|
* change or get invalidated. This is the default behaviour unless the method onInputChangedOrInvalidated is
|
||||||
|
* overridden. */
|
||||||
|
protected void tearItDown() {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Override this to react to the internal changes in the input object.
|
||||||
|
* This is called when the input object changed internally, but it is still the same object, i.e. input.get()
|
||||||
|
* will return the same reference as before. */
|
||||||
|
protected void onInputInvalidated(Input<?> input, EventInvalidated event) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Override this to react when input object changes.
|
||||||
|
* This is called when input.get() is not the same reference as before.
|
||||||
|
* Use lock for
|
||||||
|
**/
|
||||||
|
protected <T> void onInputChanged(Input<T> input) {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected <T> void onInputChangedOrInvalidated(Input<T> input) {
|
||||||
|
tearItDown();
|
||||||
|
buildItUp();
|
||||||
|
outputs().forEach(Invalidatable.Base::fireInvalidated);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected final F0<List<Input<?>>> inputsFromFields = Caches.cached(() ->
|
||||||
|
Utils.cast(Reflection.fieldsOfType(this, Input.class))
|
||||||
|
);
|
||||||
|
|
||||||
|
protected final F0<List<Output<?>>> outputsFromFields = Caches.cached(() ->
|
||||||
|
Utils.cast(Reflection.fieldsOfType(this, Output.class))
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public List<Input<?>> inputs() {
|
||||||
|
return inputsFromFields.at();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Output<?>> outputs() {
|
||||||
|
return outputsFromFields.at();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
30
src/xyz/marsavic/gfxlab/elements/ElementF.java
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
package xyz.marsavic.gfxlab.elements;
|
||||||
|
|
||||||
|
import xyz.marsavic.functions.interfaces.F0;
|
||||||
|
import xyz.marsavic.functions.interfaces.F1;
|
||||||
|
import xyz.marsavic.functions.interfaces.F2;
|
||||||
|
import xyz.marsavic.functions.interfaces.F3;
|
||||||
|
|
||||||
|
public abstract class ElementF<R> extends Element implements HasOutput<R> {
|
||||||
|
|
||||||
|
public abstract R object();
|
||||||
|
|
||||||
|
|
||||||
|
public final Output<R> out = new Output<>(this::object);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Output<R> out() {
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static <R> ElementF0<R> e(R result) {
|
||||||
|
return e(() -> result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <R > ElementF0<R > e(F0<R> f ) { return new ElementF0.Lazy<>(f ); }
|
||||||
|
public static <R, P0 > ElementF1<R, P0 > e(F1<R, P0 > f, HasOutput<? extends P0> p0 ) { return new ElementF1.Lazy<>(f, p0 ); }
|
||||||
|
public static <R, P0, P1 > ElementF2<R, P0, P1 > e(F2<R, P0, P1 > f, HasOutput<? extends P0> p0, HasOutput<? extends P1> p1 ) { return new ElementF2.Lazy<>(f, p0, p1 ); }
|
||||||
|
public static <R, P0, P1, P2> ElementF3<R, P0, P1, P2> e(F3<R, P0, P1, P2> f, HasOutput<? extends P0> p0, HasOutput<? extends P1> p1, HasOutput<? extends P2> p2) { return new ElementF3.Lazy<>(f, p0, p1, p2); }
|
||||||
|
|
||||||
|
}
|
40
src/xyz/marsavic/gfxlab/elements/ElementF0.java
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
package xyz.marsavic.gfxlab.elements;
|
||||||
|
|
||||||
|
import xyz.marsavic.functions.interfaces.F0;
|
||||||
|
|
||||||
|
|
||||||
|
public abstract class ElementF0<R> extends ElementF<R> {
|
||||||
|
|
||||||
|
protected final F0<R> f;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public ElementF0(F0<R> f) {
|
||||||
|
this.f = f;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static class Lazy<R> extends ElementF0<R> {
|
||||||
|
|
||||||
|
public Lazy(F0<R> f) {
|
||||||
|
super(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
private R object;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void buildItUp() {
|
||||||
|
object = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public R object() {
|
||||||
|
if (object == null) {
|
||||||
|
object = f.at();
|
||||||
|
}
|
||||||
|
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
43
src/xyz/marsavic/gfxlab/elements/ElementF1.java
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
package xyz.marsavic.gfxlab.elements;
|
||||||
|
|
||||||
|
import xyz.marsavic.functions.interfaces.F1;
|
||||||
|
|
||||||
|
|
||||||
|
public abstract class ElementF1<R, P0> extends ElementF<R> {
|
||||||
|
|
||||||
|
protected final F1<R, P0> f;
|
||||||
|
|
||||||
|
protected final Input<? extends P0> in0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public ElementF1(F1<R, P0> f, HasOutput<? extends P0> p0) {
|
||||||
|
this.f = f;
|
||||||
|
in0 = new Input<>(p0.out());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static class Lazy<R, P0> extends ElementF1<R, P0> {
|
||||||
|
|
||||||
|
public Lazy(F1<R, P0> f, HasOutput<? extends P0> p0) {
|
||||||
|
super(f, p0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private R object;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void buildItUp() {
|
||||||
|
object = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public R object() {
|
||||||
|
if (object == null) {
|
||||||
|
object = f.at(in0.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
45
src/xyz/marsavic/gfxlab/elements/ElementF2.java
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
package xyz.marsavic.gfxlab.elements;
|
||||||
|
|
||||||
|
import xyz.marsavic.functions.interfaces.F2;
|
||||||
|
|
||||||
|
|
||||||
|
public abstract class ElementF2<R, P0, P1> extends ElementF<R> {
|
||||||
|
|
||||||
|
protected final F2<R, P0, P1> f;
|
||||||
|
|
||||||
|
protected final Input<? extends P0> in0;
|
||||||
|
protected final Input<? extends P1> in1;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public ElementF2(F2<R, P0, P1> f, HasOutput<? extends P0> p0, HasOutput<? extends P1> p1) {
|
||||||
|
this.f = f;
|
||||||
|
in0 = new Input<>(p0.out());
|
||||||
|
in1 = new Input<>(p1.out());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static class Lazy<R, P0, P1> extends ElementF2<R, P0, P1> {
|
||||||
|
|
||||||
|
public Lazy(F2<R, P0, P1> f, HasOutput<? extends P0> p0, HasOutput<? extends P1> p1) {
|
||||||
|
super(f, p0, p1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private R object;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void buildItUp() {
|
||||||
|
object = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public R object() {
|
||||||
|
if (object == null) {
|
||||||
|
object = f.at(in0.get(), in1.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
47
src/xyz/marsavic/gfxlab/elements/ElementF3.java
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
package xyz.marsavic.gfxlab.elements;
|
||||||
|
|
||||||
|
import xyz.marsavic.functions.interfaces.F3;
|
||||||
|
|
||||||
|
|
||||||
|
public abstract class ElementF3<R, P0, P1, P2> extends ElementF<R> {
|
||||||
|
|
||||||
|
protected final F3<R, P0, P1, P2> f;
|
||||||
|
|
||||||
|
protected final Input<? extends P0> in0;
|
||||||
|
protected final Input<? extends P1> in1;
|
||||||
|
protected final Input<? extends P2> in2;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public ElementF3(F3<R, P0, P1, P2> f, HasOutput<? extends P0> p0, HasOutput<? extends P1> p1, HasOutput<? extends P2> p2) {
|
||||||
|
this.f = f;
|
||||||
|
in0 = new Input<>(p0.out());
|
||||||
|
in1 = new Input<>(p1.out());
|
||||||
|
in2 = new Input<>(p2.out());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static class Lazy<R, P0, P1, P2> extends ElementF3<R, P0, P1, P2> {
|
||||||
|
|
||||||
|
public Lazy(F3<R, P0, P1, P2> f, HasOutput<? extends P0> p0, HasOutput<? extends P1> p1, HasOutput<? extends P2> p2) {
|
||||||
|
super(f, p0, p1, p2);
|
||||||
|
}
|
||||||
|
|
||||||
|
private R object;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void buildItUp() {
|
||||||
|
object = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public R object() {
|
||||||
|
if (object == null) {
|
||||||
|
object = f.at(in0.get(), in1.get(), in2.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
86
src/xyz/marsavic/gfxlab/elements/ElementPureLazy.java
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
package xyz.marsavic.gfxlab.elements;
|
||||||
|
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
// Remove this class, it's not needed any more? Or keep it because it allows arbitrarily many inputs.
|
||||||
|
public class ElementPureLazy<T> extends Element {
|
||||||
|
|
||||||
|
private final List<Input<?>> inputs;
|
||||||
|
public final Output<T> out = new Output<>(this::object);
|
||||||
|
|
||||||
|
private T object;
|
||||||
|
private final Constructor<T> constructor;
|
||||||
|
private final int nParameters;
|
||||||
|
|
||||||
|
|
||||||
|
public ElementPureLazy(Class<T> type, Output<?>... outputs) {
|
||||||
|
// if (!UtilsElements.looksImmutable(type)) {
|
||||||
|
// throw new IllegalArgumentException("The type does not look immutable. If it is, annotate it with @Immutable.");
|
||||||
|
// }
|
||||||
|
|
||||||
|
//noinspection unchecked
|
||||||
|
Constructor<T>[] constructors = (Constructor<T>[]) type.getConstructors();
|
||||||
|
if (constructors.length != 1) {
|
||||||
|
throw new IllegalArgumentException("Specified class must have exactly one public constructor.");
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor = constructors[0];
|
||||||
|
nParameters = constructor.getParameterCount();
|
||||||
|
if (nParameters != outputs.length) {
|
||||||
|
throw new IllegalArgumentException("The number of provided outputs does not match the number of arguments of the constructor.");
|
||||||
|
}
|
||||||
|
|
||||||
|
inputs = new ArrayList<>(nParameters);
|
||||||
|
// Class<?>[] types = constructor.getParameterTypes();
|
||||||
|
for (Output<?> output : outputs) {
|
||||||
|
// TODO Type check somehow!?
|
||||||
|
inputs.add(new Input<>(output));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> ElementPureLazy<T> of(Class<T> type, Object... args) {
|
||||||
|
//noinspection SuspiciousToArrayCall
|
||||||
|
return new ElementPureLazy<T>(
|
||||||
|
type,
|
||||||
|
Arrays.stream(args)
|
||||||
|
.map(o -> o.getClass() == Output.class ? o : new Output<>(() -> o))
|
||||||
|
.toArray(Output<?>[]::new));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Constructor with named parameters (like in the class Defaults)
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Input<?>> inputs() {
|
||||||
|
return inputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void buildItUp() {
|
||||||
|
object = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public T object() {
|
||||||
|
if (object == null) {
|
||||||
|
Object[] args = new Object[nParameters];
|
||||||
|
for (int i = 0; i < nParameters; i++) {
|
||||||
|
args[i] = inputs.get(i).get();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
object = constructor.newInstance(args);
|
||||||
|
} catch (InstantiationException | InvocationTargetException | IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
5
src/xyz/marsavic/gfxlab/elements/HasOutput.java
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
package xyz.marsavic.gfxlab.elements;
|
||||||
|
|
||||||
|
public interface HasOutput<R> {
|
||||||
|
Output<R> out();
|
||||||
|
}
|
13
src/xyz/marsavic/gfxlab/elements/Immutable.java
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package xyz.marsavic.gfxlab.elements;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
|
/** A class annotation with this is "purely functional". This means that it effectively does not have any modifiable
|
||||||
|
* state, and only provides methods whose result is a function only of its parameters and constructor parameters.
|
||||||
|
* - We usually want all these parameters to also be pure or immutable.
|
||||||
|
* - We allow a method to write to a buffer passed as a parameter, that is considered to be "a result" of the function.
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface Immutable {
|
||||||
|
}
|
25
src/xyz/marsavic/gfxlab/elements/Invalidatable.java
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
package xyz.marsavic.gfxlab.elements;
|
||||||
|
|
||||||
|
import xyz.marsavic.reactions.Dispatcher;
|
||||||
|
import xyz.marsavic.reactions.Reactions;
|
||||||
|
import xyz.marsavic.reactions.Reactive;
|
||||||
|
import xyz.marsavic.reactions.values.EventInvalidated;
|
||||||
|
|
||||||
|
|
||||||
|
public interface Invalidatable extends Reactive {
|
||||||
|
Reactions<EventInvalidated> onInvalidated();
|
||||||
|
|
||||||
|
|
||||||
|
class Base {
|
||||||
|
protected final Dispatcher<EventInvalidated> dispatcherInvalidated = new Dispatcher<>();
|
||||||
|
|
||||||
|
public Reactions<EventInvalidated> onInvalidated() {
|
||||||
|
return dispatcherInvalidated.reactions();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void fireInvalidated() {
|
||||||
|
dispatcherInvalidated.fire(new EventInvalidated());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
10
src/xyz/marsavic/gfxlab/elements/Motor.java
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
package xyz.marsavic.gfxlab.elements;
|
||||||
|
|
||||||
|
|
||||||
|
public abstract class Motor {
|
||||||
|
|
||||||
|
abstract void start();
|
||||||
|
abstract void pause();
|
||||||
|
abstract void stop();
|
||||||
|
|
||||||
|
}
|
32
src/xyz/marsavic/gfxlab/elements/Output.java
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
package xyz.marsavic.gfxlab.elements;
|
||||||
|
|
||||||
|
import xyz.marsavic.functions.interfaces.F0;
|
||||||
|
|
||||||
|
|
||||||
|
public class Output<T> extends Invalidatable.Base implements HasOutput<T> {
|
||||||
|
|
||||||
|
private final F0<T> resultProvider;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public Output(F0<T> resultProvider) {
|
||||||
|
this.resultProvider = resultProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public T get() {
|
||||||
|
return resultProvider.at();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Output<T> out() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static <T> Output<T> val(T object) {
|
||||||
|
return new Output<>(() -> object);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
21
src/xyz/marsavic/gfxlab/elements/UtilsElements.java
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
package xyz.marsavic.gfxlab.elements;
|
||||||
|
|
||||||
|
import com.google.common.primitives.Primitives;
|
||||||
|
|
||||||
|
// TODO not needed - remove
|
||||||
|
public class UtilsElements {
|
||||||
|
|
||||||
|
@SuppressWarnings("RedundantIfStatement")
|
||||||
|
static boolean looksImmutable(Class<?> c) {
|
||||||
|
if (c.isRecord()) return true;
|
||||||
|
if (c.isAnnotationPresent(Immutable.class)) return true;
|
||||||
|
if (Primitives.isWrapperType(c)) return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean looksImmutable(Object o) {
|
||||||
|
return looksImmutable(o.getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
198
src/xyz/marsavic/gfxlab/graphics3d/Affine.java
Normal file
|
@ -0,0 +1,198 @@
|
||||||
|
package xyz.marsavic.gfxlab.graphics3d;
|
||||||
|
|
||||||
|
|
||||||
|
import xyz.marsavic.gfxlab.Transformation;
|
||||||
|
import xyz.marsavic.gfxlab.Vec3;
|
||||||
|
import xyz.marsavic.gfxlab.elements.Immutable;
|
||||||
|
import xyz.marsavic.utils.Numeric;
|
||||||
|
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
public record Affine (
|
||||||
|
double m00, double m01, double m02, double m03,
|
||||||
|
double m10, double m11, double m12, double m13,
|
||||||
|
double m20, double m21, double m22, double m23
|
||||||
|
// 0, 0, 0, 1
|
||||||
|
) implements Transformation {
|
||||||
|
|
||||||
|
public static Affine IDENTITY = new Affine(
|
||||||
|
1.0, 0.0, 0.0, 0.0,
|
||||||
|
0.0, 1.0, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 1.0, 0.0
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
public static Affine unitVectors(Vec3 ex, Vec3 ey, Vec3 ez) {
|
||||||
|
return new Affine(
|
||||||
|
ex.x(), ey.x(), ez.x(), 0,
|
||||||
|
ex.y(), ey.y(), ez.y(), 0,
|
||||||
|
ex.z(), ey.z(), ez.z(), 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Affine then(Affine t) {
|
||||||
|
return new Affine(
|
||||||
|
t.m00 * m00 + t.m01 * m10 + t.m02 * m20, t.m00 * m01 + t.m01 * m11 + t.m02 * m21, t.m00 * m02 + t.m01 * m12 + t.m02 * m22, t.m00 * m03 + t.m01 * m13 + t.m02 * m23 + t.m03,
|
||||||
|
t.m10 * m00 + t.m11 * m10 + t.m12 * m20, t.m10 * m01 + t.m11 * m11 + t.m12 * m21, t.m10 * m02 + t.m11 * m12 + t.m12 * m22, t.m10 * m03 + t.m11 * m13 + t.m12 * m23 + t.m13,
|
||||||
|
t.m20 * m00 + t.m21 * m10 + t.m22 * m20, t.m20 * m01 + t.m21 * m11 + t.m22 * m21, t.m20 * m02 + t.m21 * m12 + t.m22 * m22, t.m20 * m03 + t.m21 * m13 + t.m22 * m23 + t.m23
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Vec3 at(Vec3 v) {
|
||||||
|
return new Vec3(
|
||||||
|
m00 * v.x() + m01 * v.y() + m02 * v.z() + m03,
|
||||||
|
m10 * v.x() + m11 * v.y() + m12 * v.z() + m13,
|
||||||
|
m20 * v.x() + m21 * v.y() + m22 * v.z() + m23
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Vec3 applyWithoutTranslationTo(Vec3 v) {
|
||||||
|
return new Vec3(
|
||||||
|
m00 * v.x() + m01 * v.y() + m02 * v.z(),
|
||||||
|
m10 * v.x() + m11 * v.y() + m12 * v.z(),
|
||||||
|
m20 * v.x() + m21 * v.y() + m22 * v.z()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Ray at(Ray r) {
|
||||||
|
return new Ray(at(r.p()), applyWithoutTranslationTo(r.d()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Affine inverse() {
|
||||||
|
double det = determinant();
|
||||||
|
return new Affine(
|
||||||
|
(m11 * m22 - m12 * m21) / det, -(m01 * m22 - m02 * m21) / det, (m01 * m12 - m02 * m11) / det, -(m01 * m12 * m23 + m02 * m13 * m21 + m03 * m11 * m22 - m03 * m12 * m21 - m02 * m11 * m23 - m01 * m13 * m22) / det,
|
||||||
|
-(m10 * m22 - m12 * m20) / det, (m00 * m22 - m02 * m20) / det, -(m00 * m12 - m02 * m10) / det, (m00 * m12 * m23 + m02 * m13 * m20 + m03 * m10 * m22 - m03 * m12 * m20 - m02 * m10 * m23 - m00 * m13 * m22) / det,
|
||||||
|
(m10 * m21 - m11 * m20) / det, -(m00 * m21 - m01 * m20) / det, (m00 * m11 - m01 * m10) / det, -(m00 * m11 * m23 + m01 * m13 * m20 + m03 * m10 * m21 - m03 * m11 * m20 - m01 * m10 * m23 - m00 * m13 * m21) / det
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Affine transposeWithoutTranslation() {
|
||||||
|
return new Affine(
|
||||||
|
m00, m10, m20, 0,
|
||||||
|
m01, m11, m21, 0,
|
||||||
|
m02, m12, m22, 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private double determinant() {
|
||||||
|
//noinspection UnaryPlus
|
||||||
|
return (
|
||||||
|
+ (m00 * m11 * m22)
|
||||||
|
+ (m01 * m12 * m20)
|
||||||
|
+ (m02 * m10 * m21)
|
||||||
|
- (m02 * m11 * m20)
|
||||||
|
- (m01 * m10 * m22)
|
||||||
|
- (m00 * m12 * m21)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Affine translation(Vec3 d) {
|
||||||
|
return new Affine(
|
||||||
|
1.0, 0.0, 0.0, d.x(),
|
||||||
|
0.0, 1.0, 0.0, d.y(),
|
||||||
|
0.0, 0.0, 1.0, d.z()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Affine rotationAboutX(double angle) {
|
||||||
|
return new Affine(
|
||||||
|
1.0, 0.0, 0.0, 0.0,
|
||||||
|
0.0, Numeric.cosT(angle), -Numeric.sinT(angle), 0.0,
|
||||||
|
0.0, Numeric.sinT(angle), Numeric.cosT(angle), 0.0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Affine rotationAboutY(double angle) {
|
||||||
|
return new Affine(
|
||||||
|
Numeric.cosT(angle), 0.0, Numeric.sinT(angle), 0.0,
|
||||||
|
0.0, 1.0, 0.0, 0.0,
|
||||||
|
-Numeric.sinT(angle), 0.0, Numeric.cosT(angle), 0.0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Affine rotationAboutZ(double angle) {
|
||||||
|
return new Affine(
|
||||||
|
Numeric.cosT(angle), -Numeric.sinT(angle), 0.0, 0.0,
|
||||||
|
Numeric.sinT(angle), Numeric.cosT(angle), 0.0, 0.0,
|
||||||
|
0.0, 0.0, 1.0, 0.0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Affine scaling(double c) {
|
||||||
|
return new Affine(
|
||||||
|
c, 0.0, 0.0, 0.0,
|
||||||
|
0.0, c, 0.0, 0.0,
|
||||||
|
0.0, 0.0, c, 0.0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Affine scaling(Vec3 c) {
|
||||||
|
return new Affine(
|
||||||
|
c.x(), 0.0, 0.0, 0.0,
|
||||||
|
0.0, c.y(), 0.0, 0.0,
|
||||||
|
0.0, 0.0, c.z(), 0.0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* One possible linear transformation mapping EX to u and preserving angles between vectors.
|
||||||
|
*/
|
||||||
|
public static Affine asEX(Vec3 u) {
|
||||||
|
// A simpler implementation using vector products is possible, but this is more efficient.
|
||||||
|
if (u.x() != 0 || u.y() != 0) {
|
||||||
|
double mSqr = u.x() * u.x() + u.y() * u.y();
|
||||||
|
double lSqr = mSqr + u.z() * u.z();
|
||||||
|
double l = Math.sqrt(lSqr);
|
||||||
|
double m = 1.0 / Math.sqrt(mSqr);
|
||||||
|
double q = l * m;
|
||||||
|
|
||||||
|
return Affine.unitVectors(
|
||||||
|
u,
|
||||||
|
Vec3.xyz(-u.y() * q, u.x() * q, 0),
|
||||||
|
Vec3.xyz(-u.x() * u.z() * m, -u.z() * u.y() * m, (u.x() * u.x() + u.y() * u.y()) * m)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
double q = 1.0 / u.z();
|
||||||
|
return Affine.unitVectors(
|
||||||
|
u,
|
||||||
|
Vec3.xyz(q, 0, 0),
|
||||||
|
Vec3.xyz(0, Math.abs(q), 0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* One possible linear transformation mapping EX to a vector u of unit length and preserving angles between vectors.
|
||||||
|
*/
|
||||||
|
public static Affine asEXN(Vec3 u_) {
|
||||||
|
if (u_.x() != 0 || u_.y() != 0) {
|
||||||
|
double mSqr = 1 - u_.z() * u_.z();
|
||||||
|
double m = 1.0 / Math.sqrt(mSqr);
|
||||||
|
|
||||||
|
return Affine.unitVectors(
|
||||||
|
u_,
|
||||||
|
Vec3.xyz(-u_.y() * m, u_.x() * m, 0),
|
||||||
|
Vec3.xyz(-u_.x() * u_.z() * m, -u_.z() * u_.y() * m, (u_.x() * u_.x() + u_.y() * u_.y()) * m)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Affine.unitVectors(
|
||||||
|
u_,
|
||||||
|
Vec3.xyz(u_.z(), 0, 0),
|
||||||
|
Vec3.xyz(0, Math.abs(u_.z()), 0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
29
src/xyz/marsavic/gfxlab/graphics3d/GeometryUtils.java
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
package xyz.marsavic.gfxlab.graphics3d;
|
||||||
|
|
||||||
|
|
||||||
|
import xyz.marsavic.gfxlab.Vec3;
|
||||||
|
import xyz.marsavic.random.sampling.Sampler;
|
||||||
|
import xyz.marsavic.utils.Numeric;
|
||||||
|
|
||||||
|
public class GeometryUtils {
|
||||||
|
|
||||||
|
/** An orthogonal vector to v. */
|
||||||
|
public static Vec3 normal(Vec3 v) {
|
||||||
|
if (v.x() != 0 || v.y() != 0) {
|
||||||
|
return Vec3.xyz(-v.y(), v.x(), 0);
|
||||||
|
} else {
|
||||||
|
return Vec3.EX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
public static Vec3 normal(Vec3 v) {
|
||||||
|
Vec3 p = v.cross(Vec3.EX);
|
||||||
|
if (p.allZero()) {
|
||||||
|
p = v.cross(Vec3.EY);
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
49
src/xyz/marsavic/gfxlab/graphics3d/Hit.java
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
package xyz.marsavic.gfxlab.graphics3d;
|
||||||
|
|
||||||
|
import xyz.marsavic.geometry.Vector;
|
||||||
|
import xyz.marsavic.gfxlab.Vec3;
|
||||||
|
|
||||||
|
|
||||||
|
/** Interaction of a ray with a solid.*/
|
||||||
|
public interface Hit {
|
||||||
|
|
||||||
|
/** The time of the hit */
|
||||||
|
double t();
|
||||||
|
|
||||||
|
/** The normal at the point of the hit */
|
||||||
|
Vec3 n();
|
||||||
|
|
||||||
|
/** The normalized normal at the point of the hit */
|
||||||
|
default Vec3 n_() {
|
||||||
|
return n().normalized_();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// =====================================================================================================
|
||||||
|
|
||||||
|
|
||||||
|
static double t(Hit hit) {
|
||||||
|
return hit == null ? Double.POSITIVE_INFINITY : hit.t();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
abstract class RayT implements Hit {
|
||||||
|
private final Ray ray;
|
||||||
|
private final double t;
|
||||||
|
|
||||||
|
protected RayT(Ray ray, double t) {
|
||||||
|
this.ray = ray;
|
||||||
|
this.t = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Ray ray() {
|
||||||
|
return ray;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double t() {
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
33
src/xyz/marsavic/gfxlab/graphics3d/Ray.java
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
package xyz.marsavic.gfxlab.graphics3d;
|
||||||
|
|
||||||
|
import xyz.marsavic.gfxlab.Vec3;
|
||||||
|
|
||||||
|
|
||||||
|
public record Ray(Vec3 p, Vec3 d) {
|
||||||
|
|
||||||
|
|
||||||
|
public static Ray pd(Vec3 p, Vec3 d) {
|
||||||
|
return new Ray(p, d);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Ray pq(Vec3 p, Vec3 q) {
|
||||||
|
return pd(p, q.sub(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Vec3 at(double t) {
|
||||||
|
return p.add(d.mul(t));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Ray moveTo(double t) {
|
||||||
|
return Ray.pd(at(t), d());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Ray normalized_() {
|
||||||
|
return Ray.pd(p(), d().normalized_());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
22
src/xyz/marsavic/gfxlab/graphics3d/Solid.java
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
package xyz.marsavic.gfxlab.graphics3d;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
public interface Solid {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the first hit of the ray into the surface of the solid, occurring strictly after the given time.
|
||||||
|
* The default implementation is based on hits method, but implementations of Solid can choose to override
|
||||||
|
* this method to increase performance when only the first hit is needed.
|
||||||
|
* If there is no hit, returns null.
|
||||||
|
*/
|
||||||
|
Hit firstHit(Ray ray, double afterTime);
|
||||||
|
|
||||||
|
|
||||||
|
default Hit firstHit(Ray ray) {
|
||||||
|
return firstHit(ray, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package xyz.marsavic.gfxlab.graphics3d.raytracers;
|
||||||
|
|
||||||
|
import xyz.marsavic.geometry.Vector;
|
||||||
|
import xyz.marsavic.gfxlab.Color;
|
||||||
|
import xyz.marsavic.gfxlab.ColorFunctionT;
|
||||||
|
import xyz.marsavic.gfxlab.Vec3;
|
||||||
|
import xyz.marsavic.gfxlab.graphics3d.Hit;
|
||||||
|
import xyz.marsavic.gfxlab.graphics3d.Ray;
|
||||||
|
import xyz.marsavic.gfxlab.graphics3d.solids.Ball;
|
||||||
|
import xyz.marsavic.gfxlab.graphics3d.solids.HalfSpace;
|
||||||
|
|
||||||
|
|
||||||
|
public class RayTracingTest implements ColorFunctionT {
|
||||||
|
|
||||||
|
Ball ball = Ball.cr(Vec3.xyz(0, 0, 2), 1);
|
||||||
|
HalfSpace halfSpace = HalfSpace.pn(Vec3.xyz(0, -1, 0), Vec3.xyz(0, 1, 0));
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Color at(double t, Vector p) {
|
||||||
|
Ray ray = Ray.pq(Vec3.ZERO, Vec3.zp(1, p));
|
||||||
|
|
||||||
|
Hit hit1 = ball.firstHit(ray);
|
||||||
|
Hit hit2 = halfSpace.firstHit(ray);
|
||||||
|
|
||||||
|
double tMin = Math.min(Hit.t(hit1), Hit.t(hit2));
|
||||||
|
|
||||||
|
return Color.gray(1.0 / (1.0 + tMin));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
79
src/xyz/marsavic/gfxlab/graphics3d/solids/Ball.java
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
package xyz.marsavic.gfxlab.graphics3d.solids;
|
||||||
|
|
||||||
|
import xyz.marsavic.geometry.Vector;
|
||||||
|
import xyz.marsavic.gfxlab.Vec3;
|
||||||
|
import xyz.marsavic.gfxlab.graphics3d.Hit;
|
||||||
|
import xyz.marsavic.gfxlab.graphics3d.Ray;
|
||||||
|
import xyz.marsavic.gfxlab.graphics3d.Solid;
|
||||||
|
import xyz.marsavic.utils.Numeric;
|
||||||
|
|
||||||
|
|
||||||
|
public class Ball implements Solid {
|
||||||
|
|
||||||
|
private final Vec3 c;
|
||||||
|
private final double r;
|
||||||
|
|
||||||
|
// transient
|
||||||
|
private final double rSqr;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private Ball(Vec3 c, double r) {
|
||||||
|
this.c = c;
|
||||||
|
this.r = r;
|
||||||
|
rSqr = r * r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Ball cr(Vec3 c, double r) {
|
||||||
|
return new Ball(c, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Vec3 c() {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public double r() {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HitBall firstHit(Ray ray, double afterTime) {
|
||||||
|
Vec3 e = c().sub(ray.p()); // Vector from the ray origin to the ball center
|
||||||
|
|
||||||
|
double dSqr = ray.d().lengthSquared();
|
||||||
|
double l = e.dot(ray.d()) / dSqr;
|
||||||
|
double mSqr = l * l - (e.lengthSquared() - rSqr) / dSqr;
|
||||||
|
|
||||||
|
if (mSqr > 0) {
|
||||||
|
double m = Math.sqrt(mSqr);
|
||||||
|
if (l - m > afterTime) return new HitBall(ray, l - m);
|
||||||
|
if (l + m > afterTime) return new HitBall(ray, l + m);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class HitBall extends Hit.RayT {
|
||||||
|
|
||||||
|
protected HitBall(Ray ray, double t) {
|
||||||
|
super(ray, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Vec3 n() {
|
||||||
|
return ray().at(t()).sub(c());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Vec3 n_() {
|
||||||
|
return n().div(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
123
src/xyz/marsavic/gfxlab/graphics3d/solids/HalfSpace.java
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
package xyz.marsavic.gfxlab.graphics3d.solids;
|
||||||
|
|
||||||
|
import xyz.marsavic.gfxlab.Vec3;
|
||||||
|
import xyz.marsavic.gfxlab.graphics3d.GeometryUtils;
|
||||||
|
import xyz.marsavic.gfxlab.graphics3d.Hit;
|
||||||
|
import xyz.marsavic.gfxlab.graphics3d.Ray;
|
||||||
|
import xyz.marsavic.gfxlab.graphics3d.Solid;
|
||||||
|
|
||||||
|
|
||||||
|
public class HalfSpace implements Solid {
|
||||||
|
|
||||||
|
private final Vec3 p; // A point on the boundary plane
|
||||||
|
private final Vec3 e; // A vector parallel to the boundary plane.
|
||||||
|
private final Vec3 f; // A vector parallel to the boundary plane, not parallel to e.
|
||||||
|
|
||||||
|
// transient
|
||||||
|
private final Vec3 n; // A normal vector to the boundary plane
|
||||||
|
private final Vec3 n_; // A normalized normal vector to the boundary plane
|
||||||
|
private final double e_f, f_e, eLSqr, fLSqr, sinSqr;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private HalfSpace(Vec3 p, Vec3 e, Vec3 f) {
|
||||||
|
this.p = p;
|
||||||
|
this.e = e;
|
||||||
|
this.f = f;
|
||||||
|
this.n = e.cross(f);
|
||||||
|
|
||||||
|
n_ = n.normalized_();
|
||||||
|
|
||||||
|
eLSqr = e.lengthSquared();
|
||||||
|
fLSqr = f.lengthSquared();
|
||||||
|
double ef = e.dot(f);
|
||||||
|
e_f = ef / fLSqr;
|
||||||
|
f_e = ef / eLSqr;
|
||||||
|
sinSqr = 1 - e_f * f_e;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static HalfSpace pef(Vec3 p, Vec3 e, Vec3 f) {
|
||||||
|
return new HalfSpace(p, e, f);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static HalfSpace pqr(Vec3 p, Vec3 q, Vec3 r) {
|
||||||
|
return pef(p, q.sub(p), r.sub(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static HalfSpace pn(Vec3 p, Vec3 n) {
|
||||||
|
double nl = n.length();
|
||||||
|
Vec3 e = GeometryUtils.normal(n).normalizedTo(nl);
|
||||||
|
Vec3 f = n.cross(e).normalizedTo(nl);
|
||||||
|
return new HalfSpace(p, e, f);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Vec3 p() {
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Vec3 e() {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Vec3 f() {
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Vec3 n() {
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Vec3 n_() {
|
||||||
|
return n_;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Hit firstHit(Ray ray, double afterTime) {
|
||||||
|
double o = n().dot(ray.d());
|
||||||
|
if (o == 0) return null;
|
||||||
|
double l = n().dot(p().sub(ray.p()));
|
||||||
|
double t = l / o;
|
||||||
|
return (t > afterTime) ? new HitHalfSpace(ray, t) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "HalfSpace{" +
|
||||||
|
"p=" + p +
|
||||||
|
", e=" + e +
|
||||||
|
", f=" + f +
|
||||||
|
", n=" + n +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class HitHalfSpace extends Hit.RayT {
|
||||||
|
|
||||||
|
protected HitHalfSpace(Ray ray, double t) {
|
||||||
|
super(ray, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Vec3 n() {
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Vec3 n_() {
|
||||||
|
return n_;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
102
src/xyz/marsavic/gfxlab/gui/App.java
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
package xyz.marsavic.gfxlab.gui;
|
||||||
|
|
||||||
|
import javafx.application.Application;
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.scene.Scene;
|
||||||
|
import javafx.scene.input.KeyCode;
|
||||||
|
import javafx.scene.input.KeyEvent;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.Pane;
|
||||||
|
import javafx.scene.layout.Priority;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
import xyz.marsavic.gfxlab.gui.instruments.InstrumentRenderer;
|
||||||
|
import xyz.marsavic.gfxlab.playground.GfxLab;
|
||||||
|
import xyz.marsavic.gfxlab.resources.Resources;
|
||||||
|
import xyz.marsavic.objectinstruments.Context;
|
||||||
|
import xyz.marsavic.objectinstruments.Panel;
|
||||||
|
import xyz.marsavic.objectinstruments.instruments.InstrumentText;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
public class App extends Application {
|
||||||
|
|
||||||
|
private final GfxLab gfxLab = new GfxLab();
|
||||||
|
|
||||||
|
// private final ReactiveSet<Object> specialObjects = ReactiveSetBacked.withLinkedHashSet();
|
||||||
|
|
||||||
|
|
||||||
|
private final List<Object> instrumentedAtStart = List.of(
|
||||||
|
// Catalog.INSTANCE,
|
||||||
|
gfxLab
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start(Stage primaryStage) {
|
||||||
|
primaryStage.setTitle("GFX Lab");
|
||||||
|
|
||||||
|
Panel panelL = new Panel();
|
||||||
|
// Context contextL = Context.defaultForObjectSet(new ReactiveSetUnion<>(specialObjects, panelL.objectSet()));
|
||||||
|
Context contextL = Context.defaultForPanel(panelL);
|
||||||
|
for (var object : instrumentedAtStart) {
|
||||||
|
// panelL.addInstrument(InstrumentObject.of(object, f -> gfxLab.onChange(), contextL));
|
||||||
|
}
|
||||||
|
|
||||||
|
Panel panelR = new Panel();
|
||||||
|
|
||||||
|
panelR.addInstruments(
|
||||||
|
// new PollingInstrument<>(new InstrumentAnimationRawImageNew(), gfxLab::toneMappedAnimation),
|
||||||
|
new InstrumentRenderer(gfxLab.outRenderer),
|
||||||
|
new InstrumentText(() -> Profiling.infoTextSystem() + Profiling.infoTextProfilers(), 150)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
ReactiveObjectSet<Object> objectSet = panel.objectSet();
|
||||||
|
|
||||||
|
List<Tuple2<Class<?>, Function0<Factory<?>>>> factoryCreators =
|
||||||
|
new ArrayList<>(Factory.defaultFactoryCreatorsList);
|
||||||
|
|
||||||
|
factoryCreators.add(
|
||||||
|
new Tuple2<>(RawImage.class, FactoryBoolean::new)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
Context context = new Context(
|
||||||
|
objectSet,
|
||||||
|
TypeMap.concat(
|
||||||
|
TypeMap.firstMatch(factoryCreators),
|
||||||
|
TypeMap.f(type -> () -> new FactoryReference<>(objectSet.ofType(type)))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// ScrollPane sp = new ScrollPane(panelL.region());
|
||||||
|
|
||||||
|
// sp.setHbarPolicy(ScrollPane.ScrollBarPolicy.ALWAYS);
|
||||||
|
// sp.setFitToWidth(true);
|
||||||
|
// sp.setFitToHeight(true);
|
||||||
|
// sp.setPannable(true);
|
||||||
|
|
||||||
|
Pane root = new HBox(panelL.region(), panelR.region());
|
||||||
|
HBox.setHgrow(panelL.region(), Priority.ALWAYS);
|
||||||
|
|
||||||
|
Scene scene = new Scene(root, 1066, 866);
|
||||||
|
|
||||||
|
scene.getStylesheets().setAll(Resources.stylesheetURL);
|
||||||
|
|
||||||
|
primaryStage.getIcons().setAll(Resources.iconsApplication());
|
||||||
|
primaryStage.setScene(scene);
|
||||||
|
// primaryStage.size.setMaximized(true);
|
||||||
|
|
||||||
|
primaryStage.addEventHandler(KeyEvent.KEY_PRESSED, event -> {
|
||||||
|
if (event.getCode().equals(KeyCode.ESCAPE)) {
|
||||||
|
Platform.exit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
primaryStage.show();
|
||||||
|
}
|
||||||
|
}
|
90
src/xyz/marsavic/gfxlab/gui/Profiling.java
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
package xyz.marsavic.gfxlab.gui;
|
||||||
|
|
||||||
|
import xyz.marsavic.time.Profiler;
|
||||||
|
import xyz.marsavic.tuples.Tuple2;
|
||||||
|
|
||||||
|
import javax.management.*;
|
||||||
|
import java.lang.management.ManagementFactory;
|
||||||
|
import java.lang.management.MemoryMXBean;
|
||||||
|
import java.lang.management.MemoryUsage;
|
||||||
|
import java.lang.management.OperatingSystemMXBean;
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
|
|
||||||
|
public class Profiling {
|
||||||
|
|
||||||
|
private static final MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
|
||||||
|
|
||||||
|
private static final OperatingSystemMXBean operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean();
|
||||||
|
private static final int nCpus = operatingSystemMXBean.getAvailableProcessors();
|
||||||
|
|
||||||
|
private static final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
|
||||||
|
private static ObjectName name;
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
name = ObjectName.getInstance("java.lang:type=OperatingSystem");
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double lastValue = Double.NaN;
|
||||||
|
private static double nextTime = 0.0;
|
||||||
|
private static final double cpuQueryInterval = 1.0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private static double getProcessCpuLoad() {
|
||||||
|
double time = Profiler.timerDefault.getTime();
|
||||||
|
if (time < nextTime) {
|
||||||
|
return lastValue;
|
||||||
|
}
|
||||||
|
nextTime = time + cpuQueryInterval;
|
||||||
|
try {
|
||||||
|
Attribute attribute = (Attribute) mbs.getAttributes(name, new String[] {"ProcessCpuLoad"}).get(0);
|
||||||
|
return lastValue = (Double) attribute.getValue();
|
||||||
|
} catch (InstanceNotFoundException | ReflectionException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return Double.NaN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String infoTextSystem() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
MemoryUsage heapMemoryUsage = memoryMXBean.getHeapMemoryUsage();
|
||||||
|
|
||||||
|
double conversion = 1024*1024;
|
||||||
|
String unit = "MB";
|
||||||
|
sb.append(String.format("Heap used : %8.2f %s\n", heapMemoryUsage.getUsed () / conversion, unit));
|
||||||
|
sb.append(String.format("Heap max : %8.2f %s\n", heapMemoryUsage.getMax () / conversion, unit));
|
||||||
|
sb.append(String.format("Memory committed: %8.2f %s\n", heapMemoryUsage.getCommitted() / conversion, unit));
|
||||||
|
|
||||||
|
sb.append(String.format("Parallelism : %d/%d\n", UtilsGL.parallelism, nCpus));
|
||||||
|
sb.append(String.format("CPU load average: %.2f\n", getProcessCpuLoad()));
|
||||||
|
|
||||||
|
sb.append("\n");
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String infoTextProfilers() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
// Profilers change, can't sort them while running, so...
|
||||||
|
UtilsGL.profilers().stream()
|
||||||
|
.map(p -> new Tuple2<>(p, p.durationPerSecond()))
|
||||||
|
.sorted(Comparator.comparingDouble(t -> -t.p1()))
|
||||||
|
.forEach(t -> {
|
||||||
|
sb.append(t.p0().toStringDetailed());
|
||||||
|
sb.append("\n");
|
||||||
|
});
|
||||||
|
|
||||||
|
sb.append("\n");
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
224
src/xyz/marsavic/gfxlab/gui/UtilsGL.java
Normal file
|
@ -0,0 +1,224 @@
|
||||||
|
package xyz.marsavic.gfxlab.gui;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import javafx.embed.swing.SwingFXUtils;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.image.PixelFormat;
|
||||||
|
import javafx.scene.image.WritableImage;
|
||||||
|
import javafx.scene.input.Clipboard;
|
||||||
|
import javafx.scene.input.ClipboardContent;
|
||||||
|
import javafx.stage.FileChooser;
|
||||||
|
import xyz.marsavic.functions.interfaces.A1;
|
||||||
|
import xyz.marsavic.functions.interfaces.A2;
|
||||||
|
import xyz.marsavic.geometry.Vector;
|
||||||
|
import xyz.marsavic.gfxlab.Color;
|
||||||
|
import xyz.marsavic.gfxlab.Matrix;
|
||||||
|
import xyz.marsavic.gfxlab.MatrixData;
|
||||||
|
import xyz.marsavic.gfxlab.MatrixInts;
|
||||||
|
import xyz.marsavic.resources.ResourceManagerMap;
|
||||||
|
import xyz.marsavic.random.RNG;
|
||||||
|
import xyz.marsavic.time.Profiler;
|
||||||
|
import xyz.marsavic.utils.Utils;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.IntBuffer;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.WeakHashMap;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.ForkJoinPool;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.function.IntConsumer;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
|
|
||||||
|
public class UtilsGL {
|
||||||
|
|
||||||
|
private static final Set<Profiler> profilers = Collections.synchronizedSet(Collections.newSetFromMap(new WeakHashMap<>()));
|
||||||
|
|
||||||
|
|
||||||
|
public static Profiler profiler(Object object, String description) {
|
||||||
|
String name =
|
||||||
|
String.format("%08x", System.identityHashCode(object)) +
|
||||||
|
" " +
|
||||||
|
object.getClass().getSimpleName() +
|
||||||
|
" " +
|
||||||
|
description;
|
||||||
|
|
||||||
|
Profiler profiler = new Profiler(name);
|
||||||
|
profilers.add(profiler);
|
||||||
|
return profiler;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Live profilers, but not a live collection.
|
||||||
|
* The returned collection is immutable and contains only the profilers present at the moment of calling.
|
||||||
|
*/
|
||||||
|
public static Collection<Profiler> profilers() {
|
||||||
|
synchronized (profilers) {
|
||||||
|
return ImmutableList.copyOf(profilers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// JavaFX helpers
|
||||||
|
|
||||||
|
static final PixelFormat<IntBuffer> pixelFormat = PixelFormat.getIntArgbPreInstance();
|
||||||
|
|
||||||
|
|
||||||
|
public static Image writeRawImageToImage(MatrixInts rawImage) {
|
||||||
|
int sx = rawImage.width();
|
||||||
|
int sy = rawImage.height();
|
||||||
|
WritableImage image = new WritableImage(sx, sy);
|
||||||
|
writeRawImageToImage(image, rawImage);
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void writeRawImageToImage(WritableImage outImage, Matrix<Integer> inRawImage) {
|
||||||
|
int sx = inRawImage.size().xInt();
|
||||||
|
int sy = inRawImage.size().yInt();
|
||||||
|
|
||||||
|
if (inRawImage instanceof MatrixInts matrixInts) {
|
||||||
|
outImage.getPixelWriter().setPixels(0, 0, sx, sy, pixelFormat, matrixInts.array(), 0, sx);
|
||||||
|
} else {
|
||||||
|
// TODO
|
||||||
|
throw new UnsupportedOperationException("To be implemented...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Vector imageSize(Image image) {
|
||||||
|
return Vector.xy(image.getWidth(), image.getHeight());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static WritableImage createWritableImage(Vector size) {
|
||||||
|
return new WritableImage(size.xInt(), size.yInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void saveImageToFileWithDialog(Image image) {
|
||||||
|
FileChooser fileChooser = new FileChooser();
|
||||||
|
fileChooser.setTitle("Export Image");
|
||||||
|
fileChooser.setInitialDirectory(new File("."));
|
||||||
|
fileChooser.getExtensionFilters().addAll(
|
||||||
|
new FileChooser.ExtensionFilter("Image Files", "*.png"),
|
||||||
|
new FileChooser.ExtensionFilter("All Files", "*.*")
|
||||||
|
);
|
||||||
|
File file = fileChooser.showSaveDialog(null);
|
||||||
|
|
||||||
|
if (file != null) {
|
||||||
|
try {
|
||||||
|
ImageIO.write(SwingFXUtils.fromFXImage(image, null), "png", file);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void copyImageToClipboard(Image image) {
|
||||||
|
ClipboardContent content = new ClipboardContent();
|
||||||
|
content.putImage(image);
|
||||||
|
Clipboard.getSystemClipboard().setContent(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --- Parallel utils -------
|
||||||
|
|
||||||
|
|
||||||
|
public static final ExecutorService pool;
|
||||||
|
public static final int parallelism;
|
||||||
|
|
||||||
|
static {
|
||||||
|
int p = ForkJoinPool.getCommonPoolParallelism();
|
||||||
|
try {
|
||||||
|
boolean obsRunning = false;
|
||||||
|
obsRunning |= ProcessHandle.allProcesses().anyMatch(ph -> ph.info().command().orElse("").contains("obs64")); // Windows
|
||||||
|
obsRunning |= !Utils.runCommand("top -b -n 1 | grep \" obs\"").isEmpty(); // Linux
|
||||||
|
obsRunning |= true;
|
||||||
|
if (obsRunning) {
|
||||||
|
p -= 3;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
parallelism = p;
|
||||||
|
pool = new ForkJoinPool(parallelism);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void parallel(int iFrom, int iTo, IntConsumer action) {
|
||||||
|
Future<?> f = pool.submit(() -> {
|
||||||
|
try {
|
||||||
|
IntStream.range(iFrom, iTo).parallel().forEach(action);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
f.get();
|
||||||
|
break;
|
||||||
|
} catch (InterruptedException | ExecutionException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void parallel(int iTo, IntConsumer action) {
|
||||||
|
parallel(0, iTo, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void parallelY(Vector size, IntConsumer action) {
|
||||||
|
parallel(size.yInt(), action);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void parallelY(Vector size, A2<Integer, Integer> action) {
|
||||||
|
parallelY(size, y -> {
|
||||||
|
for (int x = 0; x < size.xInt(); x++) {
|
||||||
|
action.execute(x, y);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void parallel(Vector size, A1<Vector> action) {
|
||||||
|
parallelY(size, y -> {
|
||||||
|
for (int x = 0; x < size.xInt(); x++) {
|
||||||
|
action.execute(Vector.xy(x, y));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static RNG[] rngArray(int n, long seed) {
|
||||||
|
RNG seeds = new RNG(seed);
|
||||||
|
RNG[] rngs = new RNG[n];
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
rngs[i] = new RNG(seeds.nextLong());
|
||||||
|
}
|
||||||
|
return rngs;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static RNG[] rngArray(int n) {
|
||||||
|
return rngArray(n, new RNG().nextLong());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static final ResourceManagerMap<Vector, Matrix<Color>> matricesColor = new ResourceManagerMap<>(
|
||||||
|
MatrixData::new, (m, sz) -> m.fill(Color.BLACK)
|
||||||
|
);
|
||||||
|
|
||||||
|
public static final ResourceManagerMap<Vector, Matrix<Integer>> matricesInt = new ResourceManagerMap<>(
|
||||||
|
MatrixInts::new, (m, sz) -> m.fill(0)
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
package xyz.marsavic.gfxlab.gui.instruments;
|
||||||
|
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
|
import javafx.scene.image.WritableImage;
|
||||||
|
import javafx.scene.layout.Pane;
|
||||||
|
import javafx.scene.layout.Region;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
import xyz.marsavic.geometry.Vector;
|
||||||
|
import xyz.marsavic.gfxlab.Renderer;
|
||||||
|
import xyz.marsavic.gfxlab.elements.Output;
|
||||||
|
import xyz.marsavic.gfxlab.gui.UtilsGL;
|
||||||
|
import xyz.marsavic.objectinstruments.instruments.ObjectInstrument;
|
||||||
|
import xyz.marsavic.time.Profiler;
|
||||||
|
|
||||||
|
|
||||||
|
public class InstrumentRenderer extends ObjectInstrument<Output<Renderer>> {
|
||||||
|
|
||||||
|
private final Pane pane;
|
||||||
|
private final ImageView imageView;
|
||||||
|
|
||||||
|
private WritableImage image;
|
||||||
|
|
||||||
|
private int iFrameNext = 0;
|
||||||
|
|
||||||
|
private final Profiler profilerUpdate = UtilsGL.profiler(this, "update");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public InstrumentRenderer(Output<Renderer> outRenderer) {
|
||||||
|
imageView = new ImageView();
|
||||||
|
pane = new VBox(imageView);
|
||||||
|
|
||||||
|
setObject(outRenderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update() {
|
||||||
|
profilerUpdate.enter();
|
||||||
|
object().get().process(iFrameNext++, m -> {
|
||||||
|
Vector size = m.size();
|
||||||
|
if ((image == null) || !UtilsGL.imageSize(image).equals(size)) {
|
||||||
|
image = UtilsGL.createWritableImage(size);
|
||||||
|
imageView.setImage(image);
|
||||||
|
}
|
||||||
|
UtilsGL.writeRawImageToImage(image, m);
|
||||||
|
});
|
||||||
|
profilerUpdate.exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Region node() {
|
||||||
|
return pane;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
77
src/xyz/marsavic/gfxlab/playground/GfxLab.java
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
package xyz.marsavic.gfxlab.playground;
|
||||||
|
|
||||||
|
import xyz.marsavic.functions.interfaces.A2;
|
||||||
|
import xyz.marsavic.functions.interfaces.F1;
|
||||||
|
import xyz.marsavic.gfxlab.*;
|
||||||
|
import xyz.marsavic.gfxlab.elements.Output;
|
||||||
|
import xyz.marsavic.gfxlab.graphics3d.raytracers.RayTracingTest;
|
||||||
|
import xyz.marsavic.gfxlab.gui.UtilsGL;
|
||||||
|
import xyz.marsavic.gfxlab.tonemapping.ColorTransform;
|
||||||
|
import xyz.marsavic.gfxlab.tonemapping.ToneMapping;
|
||||||
|
import xyz.marsavic.gfxlab.tonemapping.colortransforms.Identity;
|
||||||
|
|
||||||
|
import static xyz.marsavic.gfxlab.elements.ElementF.e;
|
||||||
|
import static xyz.marsavic.gfxlab.elements.Output.val;
|
||||||
|
|
||||||
|
|
||||||
|
public class GfxLab {
|
||||||
|
|
||||||
|
public Output<Renderer> outRenderer;
|
||||||
|
|
||||||
|
public GfxLab() {
|
||||||
|
var eSize = e(Vec3::new, val(1200.0), val(640.0), val(640.0));
|
||||||
|
|
||||||
|
var eRenderer =
|
||||||
|
e(Renderer::new,
|
||||||
|
eSize,
|
||||||
|
e(Fs::aFillFrameInt,
|
||||||
|
e(Fs::aFillFrameColor,
|
||||||
|
e(Fs::transformedColorFunction,
|
||||||
|
// e(Blobs::new, val(5), val(0.1), val(0.2)),
|
||||||
|
e(RayTracingTest::new),
|
||||||
|
e(TransformationsFromSize.toGeometric, eSize)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
e(Fs::toneMapping,
|
||||||
|
e(ColorTransform::asColorTransformFromMatrixColor,
|
||||||
|
e(Identity::new)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
outRenderer = eRenderer.out();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class Fs {
|
||||||
|
|
||||||
|
public static ColorFunction transformedColorFunction(ColorFunction colorFunction, Transformation transformation) {
|
||||||
|
return p -> colorFunction.at(transformation.at(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static A2<Matrix<Color>, Double> aFillFrameColor(ColorFunction colorFunction) {
|
||||||
|
return (Matrix<Color> result, Double t) -> {
|
||||||
|
result.fill(p -> colorFunction.at(t, p));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static A2<Matrix<Integer>, Double> aFillFrameInt(A2<Matrix<Color>, Double> aFillFrameColor, ToneMapping toneMapping) {
|
||||||
|
return (Matrix<Integer> result, Double t) -> {
|
||||||
|
UtilsGL.matricesColor.borrow(result.size(), true, mC -> {
|
||||||
|
aFillFrameColor.execute(mC, t);
|
||||||
|
toneMapping.apply(mC, result);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ToneMapping toneMapping(F1<ColorTransform, Matrix<Color>> f_ColorTransform_MatrixColor) {
|
||||||
|
return (input, output) -> {
|
||||||
|
ColorTransform f = f_ColorTransform_MatrixColor.at(input);
|
||||||
|
output.fill(p -> f.at(input.get(p)).code());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
54
src/xyz/marsavic/gfxlab/playground/colorfunctions/Blobs.java
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
package xyz.marsavic.gfxlab.playground.colorfunctions;
|
||||||
|
|
||||||
|
import xyz.marsavic.geometry.Box;
|
||||||
|
import xyz.marsavic.geometry.Vector;
|
||||||
|
import xyz.marsavic.gfxlab.Color;
|
||||||
|
import xyz.marsavic.gfxlab.ColorFunctionT;
|
||||||
|
import xyz.marsavic.utils.Defaults;
|
||||||
|
import xyz.marsavic.gfxlab.elements.Immutable;
|
||||||
|
import xyz.marsavic.random.sampling.Sampler;
|
||||||
|
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
public class Blobs implements ColorFunctionT {
|
||||||
|
|
||||||
|
private final int n;
|
||||||
|
private final double m;
|
||||||
|
private final double threshold;
|
||||||
|
|
||||||
|
|
||||||
|
private final Vector[] o, c;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static final Defaults<Blobs> $ = Defaults.args(5, 0.1, 0.2);
|
||||||
|
|
||||||
|
public Blobs(int n, double m, double threshold) {
|
||||||
|
this.n = n;
|
||||||
|
this.m = m;
|
||||||
|
this.threshold = threshold;
|
||||||
|
|
||||||
|
o = new Vector[n];
|
||||||
|
c = new Vector[n];
|
||||||
|
|
||||||
|
Sampler sampler = new Sampler(-923147595275902678L);
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
o[i] = sampler.randomInBox(Box.cr(10)).round();
|
||||||
|
c[i] = sampler.randomInBox();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Color at(double t, Vector p) {
|
||||||
|
double s = 0;
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
Vector c = Vector.polar(o[i].mul(t).add(this.c[i])).mul(0.5);
|
||||||
|
double d = p.sub(c).lengthSquared();
|
||||||
|
s += Math.exp(-d / m);
|
||||||
|
}
|
||||||
|
double k = 0.3 * s - threshold;
|
||||||
|
return (k < 0 ? Color.rgb(0.3, 0.0, 0.4) : Color.rgb(0.9, 0.8, 0.1)).mul(Math.abs(k));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package xyz.marsavic.gfxlab.playground.colorfunctions;
|
||||||
|
|
||||||
|
import xyz.marsavic.geometry.Vector;
|
||||||
|
import xyz.marsavic.gfxlab.Color;
|
||||||
|
import xyz.marsavic.gfxlab.ColorFunctionT;
|
||||||
|
import xyz.marsavic.gfxlab.elements.Element;
|
||||||
|
|
||||||
|
public class GammaTest extends Element implements ColorFunctionT {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Color at(double t, Vector p) {
|
||||||
|
return Color.gray(p.x() < 320 ? 0.5 : (p.xInt() ^ p.yInt()) & 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package xyz.marsavic.gfxlab.playground.colorfunctions;
|
||||||
|
|
||||||
|
import xyz.marsavic.geometry.Vector;
|
||||||
|
import xyz.marsavic.gfxlab.Color;
|
||||||
|
import xyz.marsavic.gfxlab.ColorFunctionT;
|
||||||
|
import xyz.marsavic.gfxlab.elements.Element;
|
||||||
|
import xyz.marsavic.utils.Numeric;
|
||||||
|
|
||||||
|
|
||||||
|
public class Gradient extends Element implements ColorFunctionT {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Color at(double t, Vector p) {
|
||||||
|
return Color.rgb(p.x(), 1-p.x(), 0.5 + 0.5 * Numeric.sinT(t));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
16
src/xyz/marsavic/gfxlab/playground/colorfunctions/OkLab.java
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
package xyz.marsavic.gfxlab.playground.colorfunctions;
|
||||||
|
|
||||||
|
import xyz.marsavic.geometry.Vector;
|
||||||
|
import xyz.marsavic.gfxlab.Color;
|
||||||
|
import xyz.marsavic.gfxlab.ColorFunctionT;
|
||||||
|
import xyz.marsavic.gfxlab.elements.Element;
|
||||||
|
|
||||||
|
|
||||||
|
public class OkLab extends Element implements ColorFunctionT {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Color at(double t, Vector p) {
|
||||||
|
return Color.oklabPolar(t, p.x(), p.y());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package xyz.marsavic.gfxlab.playground.colorfunctions;
|
||||||
|
|
||||||
|
import xyz.marsavic.geometry.Vector;
|
||||||
|
import xyz.marsavic.gfxlab.Color;
|
||||||
|
import xyz.marsavic.gfxlab.ColorFunctionT;
|
||||||
|
import xyz.marsavic.gfxlab.elements.Element;
|
||||||
|
import xyz.marsavic.utils.Numeric;
|
||||||
|
|
||||||
|
|
||||||
|
public class Spirals extends Element implements ColorFunctionT {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Color at(double t, Vector p) {
|
||||||
|
return Color.rgb(
|
||||||
|
Math.max(0, Numeric.sinT(-t + 7 * p.angle())),
|
||||||
|
Math.max(0, Numeric.sinT(2 * t + 0.25 / p.length() + p.angle())),
|
||||||
|
0.4
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
36
src/xyz/marsavic/gfxlab/resources/Resources.java
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
package xyz.marsavic.gfxlab.resources;
|
||||||
|
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Objects;
|
||||||
|
import org.kordamp.ikonli.Ikon;
|
||||||
|
import org.kordamp.ikonli.materialdesign2.MaterialDesignC;
|
||||||
|
|
||||||
|
|
||||||
|
public class Resources {
|
||||||
|
|
||||||
|
private static Image[] iconsApplication = null;
|
||||||
|
|
||||||
|
public static final String stylesheetURL = xyz.marsavic.objectinstruments.resources.Resources.stylesheetURL;
|
||||||
|
|
||||||
|
|
||||||
|
public static Image[] iconsApplication() {
|
||||||
|
if (iconsApplication == null) {
|
||||||
|
String iconName = "mars";
|
||||||
|
|
||||||
|
iconsApplication = Arrays.stream((new String[]{"016", "024", "032", "048", "064", "128"})).map(size ->
|
||||||
|
new Image(Objects.requireNonNull(Resources.class.getResourceAsStream("icons/" + iconName + " " + size + ".png")))
|
||||||
|
).toArray(Image[]::new);
|
||||||
|
}
|
||||||
|
return iconsApplication;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class Ikons {
|
||||||
|
// https://kordamp.org/ikonli/cheat-sheet-materialdesign2.html
|
||||||
|
public static final Ikon COPY = MaterialDesignC.CONTENT_COPY;
|
||||||
|
public static final Ikon SAVE = MaterialDesignC.CONTENT_SAVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
15
src/xyz/marsavic/gfxlab/tonemapping/ColorTransform.java
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package xyz.marsavic.gfxlab.tonemapping;
|
||||||
|
|
||||||
|
import xyz.marsavic.functions.interfaces.F1;
|
||||||
|
import xyz.marsavic.gfxlab.Color;
|
||||||
|
import xyz.marsavic.gfxlab.Matrix;
|
||||||
|
|
||||||
|
|
||||||
|
public interface ColorTransform extends F1<Color, Color> {
|
||||||
|
|
||||||
|
default F1<ColorTransform, Matrix<Color>> asColorTransformFromMatrixColor() {
|
||||||
|
return colorMatrix -> ColorTransform.this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
21
src/xyz/marsavic/gfxlab/tonemapping/ToneMapping.java
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
package xyz.marsavic.gfxlab.tonemapping;
|
||||||
|
|
||||||
|
import xyz.marsavic.functions.interfaces.F1;
|
||||||
|
import xyz.marsavic.gfxlab.Color;
|
||||||
|
import xyz.marsavic.gfxlab.Matrix;
|
||||||
|
import xyz.marsavic.gfxlab.MatrixInts;
|
||||||
|
|
||||||
|
|
||||||
|
public interface ToneMapping extends F1<Matrix<Integer>, Matrix<Color>> {
|
||||||
|
void apply(Matrix<Color> input, Matrix<Integer> output);
|
||||||
|
|
||||||
|
|
||||||
|
/** A better option is to use ResourceManager to borrow an output Matrix and call apply on it. */
|
||||||
|
@Override
|
||||||
|
default Matrix<Integer> at(Matrix<Color> input) {
|
||||||
|
Matrix<Integer> output = new MatrixInts(input.size());
|
||||||
|
apply(input, output);
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package xyz.marsavic.gfxlab.tonemapping.colortransforms;
|
||||||
|
|
||||||
|
import xyz.marsavic.gfxlab.Color;
|
||||||
|
import xyz.marsavic.gfxlab.Vec3;
|
||||||
|
import xyz.marsavic.gfxlab.elements.Immutable;
|
||||||
|
import xyz.marsavic.gfxlab.tonemapping.ColorTransform;
|
||||||
|
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
public record HueShift(
|
||||||
|
double hueShift
|
||||||
|
) implements ColorTransform {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Color at(Color color) {
|
||||||
|
Vec3 hsb = color.hsb();
|
||||||
|
return Color.hsb(hsb.add(Vec3.xyz(hueShift, 0, 0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package xyz.marsavic.gfxlab.tonemapping.colortransforms;
|
||||||
|
|
||||||
|
import xyz.marsavic.gfxlab.Color;
|
||||||
|
import xyz.marsavic.gfxlab.elements.Immutable;
|
||||||
|
import xyz.marsavic.gfxlab.tonemapping.ColorTransform;
|
||||||
|
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
public record Identity(
|
||||||
|
|
||||||
|
) implements ColorTransform {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Color at(Color color) {
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package xyz.marsavic.gfxlab.tonemapping.colortransforms;
|
||||||
|
|
||||||
|
import xyz.marsavic.gfxlab.Color;
|
||||||
|
import xyz.marsavic.gfxlab.elements.Immutable;
|
||||||
|
import xyz.marsavic.gfxlab.tonemapping.ColorTransform;
|
||||||
|
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
public record Multiply(
|
||||||
|
double factor
|
||||||
|
) implements ColorTransform {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Color at(Color color) {
|
||||||
|
return color.mul(factor);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
65
todo.md
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
## Animations
|
||||||
|
|
||||||
|
### Workers
|
||||||
|
|
||||||
|
Workers are animations that compute iterations inside an infinite loop.
|
||||||
|
|
||||||
|
- Workers have to be registered with the app, so that they all can be controlled (started/paused/disposed) from outside an animation.
|
||||||
|
- Ideally, the app will provide that infinite loop, and a the worker will just provide the action to be performed inside.
|
||||||
|
|
||||||
|
|
||||||
|
### Pulling
|
||||||
|
|
||||||
|
Pullers should be the only type of workers?
|
||||||
|
|
||||||
|
`PullingCache`
|
||||||
|
- A worker with last frame cached and immediately available to the caller.
|
||||||
|
|
||||||
|
|
||||||
|
### Buffering
|
||||||
|
|
||||||
|
- Buffer manager provides matrix buffers per request.
|
||||||
|
- Has a set of matrices, reuses them.
|
||||||
|
- Clients work with buffers only inside a callback block.
|
||||||
|
- The caller needs to provide a buffer to be filled by the called animation.
|
||||||
|
- Benefit: same buffer can propagate through several layers.
|
||||||
|
- Buffer manager sets private data field inside Matrix buffer to null when the block is exited.
|
||||||
|
- If anyone saved the reference to the Matrix it will now be useless, throwing a NPE if trying to read/write to it.
|
||||||
|
|
||||||
|
What to do with animations needing to store matrices as fields?
|
||||||
|
- Well, are they really replacing these matrices or just overwriting them?
|
||||||
|
|
||||||
|
|
||||||
|
### Synchronization
|
||||||
|
|
||||||
|
Animation T might need to work on the sources A and B obtained from the same iteration of S.
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
A
|
||||||
|
/ \
|
||||||
|
S T
|
||||||
|
\ /
|
||||||
|
B
|
||||||
|
```
|
||||||
|
|
||||||
|
Keep a history of *touches* at every frame. The history is a map (animation -> iteration number) containing all the animations that processed the frame.
|
||||||
|
|
||||||
|
If synchronization is needed (at T), wait until all the sources (frames from A and B) have the same iteration number at the wanted animation (S).
|
||||||
|
|
||||||
|
How to unite histories at T?
|
||||||
|
- Keep only entries that are the same in A and B.
|
||||||
|
|
||||||
|
## Graphics
|
||||||
|
|
||||||
|
### Noise removal
|
||||||
|
|
||||||
|
1. Make ray tracers return detailed info about each pixel: body, normal, uv.
|
||||||
|
1. Make a matrix containing the ratio between the obtained color and the texture color at (body, uv) for each pixel.
|
||||||
|
1. Blur that matrix, observing thresholds based on (body, normal, uv).
|
||||||
|
1. Recompute the colors by multiplying the texture color with blurred value.
|
||||||
|
|
||||||
|
- Be adaptive, reduce the blur radius if there is less noise.
|
||||||
|
- Not only per frame, but per pixel?
|
||||||
|
|
||||||
|
* Alas, what shell we do with lens blur?
|