Datei:Animation stirling engine.ogv

Aus besserwiki.de

Originaldatei(Ogg-Theora-Videodatei, Länge: 40 s, 1.000×1.000 Pixel, 996 kbps insgesamt)

Diese Datei stammt aus Wikimedia Commons und kann von anderen Projekten verwendet werden. Die Beschreibung von deren Dateibeschreibungsseite wird unten angezeigt.

Beschreibung

Beschreibung
English: Animation of a stirling engine in alpha configuration running as a motor.A regenerator is included.
Datum
Quelle Eigenes Werk
Urheber Menner

Lizenz

Ich, der Urheber dieses Werkes, veröffentliche es unter der folgenden Lizenz:
Creative Commons CC-Zero 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.

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

Kurzbeschreibungen

Ergänze eine einzeilige Erklärung, was diese Datei darstellt.

In dieser Datei abgebildete Objekte

Motiv

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 vomVorschaubildMaßeBenutzerKommentar
aktuell16:14, 10. Dez. 201440 s, 1.000 × 1.000 (4,76 MB)wikimediacommons>Mennerupdate

Die folgende Seite verwendet diese Datei:

Metadaten