122 lines
4.3 KiB
Kotlin
122 lines
4.3 KiB
Kotlin
package org.openrndr.extra.fcurve
|
|
|
|
import org.openrndr.extra.expressions.FunctionExtensions
|
|
import org.openrndr.extra.expressions.evaluateExpression
|
|
|
|
/**
|
|
* expand mfcurve to fcurve
|
|
*/
|
|
fun mfcurve(
|
|
mf: String,
|
|
constants: Map<String, Double> = emptyMap(),
|
|
functions: FunctionExtensions = FunctionExtensions.EMPTY
|
|
): String {
|
|
/**
|
|
* perform comment substitution
|
|
*/
|
|
val stripped = Regex("(#.*)$", RegexOption.MULTILINE).replace(mf, "")
|
|
|
|
/**
|
|
* detect modifier
|
|
*/
|
|
val parts = stripped.split("|")
|
|
|
|
val efcurve = parts.getOrElse(0) { "" }
|
|
val modifier = parts.getOrNull(1)
|
|
|
|
var fcurve = efcurve(efcurve, constants, functions)
|
|
if (modifier != null) {
|
|
fcurve = modifyFCurve(fcurve, modifier, constants, functions)
|
|
}
|
|
return fcurve
|
|
}
|
|
|
|
/**
|
|
* expand efcurve to fcurve
|
|
* @param ef an efcurve string
|
|
* @param constants a map of constants that is passed to [evaluateExpression]
|
|
*/
|
|
fun efcurve(
|
|
ef: String,
|
|
constants: Map<String, Double> = emptyMap(),
|
|
functions: FunctionExtensions = FunctionExtensions.EMPTY
|
|
): String {
|
|
// IntelliJ falsely reports a redundant escape character. the escape character is required when running the regular
|
|
// expression on a javascript target. Removing the escape character will result in a `Lone quantifier brackets`
|
|
// syntax error.
|
|
|
|
@Suppress("RegExpRedundantEscape")
|
|
val expression = Regex("\\{([^{}]+)\\}")
|
|
|
|
@Suppress("RegExpRedundantEscape")
|
|
val repetition = Regex("""\(([^()]+)\)\[([^\[\]]+)\]""")
|
|
|
|
@Suppress("RegExpRedundantEscape")
|
|
val list = Regex("\\(([^()]+)\\)\\{([^\\[\\]]+)\\}")
|
|
|
|
/**
|
|
* perform comment substitution
|
|
*/
|
|
var curve = Regex("(#.*)$", RegexOption.MULTILINE).replace(ef, "")
|
|
|
|
/**
|
|
* Allow for nested repetitions and lists
|
|
*/
|
|
do {
|
|
val referenceCurve = curve
|
|
|
|
/**
|
|
* perform list expansion |text|{items}
|
|
*/
|
|
curve = list.replace(curve) { occ ->
|
|
val listText = expression.replace(occ.groupValues[2]) { exp ->
|
|
val expressionText = exp.groupValues[1]
|
|
evaluateExpression(expressionText, constants, functions)?.toString()
|
|
?: error("parse error in repetition count expression '$expressionText'")
|
|
}
|
|
val listTokens = listText.split(Regex("[,;][\t\n ]*|[\t\n ]+"))
|
|
val listItems = listTokens.filter { it.isNotEmpty() }
|
|
.map { it.trim().toDoubleOrNull() ?: error("'$it' is not a number in $listTokens") }
|
|
|
|
listItems.mapIndexed { index, value ->
|
|
expression.replace(occ.groupValues[1]) { exp ->
|
|
val expressionText = exp.groupValues[1]
|
|
evaluateExpression(
|
|
exp.groupValues[1],
|
|
constants + mapOf("index" to index.toDouble(), "it" to value),
|
|
functions
|
|
)?.toString() ?: error("parse error in repeated expression '$expressionText'")
|
|
}
|
|
}.joinToString(" ")
|
|
}
|
|
|
|
/**
|
|
* perform repetition expansion |text|[repeat-count]
|
|
*/
|
|
curve = repetition.replace(curve) { occ ->
|
|
val repetitions = expression.replace(occ.groupValues[2]) { exp ->
|
|
val expressionText = exp.groupValues[1]
|
|
evaluateExpression(expressionText, constants)?.toInt()?.toString()
|
|
?: error("parse error in repetition count expression '$expressionText'")
|
|
}.toInt()
|
|
List(repetitions) { repetition ->
|
|
expression.replace(occ.groupValues[1]) { exp ->
|
|
val expressionText = exp.groupValues[1]
|
|
evaluateExpression(
|
|
exp.groupValues[1],
|
|
constants + mapOf("it" to repetition.toDouble()),
|
|
functions
|
|
)?.toString()
|
|
?: error("parse error in repeated expression '$expressionText'")
|
|
}
|
|
}.joinToString(" ")
|
|
}
|
|
} while (curve != referenceCurve)
|
|
|
|
/**
|
|
* evaluate expression in expansion
|
|
*/
|
|
return (expression.replace(curve) { exp ->
|
|
evaluateExpression(exp.groupValues[1], constants, functions)?.toString() ?: error("parse error in '$curve")
|
|
})
|
|
} |