[orx-shade-styles] convert to MPP
This commit is contained in:
71
orx-shade-styles/src/commonMain/kotlin/AngularGradient.kt
Normal file
71
orx-shade-styles/src/commonMain/kotlin/AngularGradient.kt
Normal file
@@ -0,0 +1,71 @@
|
||||
package org.openrndr.extra.shadestyles
|
||||
|
||||
import org.openrndr.color.ColorRGBa
|
||||
import org.openrndr.draw.ShadeStyle
|
||||
import org.openrndr.extra.parameters.ColorParameter
|
||||
import org.openrndr.extra.parameters.Description
|
||||
import org.openrndr.extra.parameters.DoubleParameter
|
||||
import org.openrndr.math.Vector2
|
||||
|
||||
@Description("Angular gradient")
|
||||
class AngularGradient(
|
||||
color0: ColorRGBa,
|
||||
color1: ColorRGBa,
|
||||
offset: Vector2 = Vector2.ZERO,
|
||||
rotation: Double = 0.0,
|
||||
exponent: Double = 1.0) : ShadeStyle() {
|
||||
|
||||
@ColorParameter("start color", order = 0)
|
||||
var color0 : ColorRGBa by Parameter()
|
||||
@ColorParameter("end color", order = 1)
|
||||
var color1 : ColorRGBa by Parameter()
|
||||
@ColorParameter("offset", order = 2)
|
||||
var offset : Vector2 by Parameter()
|
||||
@ColorParameter("rotation", order = 3)
|
||||
var rotation : Double by Parameter()
|
||||
@DoubleParameter("exponent", 0.01, 10.0, order = 4)
|
||||
var exponent: Double by Parameter()
|
||||
|
||||
init {
|
||||
this.color0 = color0
|
||||
this.color1 = color1
|
||||
this.offset = offset
|
||||
this.rotation = rotation
|
||||
this.exponent = exponent
|
||||
|
||||
fragmentTransform = """
|
||||
vec2 coord = (c_boundsPosition.xy - 0.5 + p_offset/2.0) * 2.0;
|
||||
|
||||
float cr = cos(radians(p_rotation));
|
||||
float sr = sin(radians(p_rotation));
|
||||
mat2 rm = mat2(cr, -sr, sr, cr);
|
||||
vec2 rc = rm * coord;
|
||||
float f = (atan(rc.y, rc.x) + 3.1415926536) / (2.0 * 3.1415926536);
|
||||
|
||||
vec4 color0 = p_color0;
|
||||
color0.rgb *= color0.a;
|
||||
|
||||
vec4 color1 = p_color1;
|
||||
color1.rgb *= color1.a;
|
||||
|
||||
vec4 gradient = mix(color0, color1, pow(f, p_exponent));
|
||||
|
||||
vec4 fn = vec4(x_fill.rgb, 1.0) * x_fill.a;
|
||||
|
||||
x_fill = fn * gradient;
|
||||
if (x_fill.a != 0) {
|
||||
x_fill.rgb /= x_fill.a;
|
||||
}
|
||||
"""
|
||||
}
|
||||
}
|
||||
|
||||
fun angularGradient(
|
||||
color0: ColorRGBa,
|
||||
color1: ColorRGBa,
|
||||
offset: Vector2 = Vector2.ZERO,
|
||||
rotation: Double = 0.0,
|
||||
exponent: Double = 1.0
|
||||
): ShadeStyle {
|
||||
return AngularGradient(color0, color1, offset, rotation, exponent)
|
||||
}
|
||||
13
orx-shade-styles/src/commonMain/kotlin/ColorspaceHelper.kt
Normal file
13
orx-shade-styles/src/commonMain/kotlin/ColorspaceHelper.kt
Normal file
@@ -0,0 +1,13 @@
|
||||
package org.openrndr.extra.shadestyles
|
||||
|
||||
import org.openrndr.color.ColorRGBa
|
||||
import org.openrndr.extras.color.spaces.ColorOKLABa
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
internal fun generateColorTransform(kClass: KClass<*>): String {
|
||||
return when (kClass) {
|
||||
ColorRGBa::class -> """"""
|
||||
ColorOKLABa::class -> """gradient = linear_rgb_to_srgb(oklab_to_linear_rgb(gradient));"""
|
||||
else -> error("color space not supported $kClass")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package org.openrndr.extra.shadestyles
|
||||
|
||||
import org.openrndr.color.ColorRGBa
|
||||
import org.openrndr.draw.ShadeStyle
|
||||
import org.openrndr.extra.parameters.ColorParameter
|
||||
import org.openrndr.extra.parameters.Description
|
||||
import org.openrndr.extra.parameters.DoubleParameter
|
||||
import org.openrndr.math.Vector2
|
||||
|
||||
@Description("Half-angular gradient")
|
||||
class HalfAngularGradient(
|
||||
color0: ColorRGBa,
|
||||
color1: ColorRGBa,
|
||||
offset: Vector2 = Vector2.ZERO,
|
||||
rotation: Double = 0.0,
|
||||
exponent: Double = 1.0) : ShadeStyle() {
|
||||
|
||||
@ColorParameter("start color", order = 0)
|
||||
var color0: ColorRGBa by Parameter()
|
||||
@ColorParameter("end color", order = 1)
|
||||
var color1: ColorRGBa by Parameter()
|
||||
var offset: Vector2 by Parameter()
|
||||
@DoubleParameter("rotation", -180.0, 180.0, order = 2)
|
||||
var rotation: Double by Parameter()
|
||||
@DoubleParameter("exponent", 0.01, 10.0, order = 3)
|
||||
var exponent: Double by Parameter()
|
||||
|
||||
init {
|
||||
this.color0 = color0
|
||||
this.color1 = color1
|
||||
this.offset = offset
|
||||
this.rotation = rotation
|
||||
this.exponent = exponent
|
||||
|
||||
fragmentTransform = """
|
||||
vec2 coord = (c_boundsPosition.xy - 0.5 + p_offset/2.0) * 2.0;
|
||||
|
||||
float cr = cos(radians(p_rotation));
|
||||
float sr = sin(radians(p_rotation));
|
||||
mat2 rm = mat2(cr, -sr, sr, cr);
|
||||
vec2 rc = rm * coord;
|
||||
float f = abs(atan(rc.y, rc.x)) / (3.1415926536);
|
||||
|
||||
//float f = abs(atan(rc.y/rc.x)) / (3.1415926536/2.0);
|
||||
//float f = (atan(rc.y/rc.x) + 3.1415926536/2.0) / (3.1415926536);
|
||||
|
||||
vec4 color0 = p_color0;
|
||||
color0.rgb *= color0.a;
|
||||
|
||||
vec4 color1 = p_color1;
|
||||
color1.rgb *= color1.a;
|
||||
|
||||
vec4 gradient = mix(color0, color1, pow(f, p_exponent));
|
||||
|
||||
vec4 fn = vec4(x_fill.rgb, 1.0) * x_fill.a;
|
||||
|
||||
x_fill = fn * gradient;
|
||||
if (x_fill.a != 0) {
|
||||
x_fill.rgb /= x_fill.a;
|
||||
}
|
||||
"""
|
||||
}
|
||||
}
|
||||
|
||||
fun halfAngularGradient(
|
||||
color0: ColorRGBa,
|
||||
color1: ColorRGBa,
|
||||
offset: Vector2 = Vector2.ZERO,
|
||||
rotation: Double = 0.0,
|
||||
exponent: Double = 1.0
|
||||
): ShadeStyle {
|
||||
return HalfAngularGradient(color0, color1, offset, rotation, exponent)
|
||||
}
|
||||
103
orx-shade-styles/src/commonMain/kotlin/LinearGradient.kt
Normal file
103
orx-shade-styles/src/commonMain/kotlin/LinearGradient.kt
Normal file
@@ -0,0 +1,103 @@
|
||||
package org.openrndr.extra.shadestyles
|
||||
|
||||
import org.openrndr.color.*
|
||||
import org.openrndr.draw.ShadeStyle
|
||||
import org.openrndr.extra.parameters.ColorParameter
|
||||
import org.openrndr.extra.parameters.Description
|
||||
import org.openrndr.extra.parameters.DoubleParameter
|
||||
import org.openrndr.extra.shaderphrases.preprocess
|
||||
import org.openrndr.extras.color.phrases.ColorPhraseBook
|
||||
import org.openrndr.extras.color.spaces.ColorOKLABa
|
||||
import org.openrndr.math.CastableToVector4
|
||||
import org.openrndr.math.Vector2
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@Description("Linear gradient")
|
||||
open class LinearGradientBase<C>(
|
||||
color0: C,
|
||||
color1: C,
|
||||
offset: Vector2 = Vector2.ZERO,
|
||||
rotation: Double = 0.0,
|
||||
exponent: Double = 1.0
|
||||
) : ShadeStyle()
|
||||
where C : ConvertibleToColorRGBa, C : AlgebraicColor<C>, C: CastableToVector4 {
|
||||
|
||||
@ColorParameter("start color", order = 0)
|
||||
var color0: C by Parameter()
|
||||
|
||||
@ColorParameter("end color", order = 1)
|
||||
var color1: C by Parameter()
|
||||
var offset: Vector2 by Parameter()
|
||||
|
||||
@DoubleParameter("rotation", -180.0, 180.0, order = 2)
|
||||
var rotation: Double by Parameter()
|
||||
|
||||
@DoubleParameter("exponent", 0.01, 10.0, order = 3)
|
||||
var exponent: Double by Parameter()
|
||||
|
||||
init {
|
||||
ColorPhraseBook.register()
|
||||
this.color0 = color0
|
||||
this.color1 = color1
|
||||
this.offset = offset
|
||||
this.rotation = rotation
|
||||
this.exponent = exponent
|
||||
|
||||
fragmentPreamble = """
|
||||
|#pragma import color.oklab_to_linear_rgb
|
||||
|#pragma import color.linear_rgb_to_srgb""".trimMargin().preprocess()
|
||||
fragmentTransform = """
|
||||
vec2 coord = (c_boundsPosition.xy - 0.5 + p_offset);
|
||||
|
||||
float cr = cos(radians(p_rotation));
|
||||
float sr = sin(radians(p_rotation));
|
||||
mat2 rm = mat2(cr, -sr, sr, cr);
|
||||
vec2 rc = rm * coord;
|
||||
float f = clamp(rc.y + 0.5, 0.0, 1.0);
|
||||
|
||||
vec4 color0 = p_color0;
|
||||
vec4 color1 = p_color1;
|
||||
|
||||
vec4 gradient = mix(color0, color1, pow(f, p_exponent));
|
||||
${generateColorTransform(color0::class)}
|
||||
x_fill *= gradient;
|
||||
"""
|
||||
}
|
||||
}
|
||||
|
||||
class LinearGradient(
|
||||
color0: ColorRGBa,
|
||||
color1: ColorRGBa,
|
||||
offset: Vector2 = Vector2.ZERO,
|
||||
rotation: Double = 0.0,
|
||||
exponent: Double = 1.0
|
||||
): LinearGradientBase<ColorRGBa>(color0, color1, offset, rotation, exponent)
|
||||
|
||||
class LinearGradientOKLab(
|
||||
color0: ColorOKLABa,
|
||||
color1: ColorOKLABa,
|
||||
offset: Vector2 = Vector2.ZERO,
|
||||
rotation: Double = 0.0,
|
||||
exponent: Double = 1.0
|
||||
): LinearGradientBase<ColorOKLABa>(color0, color1, offset, rotation, exponent)
|
||||
|
||||
|
||||
fun linearGradient(
|
||||
color0: ColorRGBa,
|
||||
color1: ColorRGBa,
|
||||
offset: Vector2 = Vector2.ZERO,
|
||||
rotation: Double = 0.0,
|
||||
exponent: Double = 1.0
|
||||
): LinearGradient {
|
||||
return LinearGradient(color0, color1, offset, rotation, exponent)
|
||||
}
|
||||
|
||||
fun linearGradient(
|
||||
color0: ColorOKLABa,
|
||||
color1: ColorOKLABa,
|
||||
offset: Vector2 = Vector2.ZERO,
|
||||
rotation: Double = 0.0,
|
||||
exponent: Double = 1.0
|
||||
): LinearGradientOKLab {
|
||||
return LinearGradientOKLab(color0, color1, offset, rotation, exponent)
|
||||
}
|
||||
31
orx-shade-styles/src/commonMain/kotlin/NPointGradient.kt
Normal file
31
orx-shade-styles/src/commonMain/kotlin/NPointGradient.kt
Normal file
@@ -0,0 +1,31 @@
|
||||
package org.openrndr.extra.shadestyles
|
||||
|
||||
import org.openrndr.color.ColorRGBa
|
||||
import org.openrndr.draw.ShadeStyle
|
||||
import org.openrndr.extra.parameters.Description
|
||||
import org.openrndr.math.Vector2
|
||||
|
||||
@Description("N-Point gradient")
|
||||
class NPointGradient(
|
||||
colors: Array<ColorRGBa>,
|
||||
points: Array<Vector2> = arrayOf(Vector2.ZERO)) : ShadeStyle() {
|
||||
|
||||
var colors: Array<ColorRGBa> by Parameter()
|
||||
var points: Array<Vector2> by Parameter()
|
||||
|
||||
init {
|
||||
this.colors = colors
|
||||
this.points = points
|
||||
|
||||
fragmentTransform = """
|
||||
float sum = 0;
|
||||
vec4 rgba = vec4(0.0);
|
||||
for(int i=0; i<p_points_SIZE; i++) {
|
||||
float dist = 1.0 / (1.0 + distance(p_points[i], c_screenPosition));
|
||||
sum += dist;
|
||||
rgba += p_colors[i] * dist;
|
||||
}
|
||||
x_fill = rgba/sum;
|
||||
"""
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package org.openrndr.extra.shadestyles
|
||||
|
||||
import org.openrndr.color.AlgebraicColor
|
||||
import org.openrndr.color.ColorRGBa
|
||||
import org.openrndr.color.ConvertibleToColorRGBa
|
||||
import org.openrndr.draw.ShadeStyle
|
||||
import org.openrndr.extra.parameters.Description
|
||||
import org.openrndr.extras.color.spaces.ColorOKLABa
|
||||
import org.openrndr.math.CastableToVector4
|
||||
import org.openrndr.math.Vector2
|
||||
|
||||
@Description("Multicolor linear gradient")
|
||||
open class NPointLinearGradientBase<C>(
|
||||
colors: Array<C>,
|
||||
points: Array<Double> = Array(colors.size) { it / (colors.size - 1.0) },
|
||||
offset: Vector2 = Vector2.ZERO,
|
||||
rotation: Double = 0.0
|
||||
) : ShadeStyle()
|
||||
where C : ConvertibleToColorRGBa, C : AlgebraicColor<C>, C : CastableToVector4 {
|
||||
|
||||
var colors: Array<C> by Parameter()
|
||||
|
||||
// Sorted normalized values defining relative positions of colors
|
||||
var points: Array<Double> by Parameter()
|
||||
var offset: Vector2 by Parameter()
|
||||
var rotation: Double by Parameter()
|
||||
|
||||
init {
|
||||
this.colors = colors
|
||||
this.points = points
|
||||
this.offset = offset
|
||||
this.rotation = rotation
|
||||
|
||||
fragmentTransform = """
|
||||
vec2 coord = (c_boundsPosition.xy - 0.5 + p_offset);
|
||||
|
||||
float cr = cos(radians(p_rotation));
|
||||
float sr = sin(radians(p_rotation));
|
||||
mat2 rm = mat2(cr, -sr, sr, cr);
|
||||
vec2 rc = rm * coord;
|
||||
float f = clamp(rc.y + 0.5, 0.0, 1.0);
|
||||
|
||||
int i=0;
|
||||
while(i < p_points_SIZE - 1 && f >= p_points[i+1]) { i++; }
|
||||
|
||||
vec4 color0 = p_colors[i];
|
||||
vec4 color1 = p_colors[i+1];
|
||||
|
||||
float g = (f - p_points[i]) / (p_points[i+1] - p_points[i]);
|
||||
vec4 gradient = mix(color0, color1, clamp(g, 0.0, 1.0));
|
||||
|
||||
${generateColorTransform(colors[0]::class)}
|
||||
|
||||
x_fill *= gradient;
|
||||
if (x_fill.a != 0) {
|
||||
x_fill.rgb /= x_fill.a;
|
||||
}
|
||||
|
||||
"""
|
||||
}
|
||||
}
|
||||
|
||||
class NPointLinearGradient(
|
||||
colors: Array<ColorRGBa>,
|
||||
points: Array<Double> = Array(colors.size) { it / (colors.size - 1.0) },
|
||||
offset: Vector2 = Vector2.ZERO,
|
||||
rotation: Double = 0.0
|
||||
) : NPointLinearGradientBase<ColorRGBa>(colors, points, offset, rotation)
|
||||
|
||||
class NPointLinearGradientOKLab(
|
||||
colors: Array<ColorOKLABa>,
|
||||
points: Array<Double> = Array(colors.size) { it / (colors.size - 1.0) },
|
||||
offset: Vector2 = Vector2.ZERO,
|
||||
rotation: Double = 0.0
|
||||
) : NPointLinearGradientBase<ColorOKLABa>(colors, points, offset, rotation)
|
||||
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package org.openrndr.extra.shadestyles
|
||||
|
||||
import org.openrndr.color.ColorRGBa
|
||||
import org.openrndr.draw.ShadeStyle
|
||||
import org.openrndr.extra.parameters.Description
|
||||
import org.openrndr.math.Vector2
|
||||
|
||||
@Description("Multicolor radial gradient")
|
||||
class NPointRadialGradient(
|
||||
colors: Array<ColorRGBa>,
|
||||
points: Array<Double> = Array(colors.size) { it / (colors.size - 1.0) },
|
||||
offset: Vector2 = Vector2.ZERO,
|
||||
rotation: Double = 0.0,
|
||||
length: Double = 1.0) : ShadeStyle()
|
||||
|
||||
|
||||
{
|
||||
|
||||
var colors: Array<ColorRGBa> by Parameter()
|
||||
|
||||
// Sorted normalized values defining relative positions of colors
|
||||
var points: Array<Double> by Parameter()
|
||||
var offset: Vector2 by Parameter()
|
||||
var rotation: Double by Parameter()
|
||||
var length: Double by Parameter()
|
||||
|
||||
init {
|
||||
this.colors = colors
|
||||
this.points = points
|
||||
this.offset = offset
|
||||
this.rotation = rotation
|
||||
this.length = length
|
||||
|
||||
fragmentTransform = """
|
||||
vec2 coord = (c_boundsPosition.xy - 0.5 + p_offset/2.0) * 2.0;
|
||||
|
||||
float cr = cos(radians(p_rotation));
|
||||
float sr = sin(radians(p_rotation));
|
||||
mat2 rm = mat2(cr, -sr, sr, cr);
|
||||
vec2 rc = rm * coord;
|
||||
float f = clamp(p_length * length(rc), 0.0, 1.0);
|
||||
|
||||
int i=0;
|
||||
while(i < p_points_SIZE - 1 && f >= p_points[i+1]) { i++; }
|
||||
|
||||
vec4 color0 = p_colors[i];
|
||||
color0.rgb *= color0.a;
|
||||
|
||||
vec4 color1 = p_colors[i+1];
|
||||
color1.rgb *= color1.a;
|
||||
|
||||
float g = (f - p_points[i]) / (p_points[i+1] - p_points[i]);
|
||||
vec4 gradient = mix(color0, color1, clamp(g, 0.0, 1.0));
|
||||
|
||||
vec4 fn = vec4(x_fill.rgb, 1.0) * x_fill.a;
|
||||
|
||||
x_fill = fn * gradient;
|
||||
if (x_fill.a != 0) {
|
||||
x_fill.rgb /= x_fill.a;
|
||||
}
|
||||
"""
|
||||
}
|
||||
}
|
||||
103
orx-shade-styles/src/commonMain/kotlin/RadialGradient.kt
Normal file
103
orx-shade-styles/src/commonMain/kotlin/RadialGradient.kt
Normal file
@@ -0,0 +1,103 @@
|
||||
package org.openrndr.extra.shadestyles
|
||||
|
||||
import org.openrndr.color.*
|
||||
import org.openrndr.draw.ShadeStyle
|
||||
import org.openrndr.draw.shadeStyle
|
||||
import org.openrndr.extra.parameters.ColorParameter
|
||||
import org.openrndr.extra.parameters.Description
|
||||
import org.openrndr.extra.parameters.DoubleParameter
|
||||
import org.openrndr.extras.color.spaces.ColorOKLABa
|
||||
import org.openrndr.math.CastableToVector4
|
||||
import org.openrndr.math.Vector2
|
||||
|
||||
@Description("Radial gradient")
|
||||
open class RadialGradientBase<C>(
|
||||
color0: C,
|
||||
color1: C,
|
||||
offset: Vector2 = Vector2.ZERO,
|
||||
rotation: Double = 0.0,
|
||||
length: Double = 1.0,
|
||||
exponent: Double = 1.0)
|
||||
: ShadeStyle()
|
||||
where C : ConvertibleToColorRGBa, C : AlgebraicColor<C>, C: CastableToVector4 {
|
||||
@ColorParameter("start color", order = 0)
|
||||
var color0 : C by Parameter()
|
||||
@ColorParameter("end color", order = 1)
|
||||
var color1 : C by Parameter()
|
||||
var offset : Vector2 by Parameter()
|
||||
@DoubleParameter("rotation", -180.0, 180.0, order = 2)
|
||||
var rotation : Double by Parameter()
|
||||
@DoubleParameter("length", 0.0, 10.0)
|
||||
var length: Double by Parameter()
|
||||
@DoubleParameter("exponent", 0.01, 10.0, order = 3)
|
||||
var exponent: Double by Parameter()
|
||||
|
||||
init {
|
||||
this.color0 = color0
|
||||
this.color1 = color1
|
||||
this.offset = offset
|
||||
this.rotation = rotation
|
||||
this.length = length
|
||||
this.exponent = exponent
|
||||
|
||||
fragmentTransform = """
|
||||
vec2 coord = (c_boundsPosition.xy - 0.5 + p_offset/2.0) * 2.0;
|
||||
|
||||
float cr = cos(radians(p_rotation));
|
||||
float sr = sin(radians(p_rotation));
|
||||
mat2 rm = mat2(cr, -sr, sr, cr);
|
||||
vec2 rc = rm * coord;
|
||||
float f = clamp(p_length * length(rc), 0.0, 1.0);
|
||||
|
||||
vec4 color0 = p_color0;
|
||||
vec4 color1 = p_color1;
|
||||
|
||||
vec4 gradient = mix(color0, color1, pow(f, p_exponent));
|
||||
${generateColorTransform(color0::class)}
|
||||
|
||||
x_fill *= gradient;
|
||||
"""
|
||||
}
|
||||
}
|
||||
|
||||
class RadialGradient(
|
||||
color0: ColorRGBa,
|
||||
color1: ColorRGBa,
|
||||
offset: Vector2 = Vector2.ZERO,
|
||||
rotation: Double = 0.0,
|
||||
length: Double = 1.0,
|
||||
exponent: Double = 1.0
|
||||
): RadialGradientBase<ColorRGBa>(color0, color1, offset, rotation, length, exponent)
|
||||
|
||||
class RadialGradientOKLab(
|
||||
color0: ColorOKLABa,
|
||||
color1: ColorOKLABa,
|
||||
offset: Vector2 = Vector2.ZERO,
|
||||
rotation: Double = 0.0,
|
||||
length: Double = 1.0,
|
||||
exponent: Double = 1.0
|
||||
): RadialGradientBase<ColorOKLABa>(color0, color1, offset, rotation, length, exponent)
|
||||
|
||||
fun radialGradient(
|
||||
color0: ColorRGBa,
|
||||
color1: ColorRGBa,
|
||||
offset: Vector2 = Vector2.ZERO,
|
||||
rotation: Double = 0.0,
|
||||
length: Double = 1.0,
|
||||
exponent: Double = 1.0
|
||||
): RadialGradient {
|
||||
return RadialGradient(color0, color1, offset, rotation, length, exponent)
|
||||
}
|
||||
|
||||
fun radialGradient(
|
||||
color0: ColorOKLABa,
|
||||
color1: ColorOKLABa,
|
||||
offset: Vector2 = Vector2.ZERO,
|
||||
rotation: Double = 0.0,
|
||||
length: Double = 1.0,
|
||||
exponent: Double = 1.0
|
||||
): RadialGradientOKLab {
|
||||
return RadialGradientOKLab(color0, color1, offset, rotation, length, exponent)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user