Datei:Animation stirling engine.ogv
Originaldatei (Ogg-Theora-Videodatei, Länge: 40 s, 1.000×1.000 Pixel, 996 kbps insgesamt, Dateigröße: 4,76 MB)
Diese Datei und die Informationen unter dem roten Trennstrich werden aus dem zentralen Medienarchiv Wikimedia Commons eingebunden.
Inhaltsverzeichnis
Beschreibung
BeschreibungAnimation stirling engine.ogv |
English: Animation of a stirling engine in alpha configuration running as a motor.A regenerator is included. |
Datum | |
Quelle | Eigenes Werk |
Urheber | Menner |
Lizenz
Diese Datei wird unter der Creative-Commons-Lizenz „CC0 1.0 Verzicht auf das Copyright“ zur Verfügung gestellt. | |
Die Person, die das Werk mit diesem Dokument verbunden hat, übergibt dieses weltweit der Gemeinfreiheit, indem sie alle Urheberrechte und damit verbundenen weiteren Rechte – im Rahmen der jeweils geltenden gesetzlichen Bestimmungen – aufgibt. Das Werk kann – selbst für kommerzielle Zwecke – kopiert, modifiziert und weiterverteilt werden, ohne hierfür um Erlaubnis bitten zu müssen.
http://creativecommons.org/publicdomain/zero/1.0/deed.enCC0Creative Commons Zero, Public Domain Dedicationfalsefalse |
Source code
Recommended tools:
- Java (Java 8 used)
- mjpeg tools (2.1 used)
- inkscape (0.48 used)
- ffmpeg2theora (0.29 used)
- Eclipse (4.4.1 used)
All tools are available for Linux and Windows.
Core tool is Java. Additional helper tools and scripts generate a encoded video from a sequence of svg files. See below
The file File:Stirling template.svg serves as template used by Java code.
Java
Create a Java project with Eclipse. It requires only one file with one class.
package wikipedia.stirling;
import java.io.File;
import java.nio.file.InvalidPathException;
import java.nio.file.Paths;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Locale;
import javax.swing.JFileChooser;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
*
* The StirlingSvgManipulator takes a specially prepared template SVG file and from that it creates a
* sequence of animated SVG files explaining a stirling engine. The template SVG was created with the
* popular Inkscape illustration tool.
*
* StirlingSvgManipulator opens the SVG file a XML based format by using a DOM parser. With DOM specially
* tagged SVG graphic elements can be searched and manipulated. Mostly the "matrix" attribute from SVG
* is used to translate and rotate elements. Further corners of a rectangular path are edited to expand
* and shrink its volume.
*
*/
public class StirlingSvgManipulator {
static final String filename = "stirling";
static final String template = filename + "_template.svg";
static final int duration = 4*10; // duration in seconds
static final int framerate = 30; // frame rate per second
static final double angularSpeed = 1./10.; // rotations per second
static final double Length = 350.;
static final double LengthPiston = 150.;
static final double Excentricity = 100.;
static final int OffsetX = 250;
static final int OffsetY = 250;
/**
* @param args
*
* Opens a file chooser dialog which requires to select the directory where the template SVG is found
* and the resulting SVG files are placed.
*
* @throws Exception
*/
public static void main(String[] args) throws Exception {
long startTime = System.currentTimeMillis();
Path wspPath = null;
Path templatePath = null;
JFileChooser filechooser = new JFileChooser();
filechooser.setCurrentDirectory(new java.io.File("."));
filechooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
filechooser.setAcceptAllFileFilterUsed(false);
filechooser.setDialogTitle("Select working directory");
int approve = filechooser.showOpenDialog(null);
if(approve == JFileChooser.APPROVE_OPTION) {
System.out.println("You chose to open this file: " +
filechooser.getSelectedFile().getAbsolutePath());
Path path = filechooser.getSelectedFile().toPath();
wspPath = Paths.get(path.toString(), "svg");
templatePath = Paths.get(path.toString(), template);
} else {
System.out.println("Execution canceled!");
return;
}
mkdirSvg(wspPath);
StirlingSvgManipulator template = new StirlingSvgManipulator(templatePath);
template.load();
for (int count = 0 ; count <= (duration * framerate); count++) { // time in seconds
System.out.println("===============================");
System.out.println("Iteration: " + String.valueOf(count));
System.out.println("===============================");
double time = (double) count / (double) framerate;
template.setAngle(2. * Math.PI * angularSpeed * time);
template.applySettings();
Path destination = Paths.get(wspPath.toString(), filename + "-" + String.format("%04d", count) + ".svg");
template.save(destination);
}
long endTime = System.currentTimeMillis();
System.out.println("Execution time: " + String.valueOf((endTime-startTime)/1000) + "s");
System.out.println("Finished");
}
public static void mkdirSvg(Path dir) throws IOException {
boolean success = false;
File directory = dir.toFile();
if (directory.exists()) {
System.out.println("Directory already exists!");
} else {
success = directory.mkdir();
if (success == true) {
System.out.println("Successful");
} else {
throw new IOException("can't make directory for " + dir.toString());
}
}
}
private Path templatePath = null;
private Document document = null;
private double angleRad = 0.;
private double x;
private double y;
private double xStroke;
private double yStroke;
private double alpha1; // Angle between conrod and y axis
private double alpha2; // Angle between conrod and x axis
/**
*
* Konstruktor
*
* @param templatePath
*/
public StirlingSvgManipulator(Path templatePath) {
// TODO Auto-generated constructor stub
this.templatePath = templatePath;
}
/**
*
* Load template with StaXParser
* @throws Exception
*
*/
public void load() throws Exception {
// TODO Auto-generated method stub
try {
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
document = docBuilder.parse(templatePath.toFile());
} catch (IOException | ParserConfigurationException e) {
System.out.print("Error loading " + templatePath.toString() + "\n");
e.printStackTrace();
throw e;
}
}
/**
*
* Write XML file with StaXParser
*
* @param destination
* @throws TransformerException
*/
public void save(Path destination) throws TransformerException {
try {
TransformerFactory transFactory = TransformerFactory.newInstance();
Transformer trans = transFactory.newTransformer();
trans.setOutputProperty(OutputKeys.INDENT, "yes");
trans.setOutputProperty(OutputKeys.METHOD, "xml");
trans.transform(new DOMSource(document), new StreamResult(destination.toFile()));
} catch (TransformerConfigurationException e) {
e.printStackTrace();
throw e;
} catch (TransformerException e) {
// TODO Auto-generated catch block
e.printStackTrace();
throw e;
}
}
/**
*
* @param rad Angular position for the engine
*/
public void setAngle(double rad) {
angleRad = rad;
}
public void applySettings() {
System.out.println( "calcParameters();");
calcParameters();
System.out.println( "applyKolben();");
applyPiston();
System.out.println( "applyHeatexchanger();");
applyHeatexchanger();
System.out.println( "applyVolumen();");
applyVolume();
}
private void calcParameters() {
x = Math.cos(angleRad);
y = Math.sin(angleRad);
alpha1 = Math.acos(x * Excentricity / Length);
alpha2 = Math.acos(y * Excentricity / Length);
xStroke = x * Excentricity + Math.sqrt(Length * Length - (y*Excentricity)*(y*Excentricity)) - Length;
yStroke = y * Excentricity + Math.sqrt(Length * Length - (x*Excentricity)*(x*Excentricity)) - Length; // Pythagoras
}
private void applyVolume() {
NodeList nodes = document.getElementsByTagName("g");
System.out.println( "Gefunden: " + Integer.toString(nodes.getLength()) + " groups");
nodes.item(0);
for (int i = 0; i < nodes.getLength(); ++i) {
Node node = nodes.item(i);
NamedNodeMap attributes = node.getAttributes();
Node id = attributes.getNamedItem("id");
if(id.getNodeValue().equals("gVolume1") == true) {
NodeList childNodes = node.getChildNodes();
Node volumen = childNodes.item(1);
NamedNodeMap childAttributes = volumen.getAttributes();
Node childId = childAttributes.getNamedItem("id");
if(childId.getNodeValue().equals("pVolume1") == true) {
Node d = childAttributes.getNamedItem("d");
System.out.print("Volume path d: " + d.getNodeValue() + "\n");
String[] parameters = d.getNodeValue().split(" ");
if(parameters.length != 6) {
throw new NullPointerException();
}
String[] links = parameters[3].split(",");
String[] rechts = parameters[4].split(",");
links[1] = String.format(Locale.US, "%.3f", yStroke);
rechts[1] = String.format( Locale.US, "%.3f", yStroke);
parameters[3] = links[0] + "," + links[1];
parameters[4] = rechts[0] + "," + rechts[1];
StringBuilder parametersBuild = new StringBuilder();
for(String parameter : parameters) {
parametersBuild.append(parameter + " ");
}
d.setNodeValue(parametersBuild.toString().trim());
}
Node transform = attributes.getNamedItem("transform");
System.out.print("transform: " + transform.getNodeValue() + "\n");
String offset = String.format(Locale.US, "%.3f", (double) OffsetY + Length + LengthPiston);
String matrix = "translate(" + String.valueOf(OffsetX) + "," + offset + ")";
System.out.println(matrix);
transform.setNodeValue(matrix);
}
if(id.getNodeValue().equals("gVolume2") == true) {
NodeList childNodes = node.getChildNodes();
Node volumen = childNodes.item(1);
NamedNodeMap childAttributes = volumen.getAttributes();
Node childId = childAttributes.getNamedItem("id");
if(childId.getNodeValue().equals("pVolume2") == true) {
Node d = childAttributes.getNamedItem("d");
System.out.print("Volume path d: " + d.getNodeValue() + "\n");
String[] parameters = d.getNodeValue().split(" ");
if(parameters.length != 6) {
throw new NullPointerException();
}
String[] links = parameters[3].split(",");
String[] rechts = parameters[4].split(",");
links[0] = String.format(Locale.US, "%.3f", xStroke);
rechts[0] = String.format( Locale.US, "%.3f", xStroke);
parameters[3] = links[0] + "," + links[1];
parameters[4] = rechts[0] + "," + rechts[1];
StringBuilder parametersBuild = new StringBuilder();
for(String parameter : parameters) {
parametersBuild.append(parameter + " ");
}
d.setNodeValue(parametersBuild.toString().trim());
}
Node transform = attributes.getNamedItem("transform");
System.out.print("transform: " + transform.getNodeValue() + "\n");
String offset = String.format(Locale.US, "%.3f", (double) OffsetX + Length + LengthPiston);
String matrix = "translate(" + offset + "," + String.valueOf(OffsetY) + ")";
System.out.println(matrix);
transform.setNodeValue(matrix);
}
}
}
/**
*
* applyHeatexchanter()
*
* This Drawing is not very precise and just for illustration to get an idea about the state of the heat exchanger.
*
*/
private void applyHeatexchanger() {
NodeList nodes = document.getElementsByTagName("linearGradient");
System.out.println( "Gefunden: " + Integer.toString(nodes.getLength()) + " linear Gradient(s)");
nodes.item(0);
double sum = ( x + -1. * y)/Math.sqrt(2.);
double pos = 0.5 + 0.4 * sum; // TODO magic numbers
double delta = 0.1;
System.out.println("");
//double sum =
// V_1 / V_2
// V_1 = x_hub
// V_2 = y_hub
for (int i = 0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
NamedNodeMap attributes = node.getAttributes();
Node id = attributes.getNamedItem("id");
System.out.println("Gradient -ID: " + id.getNodeValue());
if(id.getNodeValue().equals("linearGradientHeat") == true) {
NodeList stopNodes = node.getChildNodes();
System.out.println( "Gefunden: " + Integer.toString(stopNodes.getLength()) + " stop(s)");
// stopNodes.item(0);
for (int j = 0; j < stopNodes.getLength(); j++) {
Node stopNode = stopNodes.item(j);
if(stopNode.getNodeName().equals("stop") == true) {
NamedNodeMap stopAttributes = stopNode.getAttributes();
System.out.println( "Gefunden: " + Integer.toString(stopAttributes.getLength()) + " stop attribut(e)");
Node stopId = stopAttributes.getNamedItem("id");
if(stopId.getNodeValue().equals("stopRed") == true) {
System.out.println("Stop ID: " + stopId.getNodeValue());
Node offset = stopAttributes.getNamedItem("offset");
offset.setNodeValue(String.valueOf(pos - delta));
}
if(stopId.getNodeValue().equals("stopBlue1") == true) {
System.out.println("Stop ID: " + stopId.getNodeValue());
Node offset = stopAttributes.getNamedItem("offset");
offset.setNodeValue(String.valueOf(pos + delta));
}
}
}
}
}
}
private void applyPiston() {
NodeList nodes = document.getElementsByTagName("g");
System.out.println( "Found: " + Integer.toString(nodes.getLength()) + " groups");
nodes.item(0);
double alpha_x1 = Math.cos(alpha1);
double alpha_y1 = Math.sin(alpha1);
double alpha_x2 = Math.cos(alpha2);
double alpha_y2 = Math.sin(alpha2);
System.out.println("alpha: " + String.format(Locale.US, "%.3f, alpha_x: ", alpha1) +
String.format(Locale.US, "%.3f, alpha_y: ", alpha_x1) + String.format( Locale.US, "%.3f, ", alpha_y1));
for (int i = 0; i < nodes.getLength(); ++i) {
Node node = nodes.item(i);
NamedNodeMap attributes = node.getAttributes();
Node id = attributes.getNamedItem("id");
System.out.println("Group-ID: " + id.getNodeValue());
if(id.getNodeValue().equals("gCrankShaft") == true) {
Node transform = attributes.getNamedItem("transform");
System.out.print("transform: " + transform.getNodeValue() + "\n");
String matrix = "matrix(" + String.valueOf(-1. * y) + "," + String.valueOf(x) +
"," + String.valueOf(-1. * x) + "," + String.valueOf(-1. * y) + ",0,0)";
System.out.println(matrix);
transform.setNodeValue(matrix);
}
if(id.getNodeValue().equals("gConrod1") == true) { // Pleuel
Node transform = attributes.getNamedItem("transform");
System.out.print("transform: " + transform.getNodeValue() + "\n");
String matrix = "matrix(" + String.valueOf(alpha_y1) + "," + String.valueOf(alpha_x1) +
"," + String.valueOf(-1. * alpha_x1) + "," + String.valueOf(alpha_y1) + "," +
String.valueOf(Excentricity * x) + "," + String.valueOf(Excentricity * y) + ")";
System.out.println(matrix);
transform.setNodeValue(matrix);
}
if(id.getNodeValue().equals("gPiston1") == true) {
Node transform = attributes.getNamedItem("transform");
String yMove = String.format(Locale.US, "%.3f", yStroke + Length);
String translate = "translate(0," + yMove + ")";
transform.setNodeValue(translate);
}
if(id.getNodeValue().equals("gConrod2") == true) { // Pleuel
Node transform = attributes.getNamedItem("transform");
System.out.print("transform: " + transform.getNodeValue() + "\n");
String matrix = "matrix("
+ String.valueOf(alpha_x2) + "," + String.valueOf(alpha_y2) +
"," + String.valueOf(alpha_y2) + "," + String.valueOf(-1. *alpha_x2) + "," +
String.valueOf(Excentricity * x) + "," + String.valueOf(Excentricity * y) + ")";
System.out.println(matrix);
transform.setNodeValue(matrix);
}
if(id.getNodeValue().equals("gPiston2") == true) {
Node transform = attributes.getNamedItem("transform");
String xMove = String.format(Locale.US, "%.3f", xStroke + Length);
String translate = "translate(" + xMove + ",0)";
transform.setNodeValue(translate);
}
if(id.getNodeValue().equals("gCrankDrive") == true) {
Node transform = attributes.getNamedItem("transform");
System.out.print("transform: " + transform.getNodeValue() + "\n");
String matrix = "translate(" + String.valueOf(OffsetX) + "," + String.valueOf(OffsetY) + ")";
System.out.println(matrix);
transform.setNodeValue(matrix);
}
}
}
}
svg_to_png
@echo off
Echo Converting...
set start=%time%
echo %CD%
rmdir png /s /q
mkdir png
cd svg
for %%f in (*.svg) do (
call :convert %%f
)
echo Start time: %start%
echo End time: %time%
timeout /T -1
goto :EOF
:convert
echo %1
set ink="C:\Program Files (x86)\Inkscape\inkscape.exe"
set svg=%1
set png=%svg%
set png=%png:~0,-3%
set png=../png/%png%png
%ink% --file=%svg% --export-png=%png% --export-background=white --without-gui
rem convert %svg% -background white -flatten %png%
echo %png%
goto :EOF
cd ..
png_to_ogv
@echo off
echo Converting...
set start=%time%
echo %CD%
echo %temp%
call :convert
echo Start time: %start%
echo End time: %time%
timeout /T -1
goto :EOF
:convert
set png2yuv="%CD%\mjpeg_tools\bin\png2yuv.exe"
set framerate=30
set filename=stirling
%png2yuv% -j .\png\%filename%-%%04d.png -f %framerate% -I p -b 0 > %temp%\out.yuv
.\ffmpeg2theora-0.29.exe %temp%\out.yuv -F %framerate% -v 9 -o %filename%.ogv
del %temp%\out.yuv /q /s
goto :EOF
In dieser Datei abgebildete Objekte
Motiv
Einige Werte ohne einen Wikidata-Eintrag
10. Dezember 2014
application/ogg
d8f9d8b7e385af3fb929487410258b67a4f74b50
4.986.682 Byte
40,03333333333333 Sekunde
1.000 Pixel
1.000 Pixel
Dateiversionen
Klicke auf einen Zeitpunkt, um diese Version zu laden.
Version vom | Vorschaubild | Maße | Benutzer | Kommentar | |
---|---|---|---|---|---|
aktuell | 17:14, 10. Dez. 2014 | 40 s, 1.000 × 1.000 (4,76 MB) | Menner | update | |
15:05, 10. Dez. 2014 | 25 s, 1.000 × 1.000 (3,49 MB) | Menner | User created page with UploadWizard |
Dateiverwendung
Die folgende Seite verwendet diese Datei:
Metadaten
Diese Datei enthält weitere Informationen (beispielsweise Exif-Metadaten), die in der Regel von der Digitalkamera oder dem verwendeten Scanner stammen. Durch nachträgliche Bearbeitung der Originaldatei können einige Details verändert worden sein.
Software |
|
---|