File:Animation stirling engine.ogv
Original file (Ogg Theora video file, length 40 s, 1,000 × 1,000 pixels, 996 kbps, file size: 4.76 MB)
Captions
Captions
Summary
[edit]DescriptionAnimation stirling engine.ogv |
English: Animation of a stirling engine in alpha configuration running as a motor.A regenerator is included. |
Date | |
Source | Own work |
Author | Menner |
Licensing
[edit]![]() ![]() |
This file is made available under the Creative Commons CC0 1.0 Universal Public Domain Dedication. |
The person who associated a work with this deed has dedicated the work to the public domain by waiving all of their rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law. You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission.
http://creativecommons.org/publicdomain/zero/1.0/deed.enCC0Creative Commons Zero, Public Domain Dedicationfalsefalse |
Source code
[edit]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
[edit]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
[edit]@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
[edit]@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
File history
Click on a date/time to view the file as it appeared at that time.
Date/Time | Thumbnail | Dimensions | User | Comment | |
---|---|---|---|---|---|
current | 15:14, 10 December 2014 | 40 s, 1,000 × 1,000 (4.76 MB) | Menner (talk | contribs) | update | |
13:05, 10 December 2014 | 25 s, 1,000 × 1,000 (3.49 MB) | Menner (talk | contribs) | User created page with UploadWizard |
You cannot overwrite this file.
File usage on Commons
There are no pages that use this file.
Transcode status
Update transcode statusFile usage on other wikis
The following other wikis use this file:
- Usage on de.wikipedia.org
Metadata
This file contains additional information such as Exif metadata which may have been added by the digital camera, scanner, or software program used to create or digitize it. If the file has been modified from its original state, some details such as the timestamp may not fully reflect those of the original file. The timestamp is only as accurate as the clock in the camera, and it may be completely wrong.
Software used |
---|