1#!/usr/bin/env php
2#
3# this file takes the formulas in formula/definition and does a couple of things:
4# - generates the information boxes in the ui from the source code
5# - generates opencl formulas
6# - generates formula thumbnails and configs
7# - write a csv file which contains a table of all formulas
8#
9# requires packages: highlight, clang-format, git and php (apt-get install highlight clang-format git php5-cli)
10# clang-format is required in version 3.8.1, get executable from here: http://releases.llvm.org/download.html
11#
12# on default this script runs dry,
13# it will try to parse all formulas and show which ui files would be modified
14# this should always be run first, to see if any issues occur
15# if you invoke this script with "nondry" as cli argument it will write changes to ui files
16# if you invoke this script with "checkCl" as cli argument it will also check formula opencl file compilability (slow)
17#
18
19<?php
20require_once(dirname(__FILE__) . '/common.inc.php');
21printStart();
22
23printStartGroup('READING FORMULA DATA');
24$formulas = getFormulasData();
25$formulaExampleUsage = getFormulaExampleUsage();
26
27printStartGroup('RUNNING FORMULA CHECKS');
28foreach ($formulas as $index => $formula) {
29	@$i++;
30	$success = true;
31	$status = array();
32	if ($success) $success = updateInfoBoxes($index, $formula, $status);
33	if ($success) $success = generateFormulaOpenCLFiles($formula, $status);
34	if ($success) $success = generateFormulaIcons($formula, $status);
35	if ($success && argumentContains('checkCl')) $success = checkOpenCLCompile($formula, $status);
36	printResultLine($formula['nameInComboBox'], $success, $status, $i / count($formulas));
37}
38printEndGroup();
39if (!isDryRun()) writeFormulaCSV($formulas);
40if (!isDryRun()) writeParameterNamesTxt();
41if (!isDryRun()) writeFractalDefinitionFile($formulas);
42printFinish();
43exit;
44
45
46function getFormulasData()
47{
48	$formulas = array();
49	$indexIdLookup = getIndexIdLookUp();
50
51	foreach (glob(PROJECT_PATH . 'formula/definition/*.cpp') as $key => $file) {
52		if(strpos($file, 'abstract_fractal.cpp') !== false) continue;
53		if(strpos($file, 'all_fractal_list.cpp') !== false) continue;
54		if(strpos($file, 'legacy_fractal_transforrms.cpp') !== false) continue;
55		if(strpos($file, 'fractal_none.cpp') !== false) continue;
56
57		//echo 'Processing file ' . $file . PHP_EOL;
58
59		$fractalCppFile = file_get_contents($file);
60		// read index and name from fractal_list
61		preg_match('/cAbstractFractal\(\)\n{([\s\S]*?)\n}/', $fractalCppFile, $match);
62		$definitionPart = @$match[1];
63                $definitionPartLinesRaw = explode(PHP_EOL, trim($definitionPart));
64                // strip comment lines
65                $definitionPartLines = array_filter($definitionPartLinesRaw, function($l) {
66                    return !preg_match('/\s*\/\/.*/', $l);
67                });
68                if (count($definitionPartLines) != 9) die('could not read index for formula : ' . $file . ' --> ' . print_r($definitionPartLinesRaw, true));
69		$f = array();
70		foreach($definitionPartLines as $definitionPartLine){
71			if(!preg_match('/([a-zA-Z]+)\s*=\s*"?([\da-zA-Z\.: -_]*?)"?;/', $definitionPartLine, $matchLine))
72				die('Line wrong for ' . $file . ' --> ' . $definitionPartLine);
73			$f[$matchLine[1]] = $matchLine[2];
74		}
75		$index = str_replace('fractal::', '', $f['internalID']);
76
77		$internalNameNew = from_camel_case($index);
78
79		//echo $index . ' ' . $internalNameNew . PHP_EOL;
80
81		// check for automatic renaming to fit naming convention
82		if ($internalNameNew != $f['internalName']) {
83			if (!isDryRun()) {
84				upgradeInternalName($f['internalName'], $internalNameNew);
85			}
86			echo noticeString('internal name upgrade from ' . $f['internalName'] . ' to ' . $internalNameNew) . PHP_EOL;
87			$f['internalName'] = $internalNameNew;
88			if (!isDryRun()) die('Changes have been written, check changes and run script again for more changes.');
89		}
90
91		// read function contents
92		$functionContentMatchString = '/\+\+\+\n\s\*(\n[\s\S]+?\*\/)[\S\s]*(FormulaCode\([\s\S]*})/';
93
94		//echo $functionContentMatchString . PHP_EOL;
95
96		$functionContentFound = false;
97		$code = false;
98		$comment = false;
99		$rawComment = false;
100
101		if (preg_match($functionContentMatchString, $fractalCppFile, $matchFunctionContent)) {
102			$functionContentFound = true;
103			$rawComment = $matchFunctionContent[1];
104			$comment = parseComment(trim($matchFunctionContent[1]));
105			$code = $matchFunctionContent[2];
106			$code = trim(str_replace('FormulaCode', 'void ' . ucfirst($index) . 'Iteration', $code));
107		}
108
109		if (!$functionContentFound) {
110			echo errorString('Warning, could not read code for index: ' . $index) . PHP_EOL;
111			continue;
112			// die('could not read code for index: ' . $index);
113		}
114
115		$formulas[$index] = array_merge($f, array(
116			'uiFile' => PROJECT_PATH . 'formula/ui/' . $f['internalName'] . '.ui',
117			'definitionFileName' => basename($file),
118			'code' => $code,
119			'comment' => $comment,
120			'rawComment' => $rawComment,
121			'id' => $indexIdLookup[$index],
122			'openclFile' => PROJECT_PATH . 'formula/opencl/' . $f['internalName'] . '.cl',
123			'definitionFile' => PROJECT_PATH . 'formula/definition/fractal_' . $f['internalName'] . '.cpp',
124			'openclCode' => parseToOpenCL($code),
125			'type' => (strpos($f['internalName'], 'transf_') !== false ? 'transf' : 'formula'),
126		));
127		// print_r($formulas);
128	}
129	return $formulas;
130}
131
132// update information boxes in the ui
133function updateInfoBoxes($index, $formula, &$status)
134{
135  if($index == 'custom') return; // nothing to do for the custom opencl formula
136	global $formulaExampleUsage;
137	$formattedEscapedCode = getFormatCode($formula['code']);
138	// remove hardcoded font-size, to use system defined font-size
139	$formattedEscapedCode = str_replace('font-size:10pt;', '', $formattedEscapedCode);
140
141	// extract only body element, since html part and comment change between versions of highlight
142	$startCode = strpos($formattedEscapedCode, '<body');
143	$endCode = strpos($formattedEscapedCode, '</body>') + strlen('</body>');
144	$formattedEscapedCode = substr($formattedEscapedCode, $startCode, $endCode - $startCode);
145
146	$comment = $formula['comment'];
147	$informationText = '';
148	if (!empty($comment['description'])) {
149		$informationText = '<p>' . implode('<br>', $comment['description']) . '</p>';
150	}
151	$informationText .= "<table>" . PHP_EOL;
152	// $informationText .= "<tr><th>Name</th><td>" . $formula['nameInComboBox'] . "</td></tr>" . PHP_EOL;
153
154	if (!empty($comment['reference'])) {
155		$informationText .= '<tr><th>Reference</th><td>';
156		foreach ($comment['reference'] as $ref) {
157			$informationText .= "<a href=\"" . $ref . "\">" . $ref . "<br>" . PHP_EOL;
158		}
159		$informationText .= '</td></tr>' . PHP_EOL;
160	}
161	if (!empty($comment['author'])) {
162		$informationText .= '<tr><th>Author</th><td>';
163		foreach ($comment['author'] as $author) {
164			$informationText .= $author . "<br>" . PHP_EOL;
165		}
166		$informationText .= '</td></tr>' . PHP_EOL;
167	}
168	$informationText .= "</table>" . PHP_EOL;
169	if (array_key_exists($formula['id'], $formulaExampleUsage)) {
170		$informationText .= '<br><b>Examples using this formula</b><br>';
171		$exampleFilenames = $formulaExampleUsage[$formula['id']];
172		if (count($exampleFilenames) > 5) { // do not show more than 5 examples
173			array_splice($exampleFilenames, 5);
174			$exampleFilenames[] = '...';
175		}
176		$informationText .= implode('<br>', $exampleFilenames);
177	} else {
178		if (isWarning()) {
179			$status[] = warningString('formula ' . $formula['nameInComboBox'] . ' is not used in any examples yet.');
180		}
181	}
182
183	$informationText .= "<h3>Code</h3>" . PHP_EOL;
184
185	$uiFileContent = file_get_contents($formula['uiFile']);
186	$regexInformation = '/(<widget class="MyGroupBox" name="groupCheck_info">[\s\S]+?)<item>[\s\S]+?<\/layout>/';
187
188	$replacement = '$1<item>
189       <widget class="QLabel" name="label_information_general">
190        <property name="text">
191         <string notr="true">' . htmlentities($informationText) . '</string>
192        </property>
193        <property name="wordWrap">
194         <bool>true</bool>
195        </property>
196        <property name="openExternalLinks">
197         <bool>true</bool>
198        </property>
199        <property name="textInteractionFlags">
200         <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
201        </property>
202       </widget>
203      </item>
204      <item>
205       <widget class="QLabel" name="label_code_content">
206        <property name="styleSheet">
207         <string notr="true">border-style: outset; border-width: 2px; border-radius: 3px; border-color: black; background-color: #fff5ee; padding: 4px;</string>
208        </property>
209        <property name="text">
210         <string notr="true">' . htmlentities($formattedEscapedCode) . '</string>
211        </property>
212        <property name="wordWrap">
213         <bool>true</bool>
214        </property>
215        <property name="openExternalLinks">
216         <bool>true</bool>
217        </property>
218        <property name="textInteractionFlags">
219         <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
220        </property>
221       </widget>
222      </item>
223     </layout>';
224	$newUiFileContent = preg_replace($regexInformation, $replacement, $uiFileContent, -1, $count);
225	$newUiFileContent = preg_replace('/<class>.+<\/class>/', '<class>' . $formula['internalName'] . '</class>', $newUiFileContent, 1);
226	$postitionOfWindowTitle = strpos($newUiFileContent, 'windowTitle ');
227	$postitionOfWindowTitleStringStart = strpos($newUiFileContent, '<string', $postitionOfWindowTitle);
228	$postitionOfWindowTitleStringEnd = strpos($newUiFileContent, '</string>', $postitionOfWindowTitle) + strlen('</string>');
229	$newUiFileContent = substr_replace($newUiFileContent, '<string notr="true">' . $formula['internalName'] . '</string>',
230		$postitionOfWindowTitleStringStart, $postitionOfWindowTitleStringEnd - $postitionOfWindowTitleStringStart);
231	if ($count == 0) {
232		$status[] = errorString('Warning, could not replace code in ui file for index: ' . $index) . PHP_EOL;
233		return false;
234	}
235
236	$replaceFormulaLookup = array(
237		array('find' => '/\>\s*([^<]+?)\s*<\/string>/', 'replace' => '>$1</string>'), // whitespace fix 1
238		array('find' => '/\>\s*([^<]+?)\s*:\s*<\/string>/', 'replace' => '>$1:</string>'), // whitespace fix 2
239		array('find' => '/<string>(.*?)\s+(\s.*?)<\/string>/', 'replace' => '<string>$1$2</string>'), // whitespace fix 3, stop those whitespaces! :)
240		array('find' => '/<string>fabs\s*\(\s*(.*?)\s*\)\s*<\/string>/', 'replace' => '<string>abs($1)</string>'),
241		array('find' => '/<string>abs\s*\(\s*(.*?)\s*\)\s*<\/string>/', 'replace' => '<string>abs($1)</string>'),
242		array('find' => '/<string>(.*?)::(.*?)<\/string>/', 'replace' => '<string>$1:$2</string>'),
243		array('find' => '/<string>Menger Scale<\/string>/', 'replace' => '<string>Menger Scale:</string>'),
244		array('find' => '/<string>Menger offsets:<\/string>/', 'replace' => '<string>Menger Offset:</string>'),
245		array('find' => '/<string>DE tweak temp<\/string>/', 'replace' => '<string>DE tweak temp:</string>'),
246		array('find' => '/<string>Offset<\/string>/', 'replace' => '<string>Offset:</string>'),
247		array('find' => '/Rotation;<\/string>/', 'replace' => 'Rotation</string>'),
248		array('find' => '/>Multiplier<\/string>/', 'replace' => '>Multiplier:</string>'),
249		array('find' => '/>Cpixel Multiplier<\/string>/', 'replace' => '>Cpixel Multiplier:</string>'),
250		array('find' => '/>Addition Constant<\/string>/', 'replace' => '>Addition Constant:</string>'),
251		array('find' => '/>DE Tweak<\/string>/', 'replace' => '>DE Tweak:</string>'),
252		array('find' => '/>Post_scale<\/string>/', 'replace' => '>Post Scale:</string>'),
253		array('find' => '/>Pre_scale<\/string>/', 'replace' => '>Pre Scale:</string>'),
254		array('find' => '/class="QCheckBox"/', 'replace' => 'class="MyCheckBox"'),
255	);
256	foreach ($replaceFormulaLookup as $item) {
257		$newUiFileContent = preg_replace($item['find'], $item['replace'], $newUiFileContent);
258	}
259
260	// untranslatable and remove double point
261	$notrRemoveDoublePointCI = array(
262		'z\.x', 'z\.y', 'z\.z', 'z\.w',
263		'c\.x', 'c\.y', 'c\.z', 'c\.w',
264		'xyz', 'xzy', 'yxz', 'yzx', 'zxy', 'zyx',
265		'x', 'y', 'z', 'w',
266		'xy', 'yx', 'xz', 'zx', 'yz', 'zy',
267		'xw', 'yw', 'zw',
268		'alpha', 'beta', 'gamma',
269		'fabs', 'fabs\(z\.x\)', 'fabs\(z\.y\)', 'fabs\(z\.z\)', 'fabs\(z\)',
270		'abs', 'abs\(x\)', 'abs\(y\)', 'abs\(z\)',
271		'T1', 'T1mod', 'T2', 'T3', 'T4', 'T5b',
272		'asin', 'acos', 'atan', 'atan2',
273	);
274
275	foreach ($notrRemoveDoublePointCI as $item) {
276		$newUiFileContent = preg_replace('/<string>' . $item . ':*;*<\/string>/i',
277			'<string notr="true">' . str_replace('\\', '', $item) . '</string>', $newUiFileContent);
278	}
279
280	// simple find and replace whole string
281	$simpleReplace = array(
282		array('find' => 'Type 4 Fold Value', 'replace' => 'Type 4 Fold value:'),
283		array('find' => 'Type 5 Fold2 Value', 'replace' => 'Type 5 Fold2 value:'),
284	);
285
286	foreach ($simpleReplace as $item) {
287		$newUiFileContent = str_replace($item['find'], $item['replace'], $newUiFileContent);
288	}
289
290	if ($newUiFileContent == $uiFileContent) {
291		return true;
292	}
293	if (!isDryRun()) {
294		file_put_contents($formula['uiFile'], $newUiFileContent);
295	}
296	$status[] = noticeString('ui file changed'); // . (' . basename($formula['uiFile']) . ')
297	return true;
298}
299
300// generate opencl formulas
301function generateFormulaOpenCLFiles($formula, &$status)
302{
303	// TODO add primitives
304	$modInterval = getModificationInterval($formula['openclFile'], true);
305	$fileHeader = '/**
306 * Mandelbulber v2, a 3D fractal generator  _%}}i*<.        ____                _______
307 * Copyright (C) ' . $modInterval . ' Mandelbulber Team   _>]|=||i=i<,     / __ \___  ___ ___  / ___/ /
308 *                                        \><||i|=>>%)    / /_/ / _ \/ -_) _ \/ /__/ /__
309 * This file is part of Mandelbulber.     )<=i=]=|=i<>    \____/ .__/\__/_//_/\___/____/
310 * The project is licensed under GPLv3,   -<>>=|><|||`        /_/
311 * see also COPYING file in this folder.    ~+{i%+++
312 * ' . str_replace(array('/**', '*/'), '', $formula['rawComment']) . '
313 * This file has been autogenerated by tools/populateUiInformation.php
314 * from the file "' . $formula['definitionFileName'] . '" in the folder formula/definition
315 * D O    N O T    E D I T    T H I S    F I L E !
316 */' . PHP_EOL;
317
318	$openclContent = @file_get_contents($formula['openclFile']);
319	$newOpenCLContent = $fileHeader . PHP_EOL;
320	$newOpenCLContent .= $formula['openclCode'];
321
322	// clang-format
323	$filepathTemp = PROJECT_PATH . '/tools/.tmp.c';
324	file_put_contents($filepathTemp, $newOpenCLContent);
325	shell_exec(CLANG_FORMAT_EXEC_PATH . ' -i --style=file ' . escapeshellarg($filepathTemp));
326	$newOpenCLContent = file_get_contents($filepathTemp);
327	unlink($filepathTemp); // nothing to see here :)
328
329	$newOpenCLContentWithoutDateLine = preg_replace('/Copyright\s\(C\)\s\d+/', '', $newOpenCLContent);
330	$openclContentWithoutDateLine = preg_replace('/Copyright\s\(C\)\s\d+/', '', $openclContent);
331
332	if ($newOpenCLContentWithoutDateLine == $openclContentWithoutDateLine) {
333		return true;
334	}
335	if (!isDryRun()) {
336		file_put_contents($formula['openclFile'], $newOpenCLContent);
337		// file_put_contents($formula['openclFile'] . '.orig', $formula['code']);
338	}
339	$status[] = noticeString('opencl file changed'); // (' . basename($formula['openclFile']) . ')
340	return true;
341}
342
343// generate formula icons
344function generateFormulaIcons($formula, &$status)
345{
346	// autogenerate missing formula and transform images
347	$imgPath = PROJECT_PATH . 'formula/img/' . $formula['internalName'] . '.png';
348	if (file_exists($imgPath)) {
349		return true;
350	}
351
352	if (!isDryRun()) {
353		if (generate_formula_icon($formula, $imgPath)) {
354			$status[] = successString('image generated.');
355			return true;
356		} else {
357			$status[] = noticeString('image not generated. Maybe Mandelbulber not found?');
358			return true;
359		}
360	} else {
361		$status[] = noticeString('image does not exist');
362		return true;
363	}
364}
365
366function checkOpenCLCompile($formula, &$status)
367{
368	$checkOpenCLCompileCmd = 'clang -c -S -emit-llvm -o test.ll -w -include clc/clc.h -Dcl_clang_storage_class_specifiers -x cl';
369	$checkOpenCLCompileCmd .= '  -include ' . PROJECT_PATH . 'opencl/cl_kernel_include_headers.h';
370	$checkOpenCLCompileCmd .= '  -o /dev/null'; // -S -emit-llvm
371	$checkOpenCLCompileCmd .= ' -DOPENCL_KERNEL_CODE -I' . PROJECT_PATH . 'opencl/';
372	$checkOpenCLCompileCmd .= ' ' . $formula['openclFile'] . ' 2>&1';
373	exec($checkOpenCLCompileCmd, $output, $ret);
374	if ($ret != 0) {
375		$status[] = errorString('formula opencl file broken! ' . (!isVerbose() ? 'see error with verbose mode' : ''));
376		if (isVerbose()) $status[] = print_r($output);
377		return false;
378	}
379	return true;
380}
381
382function getFormatCode($code)
383{
384	$cmd = "echo " . escapeshellarg($code);
385	$cmd .= " | sed 's/	/  /g' "; // replace tab with double space
386	$cmd .= " | highlight -O html --style seashell --inline-css --syntax cpp";
387	return shell_exec($cmd);
388}
389
390function parseComment($c)
391{
392	$lines = explode(PHP_EOL, $c);
393	$out = array();
394	$tag = 'description';
395	foreach ($lines as $l) {
396		$line = trim($l);
397		if (in_array($line, array('/**', '*/'))) continue;
398		if (preg_match("/\*[\s]+@([\S]+)[\s]*(.*)/", $line, $match)) {
399			$tag = $match[1];
400			if (!array_key_exists($tag, $out)) $out[$tag] = array();
401			$text = trim($match[2]);
402			if ($text != '' || count($out[$tag]) > 0) {
403				$out[$tag][] = $match[2];
404			}
405		} else if (preg_match("/\*[\s]+(.*)/", $line, $match)) {
406			$out[$tag][] = $match[1];
407		} else {
408			$out[$tag][] = '';
409		}
410	}
411	return $out;
412}
413
414function getFormulaExampleUsage()
415{
416	// get a lookup for every formula in which example files it is used
417	$exampleBasePath = PROJECT_PATH . 'deploy/share/mandelbulber2/examples/';
418	$exampleFileMasks[] = $exampleBasePath . '*.fract';
419	$exampleFileMasks[] = $exampleBasePath . '*/*.fract';
420	$formulaExampleUsage = array();
421	foreach ($exampleFileMasks as $exampleFileMask) {
422		foreach (glob($exampleFileMask) as $exampleFileName) {
423			$exampleContent = file_get_contents($exampleFileName);
424			$exampleContentLines = explode(PHP_EOL, $exampleContent);
425			$pathOfFileFromExamples = str_replace($exampleBasePath, '', $exampleFileName);
426
427			foreach ($exampleContentLines as $line) {
428				$line = trim($line);
429				if (preg_match("/^formula_\d+\s(\d+);$/", $line, $match)) {
430					if (!array_key_exists($match[1], $formulaExampleUsage)) $formulaExampleUsage[$match[1]] = array();
431					if (!in_array($pathOfFileFromExamples, $formulaExampleUsage[$match[1]])) {
432						$formulaExampleUsage[$match[1]][] = $pathOfFileFromExamples;
433					}
434				}
435			}
436		}
437	}
438	return $formulaExampleUsage;
439}
440
441function getIndexIdLookUp()
442{
443	// get the integer id from fractal_list.hpp for the named index of each formula
444	$fractalListHeaderContent = file_get_contents(PROJECT_PATH . 'formula/definition/all_fractal_list_enums.hpp');
445	$indexIdLookUp = array();
446	$fractalListHeaderContentLines = explode(PHP_EOL, $fractalListHeaderContent);
447	foreach ($fractalListHeaderContentLines as $line) {
448		$line = trim($line);
449		if (preg_match("/^(\w+)\s=\s(\d+),.*$/", $line, $match)) {
450			$indexIdLookUp[$match[1]] = $match[2];
451		}
452	}
453	return $indexIdLookUp;
454}
455
456function parseToOpenCL($code, $mode = 'single')
457{
458	// $fod = $mode == 'single' ? 'float' : 'double';
459	$fod = 'REAL';
460	$var = '[A-Za-z_][A-Za-z0-9\.\-\>_\[\]]*'; // regex for a var name
461	$functionName = '[A-Za-z_][A-Za-z0-9\.\-\>_]*'; // regex for a function name
462	$double = '(?:-?\d+\.?\d*(?:[eE][+-]?\d+)?|-?\d*\.?\d+(?:[eE][+-]?\d+)?)'; // regex for a double value
463	$float = $double . 'f'; // regex for a float value
464	$nb = "[^()]*"; // matches everything but braces
465	$br = "-?(?:$functionName)?\($nb\)"; // regex for a simply braced expression with optional function invocation
466	$br1_1 = "\($nb$br$nb\)"; // regex for a double braced expression (one inner brace)
467	$br1_2 = "\($nb$br$nb$br$nb\)"; // regex for a double braced expression (two inner braces)
468	$s = '[\n\r\s]+'; // any whitespace including new lines
469	$all = '[\S\s]+?'; // anything including new lines as few as possible (mark end with next operator)
470	$rval = "$br1_2|$br1_1|$br|$float|-?$var"; // any of those types can be an "assignable expression"
471	$preF = "\s|\(|\{|-"; // these chars can occur befire a function
472	$multChain = "(?:(?:$rval)$s\*$s)*(?:$rval)"; // a chain of multiplicated expressions
473	// see here for all possible: https://www.khronos.org/registry/cl/sdk/1.0/docs/man/xhtml/mathFunctions.html
474	$cppToOpenCLReplaceLookup = array(
475		array('find' => '/double/', 'replace' => $fod),                // all doubles to floats
476		array('find' => "/([\s\(\{])($double)([\s\)\};,])/", 'replace' => '$1$2' . ($mode == 'single' ? 'f' : '') . '$3'),    // double literals to float literals
477		array('find' => "/($preF)sin\(/", 'replace' => '$1native_sin('),          // native sin
478		array('find' => "/($preF)cos\(/", 'replace' => '$1native_cos('),          // native cos
479		array('find' => "/($preF)pow\(/", 'replace' => '$1native_powr('),         // native pow
480		array('find' => "/($preF)sqrt\(/", 'replace' => '$1native_sqrt('),        // native sqrt
481		array('find' => "/CVector3\(($multChain,$s$multChain,$s$multChain)\)/", 'replace' => '(' . $fod . '3) {$1}'),  // CVector3 to built in float3
482		array('find' => "/CVector3\(\)/", 'replace' => '(' . $fod . '3) {0, 0, 0}'),  // CVector3 default constructor to built in float3
483		array('find' => "/CVector3\(($all)\);/", 'replace' => '(' . $fod . '3) {$1};'),  // CVector3 to built in float3
484		array('find' => "/CVector3(\s)/", 'replace' => $fod . '3$1'),               // CVector3 to built in float3
485		array('find' => "/CVector4\(\)/", 'replace' => '(' . $fod . '4) {0, 0, 0, 0}'),  // CVector3 default constructor to built in float3
486		array('find' => "/CVector4\(($multChain),($s$multChain)\)/", 'replace' => '(' . $fod . '4) {$1.x,$1.y,$1.z,$2}'),  // CVector4 construct by CVector3 and float to built in construct
487		array('find' => "/CVector4\(($multChain,$s$multChain,$s$multChain,$s$multChain)\)/", 'replace' => '(' . $fod . '4) {$1}'),  // CVector4 to built in float4
488		array('find' => "/CVector4\(($all)\);/", 'replace' => '(' . $fod . '4) {$1};'),  // CVector4 to built in float4
489		array('find' => "/CVector4(\s)/", 'replace' => $fod . '4$1'),               // CVector4 to built in float4
490		array('find' => "/($var)\.Length\(\)/", 'replace' => 'length($1)'),       // CVector3 Length() to built in length
491		array('find' => "/($var)\.Dot\(/", 'replace' => 'dot($1, '),              // CVector3 Dot() to built in dot
492		array('find' => "/($var)\.Cross\(/", 'replace' => 'cross($1, '),          // CVector3 Cross() to built in cross
493		array('find' => "/($var)\.RotateVector\(/", 'replace' => 'Matrix33MulFloat4($1, '), // CRotationMatrix33 to custom rotation function
494		array('find' => "/($var)\.RotateX\(/", 'replace' => '$1 = RotateX($1, '), // CRotationMatrix33 to custom rotation function
495		array('find' => "/($var)\.RotateY\(/", 'replace' => '$1 = RotateY($1, '), // CRotationMatrix33 to custom rotation function
496		array('find' => "/($var)\.RotateZ\(/", 'replace' => '$1 = RotateZ($1, '), // CRotationMatrix33 to custom rotation function
497		array('find' => "/($var)\.RotateAroundVectorByAngle\(/", 'replace' => 'RotateAroundVectorByAngle4($1, '), // CVector3 to custom rotation function
498		array('find' => "/($var)\.GetXYZ\(\)/", 'replace' => '$1.xyz'), // CVector4 getxyz to native accessor of float4
499		array('find' => "/CRotationMatrix$s($var);/", 'replace' => 'matrix33 $1;' . PHP_EOL . '$1.m1 = (float3){1.0f, 0.0f, 0.0f};' . PHP_EOL . '$1.m2 = (float3){0.0f, 1.0f, 0.0f};' . PHP_EOL . '$1.m3 = (float3){0.0f, 0.0f, 1.0f};'), // CRotationMatrix33 to matrix33
500		array('find' => "/swap\(($var),\s($var)\);/", 'replace' => '{ ' . $fod . ' temp = $1; $1 = $2; $2 = temp; }'),// swap vals
501		array('find' => "/($s|\()(-?\d+)f($s|;|\))/", 'replace' => '$1$2$3'),          // int vals should not have a "f" at the end
502		// array('find' => "/sign\(($rval)\)$s\*$s($multChain)/", 'replace' => 'copysign($2, $1)'),// sign(x) * y => copysign(y, x) (this is wrong! probably copysign not usable at all)
503
504		// from here on its getting messy
505		//array('find' => "/1.0f$s\/$s($rval)/", 'replace' => 'native_recip($1)'),        // native reciprocal
506		//array('find' => "/($rval)$s\/$s($rval)/", 'replace' => 'native_divide($1, $2)'),        // native division
507		array('find' => "/($preF)native_recip\(native_sqrt($rval)\)/", 'replace' => '$1native_rsqrt$2'),        // native reciprocal sqrt
508		//array('find' => "/($preF)native_sqrt\(native_recip($rval)\)/", 'replace' => '$1native_rsqrt$2'),        // native reciprocal sqrt
509
510		// mad (literally ;D )
511		//array('find' => "/\(($multChain)$s\*$s($rval)$s\+$s($multChain)\)/", 'replace' => '(mad($1, $2, $3))'),    // (a * b + c) ====> mad(a, b, c)
512		//array('find' => "/\(($multChain)$s\*$s($rval)$s\-$s($multChain)\)/", 'replace' => '(mad($1, $2, -$3))'),    // (a * b - c) ====> mad(a, b, -c)
513		//array('find' => "/([^*-]$s)($multChain)$s\*$s($rval)$s\+$s($multChain)(${'s'}[^*]|;)/", 'replace' => '$1mad($2, $3, $4)$5'), // a * b + c ====> mad(a, b, c)
514		//array('find' => "/([^*-]$s)($multChain)$s\*$s($rval)$s\-$s($multChain)(${'s'}[^*]|;)/", 'replace' => '$1mad($2, $3, -$4)$5'), // a * b - c ====> mad(a, b, -c)
515		//array('find' => "/\(($multChain)$s\+$s($rval)$s\*$s($multChain)\)/", 'replace' => '(mad($2, $3, $1))'),    // (c + a * b) ====> mad(a, b, c)
516		//array('find' => "/\(($multChain)$s\-$s($rval)$s\*$s($multChain)\)/", 'replace' => '(mad(-$2, $3, $1))'),    // (c - a * b) ====> mad(-a, b, c)
517		//array('find' => "/([^*-]$s)($multChain)$s\+$s($multChain)$s\*$s($rval)(${'s'}[^*]|;)/", 'replace' => '$1mad($4, $3, $2)$5'), // a * b + c ====> mad(a, b, c)
518		//array('find' => "/([^*-]$s)($multChain)$s\-$s($multChain)$s\*$s($rval)(${'s'}[^*]|;)/", 'replace' => '$1mad(-$4, $3, $2)$5'), // c - a * b ====> mad(-a, b, c)
519
520		// formula specific replacements
521		array('find' => "/^void(\s)/", 'replace' => $fod . '4 $1'), // mark void with inline void
522		array('find' => "/" . $fod . "4 &z/", 'replace' => $fod . '4 z'), // no passing by reference
523		//array('find' => "/" . $fod . " \&w/", 'replace' => $fod . ' *w'), // no passing by reference
524		//array('find' => "/z\./", 'replace' => 'z->'),
525		array('find' => "/" . $fod . "4 &z4D/", 'replace' => $fod . '4 *z4D'), // no passing by reference
526		array('find' => "/z4D\./", 'replace' => 'z4D->'),
527		array('find' => "/sExtendedAux &aux/", 'replace' => 'sExtendedAuxCl *aux'), // no passing by reference
528		array('find' => "/const sFractal \*fractal/", 'replace' => '__constant sFractalCl *fractal'), // no passing by reference
529		array('find' => "/aux\./", 'replace' => 'aux->'),
530		array('find' => "/const(\s\w+\s\*)/", 'replace' => '__constant$1'), // const modifier allowed for pointers (vars from outside)
531		array('find' => "/const(\s)/", 'replace' => '$1'), // const modifier not allowed for local vars
532		//array('find' => "/(\s)z\s=/", 'replace' => '$1*z ='), // z to pointer
533		//array('find' => "/(\s)z\s(.)=/", 'replace' => '$1*z $2='), // z to pointer
534		//array('find' => "/([\s\(-])z([,\);\s}])/", 'replace' => '$1*z$2'), // z to pointer
535		array('find' => "/(\s)z4D\s=/", 'replace' => '$1*z4D ='), // z4D to pointer
536		array('find' => "/(\s)z4D\s(.)=/", 'replace' => '$1*z4D $2='), // z4D to pointer
537		array('find' => "/([\s\(-])z4D([,\);\s}])/", 'replace' => '$1*z4D$2'), // z4D to pointer
538		//array('find' => "/(\s)w\s=/", 'replace' => '$1*w ='), // w to pointer
539		//array('find' => "/(\s)w\s(.)=/", 'replace' => '$1*w $2='), // w to pointer
540		//array('find' => "/([\s\(-])w([,\);\s}])/", 'replace' => '$1*w$2'), // w to pointer
541		array('find' => "/case ([a-zA-Z]+[a-zA-Z0-9_]+?[^l])(_[a-zA-Z0-9]+):/", 'replace' => 'case $1Cl$2:'), // replace enum switch cases with cl version
542		array('find' => "/== ([a-zA-Z]+[a-zA-Z0-9_]+?[^l])(_[a-zA-Z0-9]+)\)/", 'replace' => '== $1Cl$2)'), // replace enum if comparison with cl version
543		array('find' => "/($s)(enum[a-zA-Z0-9_]+?[^l])($s)/", 'replace' => '$1$2Cl$3'), // replace enum definitions with cl version
544		array('find' => "/M_PI([\s\)\};,])/", 'replace' => ($mode == 'single' ? 'M_PI_F$1' : 'M_PI$1')), // replace Math constant
545		array('find' => "/M_PI_180([\s\)\};,])/", 'replace' => ($mode == 'single' ? 'M_PI_180_F$1' : 'M_PI_180$1')), // replace Math constant
546		array('find' => "/M_PI_8([\s\)\};,])/", 'replace' => ($mode == 'single' ? 'M_PI_8_F$1' : 'M_PI_8$1')), // replace Math constant
547		array('find' => "/M_PI_4([\s\)\};,])/", 'replace' => ($mode == 'single' ? 'M_PI_4_F$1' : 'M_PI_4$1')), // replace Math constant
548		array('find' => "/M_PI_2x([\s\)\};,])/", 'replace' => ($mode == 'single' ? 'M_PI_2x_F$1' : 'M_PI_2x$1')), // replace Math constant
549		array('find' => "/SQRT_1_3([\s\)\};,])/", 'replace' => ($mode == 'single' ? 'SQRT_1_3_F$1' : 'SQRT_1_3$1')), // replace Math constant
550		array('find' => "/SQRT_1_2([\s\)\};,])/", 'replace' => ($mode == 'single' ? 'SQRT_1_2_F$1' : 'SQRT_1_2$1')), // replace Math constant
551		array('find' => "/SQRT_2_3([\s\)\};,])/", 'replace' => ($mode == 'single' ? 'SQRT_2_3_F$1' : 'SQRT_2_3$1')), // replace Math constant
552		array('find' => "/SQRT_3_2([\s\)\};,])/", 'replace' => ($mode == 'single' ? 'SQRT_3_2_F$1' : 'SQRT_3_2$1')), // replace Math constant
553		array('find' => "/SQRT_3_4([\s\)\};,])/", 'replace' => ($mode == 'single' ? 'SQRT_3_4_F$1' : 'SQRT_3_4$1')), // replace Math constant
554		array('find' => "/SQRT_3_4d2([\s\)\};,])/", 'replace' => ($mode == 'single' ? 'SQRT_3_4d2_F$1' : 'SQRT_3_4d2$1')), // replace Math constant
555		array('find' => "/SQRT_3_4d2([\s\)\};,])/", 'replace' => ($mode == 'single' ? 'SQRT_3_4d2_F$1' : 'SQRT_3_4d2$1')), // replace Math constant
556		array('find' => "/SQRT_3([\s\)\};,])/", 'replace' => ($mode == 'single' ? 'SQRT_3_F$1' : 'SQRT_3$1')), // replace Math constant
557		array('find' => "/FRAC_1_3([\s\)\};,])/", 'replace' => ($mode == 'single' ? 'FRAC_1_3_F$1' : 'FRAC_1_3$1')), // replace Math constant
558
559
560		array('find' => "/1e-061f/", 'replace' => ($mode == 'single' ? '1e-030f' : '1e-061')), // replace minimal double constant
561		array('find' => "/1e-021f/", 'replace' => ($mode == 'single' ? '1e-006f' : '1e-021')), // replace minimal double constant
562		array('find' => "/reinterpret_cast<(.*?)>\((.*?)\)/", 'replace' => '($1)$2'), // replace reinterpret_cast with simple cast
563		// TODO more replacements
564	);
565
566	foreach ($cppToOpenCLReplaceLookup as $item) {
567		$code = preg_replace($item['find'], $item['replace'], $code);
568		$code = preg_replace($item['find'], $item['replace'], $code); // regex sometimes overlap, so run twice!
569	}
570	$code = substr($code, 0, -1) . 'return z;' . PHP_EOL . '}';
571	return $code;
572}
573
574function generate_formula_icon($formula, $imgPath)
575{
576	$formulaId = $formula['id'];
577	if ($formula['type'] == 'transf') {
578		$settings = '# Mandelbulber settings file
579# version 2.09
580# only modified parameters
581[main_parameters]
582ambient_occlusion_enabled true;
583camera 1,079630935663999 -2,590704819137228 1,163428502911517;
584camera_distance_to_target 7,000000000000004;
585camera_rotation 23,56505117707793 -24,60154959902026 0;
586camera_top -0,1664347300812756 0,3815883285622319 0,9092248501486611;
587flight_last_to_render 0;
588formula_1 7;
589formula_2 ' . $formulaId . ';
590hdr true;
591hybrid_fractal_enable true;
592image_height 256;
593image_width 256;
594keyframe_last_to_render 0;
595target -1,464862100002681 3,2431066939054 -1,750709177730066;
596[fractal_1]
597[fractal_2]
598info true;
599mandelbox_folding_limit -0,5;
600mandelbox_folding_value 0;
601transf_addition_constant_0000 0,1 -0,5 0,1 -0,2;
602transf_addition_constant_111 0,135135 1 1;
603transf_function_enabledAz_false true;
604transf_int_A 5;
605transf_int_B 5;
606transf_offset 0,7;
607transf_offset_0 0,8;';
608	} else {
609		$settings = '# Mandelbulber settings file
610# version 2.09
611# only modified parameters
612[main_parameters]
613ambient_occlusion_enabled true;
614camera 1,080915186153033 -1,903722297292515 1,202015809937754;
615camera_distance_to_target 6,277124159763113;
616camera_rotation 29,9681686749304 -29,10022837829406 0;
617camera_top -0,2429354017345652 0,421316840960071 0,8737702845184721;
618DE_factor 0,18197;
619flight_last_to_render 0;
620formula_1 ' . $formulaId . ';
621hdr true;
622hybrid_fractal_enable true;
623image_height 256;
624image_width 256;
625keyframe_last_to_render 0;
626mat1_coloring_palette_offset 1,6;
627target -1,65882778581033 2,847745976777177 -1,850793618305075;
628[fractal_1]
629transf_function_enabled_false true;
630[fractal_2]
631info true;
632mandelbox_folding_limit -0,5;
633mandelbox_folding_value 0;
634transf_addition_constant 1,935484 0 0;
635transf_addition_constant_0000 0,1 -0,5 0,1 -0,2;
636transf_constant_multiplier_111 2,934272 2,089201 1,877934;
637transf_function_enabledy_false true;
638transf_function_enabledz_false true;
639transf_int_A 5;
640transf_int_B 5;
641transf_offset 0,7;
642transf_offset_0 0,8;
643transf_scale_0 0,034722;
644transf_scale_2 1,079812;';
645	}
646	$tempFractPath = PROJECT_PATH . 'formula/img/' . $formula['internalName'] . '.fract';
647
648	if (!file_exists($tempFractPath)) { // allow manual override
649		file_put_contents($tempFractPath, $settings);
650	}
651	if (!file_exists(MANDELBULBER_EXEC_PATH)) return false;
652        $cmd = MANDELBULBER_EXEC_PATH . " -n -f png16alpha -O 'opencl_enabled=0' -o '" . $imgPath . "' '" . $tempFractPath . "'";
653        // echo PHP_EOL . $cmd . PHP_EOL;
654	shell_exec($cmd);
655	shell_exec("convert '" . $imgPath . "' -depth 8 '" . $imgPath . "'"); // save disk space with 8-bit png
656	return true;
657}
658
659function upgradeInternalName($internalName, $internalNameNew)
660{
661        if (file_exists(PROJECT_PATH . 'formula/ui/' . $internalName . '.ui')) {
662            shell_exec('git mv'
663                    . ' \'' . PROJECT_PATH . 'formula/ui/' . $internalName . '.ui\''
664                    . ' \'' . PROJECT_PATH . 'formula/ui/' . $internalNameNew . '.ui\''
665            );
666        }
667        if (file_exists(PROJECT_PATH . 'formula/img/' . $internalName . '.png')) {
668            shell_exec('git mv'
669                    . ' \'' . PROJECT_PATH . 'formula/img/' . $internalName . '.png\''
670                    . ' \'' . PROJECT_PATH . 'formula/img/' . $internalNameNew . '.png\''
671            );
672        }
673        if (file_exists(PROJECT_PATH . 'formula/definition/fractal_' . $internalName . '.cpp')) {
674            shell_exec('git mv'
675                    . ' \'' . PROJECT_PATH . 'formula/definition/fractal_' . $internalName . '.cpp\''
676                    . ' \'' . PROJECT_PATH . 'formula/definition/fractal_' . $internalNameNew . '.cpp\''
677            );
678        }
679        if (file_exists(PROJECT_PATH . 'formula/opencl/' . $internalName . '.cl')) {
680            shell_exec('git mv'
681                    . ' \'' . PROJECT_PATH . 'formula/opencl/' . $internalName . '.cl\''
682                    . ' \'' . PROJECT_PATH . 'formula/opencl/' . $internalNameNew . '.cl\''
683            );
684        }
685	if (file_exists(PROJECT_PATH . 'formula/img/' . $internalName . '.fract')) {
686		shell_exec('git mv'
687			. ' \'' . PROJECT_PATH . 'formula/img/' . $internalName . '.fract\''
688			. ' \'' . PROJECT_PATH . 'formula/img/' . $internalNameNew . '.fract\''
689		);
690	}
691        $fractal_list_content = file_get_contents(PROJECT_PATH . 'formula/definition/fractal_' . $internalNameNew . '.cpp');
692	$fractal_list_content = str_replace('"' . $internalName . '"', '"' . $internalNameNew . '"', $fractal_list_content);
693        file_put_contents(PROJECT_PATH . 'formula/definition/fractal_' . $internalNameNew . '.cpp', $fractal_list_content);
694}
695
696function upgradeFunctionName($functionName, $functionNameNew)
697{
698	$replaceInFiles = array(
699		PROJECT_PATH . 'src/compute_fractal.cpp',
700		PROJECT_PATH . 'src/fractal_formulas.cpp',
701		PROJECT_PATH . 'src/fractal_formulas.hpp',
702		PROJECT_PATH . 'src/fractal_list.cpp',
703	);
704	foreach ($replaceInFiles as $replaceInFile) {
705		$fileContent = file_get_contents($replaceInFile);
706		$fileContent = str_replace($functionName, $functionNameNew, $fileContent);
707		file_put_contents($replaceInFile, $fileContent);
708	}
709}
710
711function from_camel_case($input)
712{
713        $input = str_replace('dIFS', 'difs', $input); // explicitly keep those
714	preg_match_all('@([A-Z][A-Z0-9]*(?=$|[A-Z][a-z0-9])|[A-Za-z][a-z0-9]+)@', $input, $matches);
715	$ret = $matches[0];
716	foreach ($ret as &$match) {
717		$match = $match == strtoupper($match) ? strtolower($match) : lcfirst($match);
718	}
719	return implode('_', $ret);
720}
721
722function writeFormulaCSV($formulas){
723	$file = fopen(PROJECT_PATH . 'deploy/formulaData.csv', 'w');
724	fputcsv($file,
725    array(
726        'index',
727        'name',
728        'deType',
729        'deFunctionType',
730        'pixelAddition',
731        'defaultBailout',
732        'analyticFunction',
733        'coloringFunction',
734    ));
735	foreach ($formulas as $index => $formula) {
736	    $data = array(
737			$index,
738			$formula['nameInComboBox'],
739			// $formula['internalName'],
740			// $formula['functionName'],
741			$formula['DEType'],
742			$formula['DEFunctionType'],
743			$formula['cpixelAddition'],
744			$formula['defaultBailout'],
745			$formula['DEAnalyticFunction'],
746			$formula['coloringFunction'],
747        );
748
749    	fputcsv($file, $data);
750	}
751	fclose($file);
752}
753
754function writeParameterNamesTxt(){
755	$fractalCpp = file_get_contents(PROJECT_PATH . 'src/fractal.cpp');
756	preg_match_all('/(.*?)\s=[\s\n]+.*container->Get<(.*?)>\("(.*?)"(, i)?\).*;/', $fractalCpp, $matches);
757        $parameterNames = array();
758	foreach($matches[0] as $i => $unused){
759		$parameter = trim($matches[1][$i]);
760		if(strpos($parameter, '//') !== false) continue; // skip comments
761		$mainParameter = @explode('.', $parameter)[0];
762		switch($matches[2][$i]){
763			case 'double': $type = 'REAL'; break;
764			case 'CVector3': $type = 'REAL3'; break;
765			case 'CVector4': $type = 'REAL4'; break;
766			default: $type = $matches[2][$i];
767		}
768		$name = $matches[3][$i];
769		if(empty($matches[4][$i])){
770	        	$parameterNames[$mainParameter][] = $parameter . ' ' . $type . ' ' . $name;
771		}else{
772			if(strpos($parameter, 'mandelbox.rot') !== false){
773				$minI = 1;
774				$maxI = 4;
775			}
776			if(strpos($parameter, 'IFS') !== false){
777				$minI = 0;
778				$maxI = 9;
779			}
780			for($i = $minI; $i < $maxI; $i++){
781				$parameterRun = $parameter;
782				$parameterRun = str_replace('[i - 1]', '[' . ($i - 1) . ']', $parameterRun);
783				$parameterRun = str_replace('[i]', '[' . $i . ']', $parameterRun);
784				$typeRun = str_replace('REAL3', 'matrix33', $type);
785				$parameterNames[$mainParameter][] = $parameterRun . ' ' . $typeRun . ' ' . $name . '_' . $i;
786			}
787		}
788	}
789	$txtFile = '';
790	foreach($parameterNames as $parameterName => $parameterLines){
791		$txtFile .= implode(PHP_EOL, $parameterLines) . PHP_EOL . PHP_EOL;
792	}
793
794  // derived parameters
795  preg_match_all('/(.*\..*?)\s=[\s\n]([A-Za-z0-9.+*\/\s\(\)]*);/', $fractalCpp, $matches);
796  foreach($matches[0] as $i => $line){
797    if(strpos($line, '//') !== false || strpos($line, 'for (') !== false) continue; // skip comments and loops
798    $txtFile .= trim($matches[1][$i] . ' none none') . PHP_EOL;
799
800  }
801
802  $txtFile .= PHP_EOL;
803  $txtFile .= "IFS.mainRot matrix33 IFS_rotation" . PHP_EOL;
804  $txtFile .= "mandelbox.mainRot matrix33 mandelbox_rotation_main" . PHP_EOL;
805  $txtFile .= "transformCommon.rotationMatrix matrix33 transf_rotation" . PHP_EOL;
806  $txtFile .= "transformCommon.rotationMatrix2 matrix33 transf_rotation2" . PHP_EOL;
807
808	file_put_contents(PROJECT_PATH . 'deploy/share/mandelbulber2/data/parameterNames.txt', $txtFile);
809}
810
811function writeFractalDefinitionFile($formulas){
812	$out = '/**
813 * Mandelbulber v2, a 3D fractal generator  _%}}i*<.         ______
814 * Copyright (C) ' . date('Y') . ' Mandelbulber Team   _>]|=||i=i<,      / ____/ __    __
815 *                                        \><||i|=>>%)     / /   __/ /___/ /_
816 * This file is part of Mandelbulber.     )<=i=]=|=i<>    / /__ /_  __/_  __/
817 * The project is licensed under GPLv3,   -<>>=|><|||`    \____/ /_/   /_/
818 * see also COPYING file in this folder.    ~+{i%+++
819 */
820
821#ifndef MANDELBULBER2_FORMULA_DEFINITION_ALL_FRACTAL_DEFINITIONS_H_
822#define MANDELBULBER2_FORMULA_DEFINITION_ALL_FRACTAL_DEFINITIONS_H_
823
824#include "abstract_fractal.h"
825
826';
827	$out .= 'FRACTAL_CLASS(cFractalNone)' . PHP_EOL;
828	foreach ($formulas as $index => $formula) {
829
830		$out .= 'FRACTAL_CLASS(cFractal' . ucfirst($index) . ')' . PHP_EOL;
831	}
832	$out .= '
833#endif /* MANDELBULBER2_FORMULA_DEFINITION_ALL_FRACTAL_DEFINITIONS_H_ */
834';
835	file_put_contents(PROJECT_PATH . 'formula/definition/all_fractal_definitions.h', $out);
836
837	/*
838	$out = '//====================== FRACTAL LIST - START ==================' . PHP_EOL;
839	foreach ($formulas as $index => $formula) {
840		$out .= '	newFractalList.append(new cFractal' . ucfirst($index) . '());' . PHP_EOL;
841	}
842	$out .= '	//====================== FRACTAL LIST - END ==================';
843	*/
844	$regex = '/\/\/=* FRACTAL LIST - START =*[\s\S]*?\/\/=* FRACTAL LIST - END =*/';
845	/*
846	file_put_contents(PROJECT_PATH . 'formula/definition/all_fractal_list.cpp', preg_replace($regex, $out, file_get_contents(PROJECT_PATH . 'src/fractal_list.cpp')));
847    */
848}
849
850?>
851
852