לדלג לתוכן

קובץ:Animation stirling engine.ogv

תוכן הדף אינו נתמך בשפות אחרות.
מתוך ויקיפדיה, האנציקלופדיה החופשית

לקובץ המקורי(קובץ וידאו Theora של Ogg, באורך 40 שניות, 1,000 × 1,000 פיקסלים, 996 קילו־ביטים בשנייה, גודל הקובץ: 4.76 מ"ב)

ויקישיתוף זהו קובץ שמקורו במיזם ויקישיתוף. תיאורו בדף תיאור הקובץ המקורי (בעברית) מוצג למטה.

תקציר

תיאור
English: Animation of a stirling engine in alpha configuration running as a motor.A regenerator is included.
תאריך יצירה
מקור נוצר על־ידי מעלה היצירה
יוצר Menner

רישיון

אני, בעל זכויות היוצרים על עבודה זו, מפרסם בזאת את העבודה תחת הרישיון הבא:
Creative Commons CC-Zero קובץ זה זמין לפי תנאי הקדשה עולמית לנחלת הכלל CC0 1.0 של Creative Commons.
האדם ששייך יצירה להיתר הזה הקדיש את היצירה לנחלת הכלל על־ידי ויתור על כל הזכויות שלו או שלה על היצירה בכל העולם לפי חוק זכויות יוצרים, לרבות כל הזכויות הקשורות או הסמוכות כקבוע בחוק. באפשרותך להעתיק, לשנות, להפיץ, או להציג את היצירה, אפילו למטרות מסחריות, וכל זה אפילו מבלי לבקש רשות.

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

כיתובים

נא להוסיף משפט שמסביר מה הקובץ מייצג

פריטים שמוצגים בקובץ הזה

מוצג

application/ogg

checksum אנגלית

d8f9d8b7e385af3fb929487410258b67a4f74b50

הוגדר לפי: SHA-1 אנגלית

4,986,682 בית

40.03333333333333 שנייה

1,000 פיקסל

1,000 פיקסל

היסטוריית הקובץ

ניתן ללחוץ על תאריך/שעה כדי לראות את הקובץ כפי שנראה באותו זמן.

תאריך/שעהתמונה ממוזערתממדיםמשתמשהערה
נוכחית18:14, 10 בדצמבר 201440 שניות, 1,000 × 1,000 (4.76 מ"ב)Mennerupdate
16:05, 10 בדצמבר 201425 שניות, 1,000 × 1,000 (3.49 מ"ב)MennerUser created page with UploadWizard

אין בוויקיפדיה דפים המשתמשים בקובץ זה.

שימוש גלובלי בקובץ

אתרי הוויקי השונים הבאים משתמשים בקובץ זה:

מטא־נתונים