Allow files to be created and deleted and provide some default files for svg, lua, obj, and txt

pull/82/head
James Ball 2022-06-11 14:21:01 +01:00 zatwierdzone przez James H Ball
rodzic a4a6f4bb53
commit ad8f939ad5
12 zmienionych plików z 237 dodań i 86 usunięć

Wyświetl plik

@ -3,9 +3,11 @@ package sh.ball.gui.controller;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.*;
import javafx.scene.input.KeyCode;
import javafx.scene.shape.SVGPath;
import javafx.stage.DirectoryChooser;
import javafx.stage.FileChooser;
@ -19,10 +21,12 @@ import javax.xml.parsers.ParserConfigurationException;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.*;
import static sh.ball.gui.Gui.audioPlayer;
import static sh.ball.gui.Gui.main;
public class GeneralController implements Initializable, SubController {
@ -61,6 +65,14 @@ public class GeneralController implements Initializable, SubController {
private Slider micVolumeSlider;
@FXML
private SVGPath micVolumeMidi;
@FXML
private TextField createFileTextField;
@FXML
private ComboBox<String> createFileComboBox;
@FXML
private Button createFileButton;
@FXML
private Button deleteFileButton;
public void setRecordResult(String result) {
recordLabel.setText(result);
@ -216,6 +228,38 @@ public class GeneralController implements Initializable, SubController {
});
editFileButton.setOnAction(e -> mainController.openCodeEditor());
deleteFileButton.setOnAction(e -> mainController.deleteCurrentFile());
createFileComboBox.setItems(FXCollections.observableList(List.of(".lua", ".svg", ".obj", ".txt")));
createFileComboBox.setValue(".lua");
try {
Map<String, byte[]> defaultFileMap = Map.of(
".lua", getClass().getResourceAsStream("/lua/demo.lua").readAllBytes(),
".svg", getClass().getResourceAsStream("/svg/demo.svg").readAllBytes(),
".obj", getClass().getResourceAsStream("/models/cube.obj").readAllBytes(),
".txt", "hello".getBytes(StandardCharsets.UTF_8)
);
createFileButton.setOnAction(e -> {
try {
String fileName = createFileTextField.getText() + createFileComboBox.getValue();
byte[] fileData = defaultFileMap.get(createFileComboBox.getValue());
mainController.createFile(fileName, fileData);
createFileTextField.setText("");
} catch (Exception ex) {
ex.printStackTrace();
}
});
} catch (IOException e) {
e.printStackTrace();
}
createFileTextField.setOnKeyPressed(event -> {
if (event.getCode() == KeyCode.ENTER) {
createFileButton.getOnAction().handle(null);
}
});
}
@Override
@ -233,7 +277,7 @@ public class GeneralController implements Initializable, SubController {
public void updateFrequency(double leftFrequency, double rightFrequency) {
Platform.runLater(() ->
frequencyLabel.setText(String.format("L/R Frequency:\n%d Hz / %d Hz", Math.round(leftFrequency), Math.round(rightFrequency)))
frequencyLabel.setText(String.format("L/R Frequency: %5d Hz\t / %5d Hz", Math.round(leftFrequency), Math.round(rightFrequency)))
);
}

Wyświetl plik

@ -53,6 +53,7 @@ import sh.ball.engine.ObjectServer;
import sh.ball.engine.ObjectSet;
import sh.ball.gui.Gui;
import sh.ball.oscilloscope.ByteWebSocketServer;
import sh.ball.parser.FileParser;
import sh.ball.parser.lua.LuaParser;
import sh.ball.parser.obj.ObjFrameSettings;
import sh.ball.parser.obj.ObjParser;
@ -96,11 +97,10 @@ public class MainController implements Initializable, FrequencyListener, MidiLis
// frames
private static final InputStream DEFAULT_OBJ = MainController.class.getResourceAsStream("/models/cube.obj");
private final ExecutorService executor = Executors.newSingleThreadExecutor();
private final LuaParser luaParser = new LuaParser();
private final Set<String> unsavedFileNames = new LinkedHashSet<>();
private List<byte[]> openFiles = new ArrayList<>();
private List<String> frameSourcePaths = new ArrayList<>();
private List<FrameSource<Vector2>> sampleSources = new ArrayList<>();
private List<FileParser<FrameSource<Vector2>>> sampleParsers = new ArrayList<>();
private List<FrameSource<List<Shape>>> frameSources = new ArrayList<>();
private FrameProducer<List<Shape>> producer;
private int currentFrameSource;
@ -190,7 +190,7 @@ public class MainController implements Initializable, FrequencyListener, MidiLis
openFiles.add(baos.toByteArray());
frameSources.add(frames);
sampleSources.add(null);
sampleParsers.add(null);
frameSourcePaths.add("cube.obj");
currentFrameSource = 0;
this.producer = new FrameProducer<>(audioPlayer, frames);
@ -518,8 +518,12 @@ public class MainController implements Initializable, FrequencyListener, MidiLis
}
public void setLuaVariable(String variableName, Object value) {
sampleParsers.forEach(sampleParser -> {
if (sampleParser instanceof LuaParser luaParser) {
luaParser.setVariable(variableName, value);
}
});
}
public void setSoftwareOscilloscopeAction(Runnable openBrowser) {
softwareOscilloscopeMenuItem.setOnAction((e) -> openBrowser.run());
@ -620,17 +624,42 @@ public class MainController implements Initializable, FrequencyListener, MidiLis
new Thread(analyser).start();
}
private void disableSources() {
frameSources.stream().filter(Objects::nonNull).forEach(FrameSource::disable);
sampleParsers.stream().filter(Objects::nonNull).forEach(parser -> {
try {
parser.get().disable();
} catch (Exception e) {
e.printStackTrace();
}
});
}
// changes the FrameProducer e.g. could be changing from a 3D object to an
// SVG. The old FrameProducer is stopped and a new one created and initialised
// with the same settings that the original had.
private void changeFrameSource(int index) {
if (frameSources.size() == 0) {
generalController.setFrameSourceName("No file open!");
generalController.updateFrameLabels();
audioPlayer.removeSampleSource();
audioPlayer.addFrame(List.of(new Vector2()));
return;
}
index = Math.max(0, Math.min(index, frameSources.size() - 1));
currentFrameSource = index;
frameSources.stream().filter(Objects::nonNull).forEach(FrameSource::disable);
sampleSources.stream().filter(Objects::nonNull).forEach(FrameSource::disable);
disableSources();
FrameSource<List<Shape>> frames = frameSources.get(index);
FrameSource<Vector2> samples = sampleSources.get(index);
FrameSource<Vector2> samples = null;
try {
FileParser<FrameSource<Vector2>> sampleParser = sampleParsers.get(index);
if (sampleParser != null ) {
samples = sampleParser.get();
}
} catch (Exception e) {
e.printStackTrace();
}
if (frames != null) {
frames.enable();
audioPlayer.removeSampleSource();
@ -676,6 +705,56 @@ public class MainController implements Initializable, FrequencyListener, MidiLis
});
}
private void createFile(String name, byte[] fileData, List<FileParser<FrameSource<Vector2>>> sampleParsers, List<FrameSource<List<Shape>>> frameSources, List<String> frameSourcePaths, List<byte[]> openFiles) throws Exception {
if (frameSourcePaths.contains(name)) {
throw new IOException("File already exists with this name");
}
if (LuaParser.isLuaFile(name)) {
LuaParser luaParser = new LuaParser();
luaParser.setScriptFromInputStream(new ByteArrayInputStream(fileData));
luaParser.parse();
sampleParsers.add(luaParser);
luaController.updateLuaVariables();
frameSources.add(null);
} else {
frameSources.add(ParserFactory.getParser(name, fileData).parse());
sampleParsers.add(null);
}
frameSourcePaths.add(name);
openFiles.add(fileData);
Platform.runLater(() -> {
generalController.showMultiFileTooltip(frameSources.size() > 1);
if (generalController.framesPlaying() && frameSources.size() == 1) {
generalController.disablePlayback();
}
});
}
public void createFile(String name, byte[] fileData) throws Exception {
createFile(name, fileData, sampleParsers, frameSources, frameSourcePaths, openFiles);
changeFrameSource(frameSources.size() - 1);
unsavedFileNames.add(name);
setUnsavedFileWarning();
}
public void deleteCurrentFile() {
if (frameSources.isEmpty()) {
return;
}
disableSources();
sampleParsers.remove(currentFrameSource);
frameSources.remove(currentFrameSource);
String name = frameSourcePaths.get(currentFrameSource);
unsavedFileNames.remove(name);
setUnsavedFileWarning();
frameSourcePaths.remove(currentFrameSource);
openFiles.remove(currentFrameSource);
if (currentFrameSource >= frameSources.size()) {
currentFrameSource--;
}
changeFrameSource(currentFrameSource);
}
void updateFileData(byte[] file, String name) {
int index = frameSourcePaths.indexOf(name);
if (index == -1) {
@ -685,16 +764,18 @@ public class MainController implements Initializable, FrequencyListener, MidiLis
openFiles.set(index, file);
try {
if (LuaParser.isLuaFile(name)) {
FileParser<FrameSource<Vector2>> sampleParser = sampleParsers.get(index);
if (sampleParser instanceof LuaParser luaParser) {
luaParser.setScriptFromInputStream(new ByteArrayInputStream(file));
FrameSource<Vector2> sampleSource = sampleSources.get(index);
sampleSources.set(index, luaParser.parse());
sampleSource.disable();
}
sampleParser.parse().disable();
sampleParsers.set(index, sampleParser);
frameSources.set(index, null);
} else {
FrameSource<List<Shape>> frameSource = frameSources.get(index);
frameSources.set(index, ParserFactory.getParser(name, file).parse());
frameSource.disable();
sampleSources.set(index, null);
sampleParsers.set(index, null);
}
unsavedFileNames.add(name);
@ -711,7 +792,7 @@ public class MainController implements Initializable, FrequencyListener, MidiLis
void setUnsavedFileWarning() {
Platform.runLater(() -> {
if (!unsavedFileNames.isEmpty()) {
String warning = "(unsaved file changes: " + String.join(", ", unsavedFileNames) + ")";
String warning = "(unsaved files: " + String.join(", ", unsavedFileNames) + ")";
updateTitle(warning, openProjectPath);
} else {
updateTitle(null, openProjectPath);
@ -720,7 +801,7 @@ public class MainController implements Initializable, FrequencyListener, MidiLis
}
void updateFiles(List<byte[]> files, List<String> names, int startingFrameSource) throws Exception {
List<FrameSource<Vector2>> newSampleSources = new ArrayList<>();
List<FileParser<FrameSource<Vector2>>> newSampleParsers = new ArrayList<>();
List<FrameSource<List<Shape>>> newFrameSources = new ArrayList<>();
List<String> newFrameSourcePaths = new ArrayList<>();
List<byte[]> newOpenFiles = new ArrayList<>();
@ -735,17 +816,8 @@ public class MainController implements Initializable, FrequencyListener, MidiLis
for (int i = 0; i < files.size(); i++) {
try {
if (LuaParser.isLuaFile(names.get(i))) {
luaParser.setScriptFromInputStream(new ByteArrayInputStream(files.get(i)));
newSampleSources.add(luaParser.parse());
newFrameSources.add(null);
} else {
newFrameSources.add(ParserFactory.getParser(names.get(i), files.get(i)).parse());
newSampleSources.add(null);
}
newFrameSourcePaths.add(names.get(i));
newOpenFiles.add(files.get(i));
} catch (IOException | IllegalArgumentException e) {
createFile(names.get(i), files.get(i), newSampleParsers, newFrameSources, newFrameSourcePaths, newOpenFiles);
} catch (Exception e) {
Platform.runLater(() -> {
generalController.setFrameSourceName(e.getMessage());
generalController.updateFrameLabels();
@ -753,21 +825,14 @@ public class MainController implements Initializable, FrequencyListener, MidiLis
}
}
Platform.runLater(() -> {
if (newFrameSources.size() > 0) {
generalController.showMultiFileTooltip(newFrameSources.size() > 1);
if (generalController.framesPlaying() && newFrameSources.size() == 1) {
generalController.disablePlayback();
}
frameSources.stream().filter(Objects::nonNull).forEach(FrameSource::disable);
sampleSources.stream().filter(Objects::nonNull).forEach(FrameSource::disable);
sampleSources = newSampleSources;
disableSources();
sampleParsers = newSampleParsers;
frameSources = newFrameSources;
frameSourcePaths = newFrameSourcePaths;
openFiles = newOpenFiles;
changeFrameSource(startingFrameSource);
}
});
}
// used so that the Controller has access to the stage, allowing it to open
@ -1218,6 +1283,8 @@ public class MainController implements Initializable, FrequencyListener, MidiLis
updateFiles(files, fileNames, 0);
}
luaController.updateLuaVariables();
openProjectPath = projectFileName;
updateTitle(null, projectFileName);
} catch (Exception e) {
@ -1297,8 +1364,12 @@ public class MainController implements Initializable, FrequencyListener, MidiLis
}
public void resetLuaStep() {
sampleParsers.forEach(sampleParser -> {
if (sampleParser instanceof LuaParser luaParser) {
luaParser.resetStep();
}
});
}
private record PrintableSlider(Slider slider) {
@Override

Wyświetl plik

@ -1,10 +1,10 @@
package sh.ball.parser;
import java.io.IOException;
import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.SAXException;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
public abstract class FileParser<T> {
public abstract String getFileExtension();
@ -21,4 +21,6 @@ public abstract class FileParser<T> {
}
public abstract T parse() throws Exception;
public abstract T get() throws Exception;
}

Wyświetl plik

@ -35,6 +35,11 @@ public class LuaParser extends FileParser<FrameSource<Vector2>> {
return sampleSource;
}
@Override
public FrameSource<Vector2> get() {
return sampleSource;
}
public static boolean isLuaFile(String path) {
return path.matches(".*\\.lua");
}

Wyświetl plik

@ -55,6 +55,11 @@ public class ObjParser extends FileParser<FrameSource<List<Shape>>> {
return new ObjFrameSource(object, camera);
}
@Override
public FrameSource<List<Shape>> get() throws IOException {
return parse();
}
// If camera position arguments haven't been specified, automatically work out the position of
// the camera based on the size of the object in the camera's view.
public void setFocalLength(double focalLength) {

Wyświetl plik

@ -190,6 +190,11 @@ public class SvgParser extends FileParser<FrameSource<List<Shape>>> {
}
}
@Override
public FrameSource<List<Shape>> get() throws IOException, ParserConfigurationException, SAXException {
return parse();
}
/* Given a character, will return the glyph associated with it.
* Assumes that the .svg loaded has character glyphs. */
public List<Shape> parseGlyphsWithUnicode(char unicode) {

Wyświetl plik

@ -83,6 +83,11 @@ public class TextParser extends FileParser<FrameSource<List<Shape>>> {
return new ShapeFrameSource(Shape.flip(Shape.normalize(shapes)));
}
@Override
public FrameSource<List<Shape>> get() throws Exception {
return parse();
}
public static boolean isTxtFile(String path) {
return path.matches(".*\\.txt");
}

Wyświetl plik

@ -1,25 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ComboBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.Slider?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.shape.SVGPath?>
<AnchorPane id="control-pane" prefHeight="324.0" prefWidth="364.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sh.ball.gui.controller.GeneralController">
<AnchorPane id="control-pane" prefHeight="341.0" prefWidth="364.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sh.ball.gui.controller.GeneralController">
<children>
<Button fx:id="chooseFileButton" layoutX="14.0" layoutY="26.0" mnemonicParsing="false" prefHeight="26.0" prefWidth="114.0" text="Choose File" />
<Label fx:id="fileLabel" alignment="TOP_LEFT" layoutX="143.0" layoutY="30.0" maxWidth="270.0" prefHeight="42.0" prefWidth="174.0" text="cube.obj" wrapText="true" />
<Label fx:id="recordLabel" layoutX="13.0" layoutY="304.0" maxWidth="451.0" prefHeight="18.0" prefWidth="343.0" />
<Label id="frequency" fx:id="frequencyLabel" layoutX="14.0" layoutY="141.0" prefHeight="58.0" prefWidth="343.0" text="L/R Frequency:&#10; &#10;" />
<Button fx:id="chooseFolderButton" layoutX="14.0" layoutY="76.0" mnemonicParsing="false" prefHeight="26.0" prefWidth="114.0" text="Choose Folder" />
<Label fx:id="jkLabel" layoutX="143.0" layoutY="69.0" maxWidth="270.0" prefHeight="70.0" prefWidth="210.0" text="Use j and k (or MIDI Program Change) to cycle between files, or i to start playback" visible="false" wrapText="true" />
<Slider fx:id="octaveSlider" blockIncrement="1.0" layoutX="82.0" layoutY="215.0" majorTickUnit="1.0" max="5.0" min="-5.0" minorTickCount="0" prefHeight="42.0" prefWidth="246.0" showTickLabels="true" showTickMarks="true" snapToTicks="true" />
<SVGPath fx:id="octaveMidi" content="M20.15 8.26H22V15.74H20.15M13 8.26H18.43C19 8.26 19.3 8.74 19.3 9.3V14.81C19.3 15.5 19 15.74 18.38 15.74H13V11H14.87V13.91H17.5V9.95H13M10.32 8.26H12.14V15.74H10.32M2 8.26H8.55C9.1 8.26 9.41 8.74 9.41 9.3V15.74H7.59V10.15H6.5V15.74H4.87V10.15H3.83V15.74H2Z" fill="WHITE" layoutX="333.0" layoutY="211.0" pickOnBounds="true" />
<Label layoutX="13.0" layoutY="214.0" text="Octave" />
<Slider fx:id="micVolumeSlider" blockIncrement="1.0" layoutX="82.0" layoutY="254.0" majorTickUnit="1.0" max="10.0" prefHeight="42.0" prefWidth="246.0" showTickLabels="true" showTickMarks="true" value="1.0" />
<SVGPath fx:id="micVolumeMidi" content="M20.15 8.26H22V15.74H20.15M13 8.26H18.43C19 8.26 19.3 8.74 19.3 9.3V14.81C19.3 15.5 19 15.74 18.38 15.74H13V11H14.87V13.91H17.5V9.95H13M10.32 8.26H12.14V15.74H10.32M2 8.26H8.55C9.1 8.26 9.41 8.74 9.41 9.3V15.74H7.59V10.15H6.5V15.74H4.87V10.15H3.83V15.74H2Z" fill="WHITE" layoutX="333.0" layoutY="250.0" pickOnBounds="true" />
<Label layoutX="13.0" layoutY="253.0" text="Mic Volume" />
<Button fx:id="editFileButton" layoutX="325.0" layoutY="26.0" mnemonicParsing="false" text="Edit" />
<Button fx:id="chooseFileButton" layoutX="14.0" layoutY="17.0" mnemonicParsing="false" prefHeight="26.0" prefWidth="114.0" text="Choose File" />
<Label fx:id="fileLabel" alignment="TOP_LEFT" layoutX="143.0" layoutY="22.0" maxWidth="270.0" prefHeight="58.0" prefWidth="210.0" text="cube.obj" wrapText="true" />
<Label fx:id="recordLabel" layoutX="14.0" layoutY="323.0" maxWidth="451.0" prefHeight="18.0" prefWidth="343.0" />
<Label id="frequency" fx:id="frequencyLabel" layoutX="14.0" layoutY="152.0" prefHeight="58.0" prefWidth="343.0" text="L/R Frequency:&#10; &#10;" />
<Button fx:id="chooseFolderButton" layoutX="14.0" layoutY="118.0" mnemonicParsing="false" prefHeight="26.0" prefWidth="114.0" text="Choose Folder" />
<Label fx:id="jkLabel" layoutX="143.0" layoutY="89.0" maxWidth="270.0" prefHeight="70.0" prefWidth="218.0" text="Use j and k (or MIDI Program Change) to cycle between files, or i to start playback" visible="false" wrapText="true" />
<Slider fx:id="octaveSlider" blockIncrement="1.0" layoutX="82.0" layoutY="198.0" majorTickUnit="1.0" max="5.0" min="-5.0" minorTickCount="0" prefHeight="42.0" prefWidth="246.0" showTickLabels="true" showTickMarks="true" snapToTicks="true" />
<SVGPath fx:id="octaveMidi" content="M20.15 8.26H22V15.74H20.15M13 8.26H18.43C19 8.26 19.3 8.74 19.3 9.3V14.81C19.3 15.5 19 15.74 18.38 15.74H13V11H14.87V13.91H17.5V9.95H13M10.32 8.26H12.14V15.74H10.32M2 8.26H8.55C9.1 8.26 9.41 8.74 9.41 9.3V15.74H7.59V10.15H6.5V15.74H4.87V10.15H3.83V15.74H2Z" fill="WHITE" layoutX="333.0" layoutY="194.0" pickOnBounds="true" />
<Label layoutX="13.0" layoutY="197.0" text="Octave" />
<Slider fx:id="micVolumeSlider" blockIncrement="1.0" layoutX="82.0" layoutY="237.0" majorTickUnit="1.0" max="10.0" prefHeight="42.0" prefWidth="246.0" showTickLabels="true" showTickMarks="true" value="1.0" />
<SVGPath fx:id="micVolumeMidi" content="M20.15 8.26H22V15.74H20.15M13 8.26H18.43C19 8.26 19.3 8.74 19.3 9.3V14.81C19.3 15.5 19 15.74 18.38 15.74H13V11H14.87V13.91H17.5V9.95H13M10.32 8.26H12.14V15.74H10.32M2 8.26H8.55C9.1 8.26 9.41 8.74 9.41 9.3V15.74H7.59V10.15H6.5V15.74H4.87V10.15H3.83V15.74H2Z" fill="WHITE" layoutX="333.0" layoutY="233.0" pickOnBounds="true" />
<Label layoutX="13.0" layoutY="236.0" text="Mic Volume" />
<Button fx:id="deleteFileButton" layoutX="14.0" layoutY="84.0" mnemonicParsing="false" prefHeight="26.0" prefWidth="114.0" text="Remove File" />
<Button fx:id="createFileButton" layoutX="248.0" layoutY="286.0" minHeight="30.0" mnemonicParsing="false" prefHeight="30.0" prefWidth="95.0" text="Create File" />
<ComboBox fx:id="createFileComboBox" layoutX="166.0" layoutY="286.0" minHeight="30.0" prefHeight="30.0" prefWidth="78.0" />
<TextField fx:id="createFileTextField" alignment="CENTER_RIGHT" layoutX="35.0" layoutY="286.0" prefHeight="30.0" prefWidth="127.0" />
<Button fx:id="editFileButton" layoutX="14.0" layoutY="51.0" mnemonicParsing="false" prefHeight="26.0" prefWidth="114.0" text="Edit File" />
</children>
</AnchorPane>

Wyświetl plik

@ -198,7 +198,7 @@
</Menu>
</menus>
</MenuBar>
<TitledPane animated="false" collapsible="false" layoutX="7.0" layoutY="33.0" minWidth="378.0" prefHeight="371.0" prefWidth="370.0" text="Main settings">
<TitledPane animated="false" collapsible="false" layoutX="7.0" layoutY="33.0" minHeight="371.0" minWidth="378.0" prefHeight="364.0" prefWidth="378.0" text="Main settings">
<fx:include fx:id="general" source="general.fxml" />
</TitledPane>
<TitledPane animated="false" collapsible="false" layoutX="478.0" layoutY="411.0" prefHeight="334.0" prefWidth="523.0" text="Image settings">

Wyświetl plik

@ -0,0 +1,33 @@
--
-- .lua files can be used to make your own custom audio sources!
-- Lua documentation: https://www.lua.org/docs.html
--
-- All variables are saved between calls to this script.
--
-- Below is a simple example of an audio effect that makes a
-- nice visual on an oscilloscope and shows off some of the
-- functionality available.
--
-- The variable 'step' used below is incremented with every
-- call to this script, starting at 1.
--
-- sets 'theta' to 0 initially, or the previous value of
-- 'theta' the last time this script ran
theta = theta or 0
-- updates 'theta' using 'step'
theta = theta + math.sqrt(step) / 1000000000
-- 'slider_a', 'slider_b', ..., 'slider_e' are controlled by
-- the respective sliders in the .lua file settings
left_scale = 0.3 * slider_a
right_scale = 0.3 * slider_b
-- Returns audio samples that will be played back
return {
-- left audio channel
left_scale * math.tan(theta * step),
-- right audio channel
right_scale * math.tan(theta * step + math.pi / 2)
}

Wyświetl plik

@ -1,7 +1,3 @@
# Blender v2.81 (sub 16) OBJ File: ''
# www.blender.org
mtllib cube.mtl
o Cube
v 1.000000 1.000000 -1.000000
v 1.000000 -1.000000 -1.000000
v 1.000000 1.000000 1.000000
@ -10,28 +6,6 @@ v -1.000000 1.000000 -1.000000
v -1.000000 -1.000000 -1.000000
v -1.000000 1.000000 1.000000
v -1.000000 -1.000000 1.000000
vt 0.625000 0.500000
vt 0.875000 0.500000
vt 0.875000 0.750000
vt 0.625000 0.750000
vt 0.375000 0.750000
vt 0.625000 1.000000
vt 0.375000 1.000000
vt 0.375000 0.000000
vt 0.625000 0.000000
vt 0.625000 0.250000
vt 0.375000 0.250000
vt 0.125000 0.500000
vt 0.375000 0.500000
vt 0.125000 0.750000
vn 0.0000 1.0000 0.0000
vn 0.0000 0.0000 1.0000
vn -1.0000 0.0000 0.0000
vn 0.0000 -1.0000 0.0000
vn 1.0000 0.0000 0.0000
vn 0.0000 0.0000 -1.0000
usemtl Material
s off
f 1/1/1 5/2/1 7/3/1 3/4/1
f 4/5/2 3/4/2 7/6/2 8/7/2
f 8/8/3 7/9/3 5/10/3 6/11/3

Wyświetl plik

@ -0,0 +1 @@
<svg><path d="M 0,0 a 75,75 0 1,0 150,0 a 75,75 0 1,0 -150,0"/></svg>

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 69 B