diff --git a/Assets/Resources/LuaModules/__autocomplete.lua b/Assets/Resources/LuaModules/__autocomplete.lua index 8b83108986..21db6ea480 100644 --- a/Assets/Resources/LuaModules/__autocomplete.lua +++ b/Assets/Resources/LuaModules/__autocomplete.lua @@ -274,6 +274,13 @@ function path.transform(path, transform) end function path.translate(path, amount) end function path.rotate(path, amount) end function path.scale(path, amount) end +function path.centered(path) end +function path.startingFrom(path, index) end +function path.findClosest(path, point) end +function path.findMinimum(path, axis) end +function path.findMaximum(path, axis) end +function path.normalized(path) end +function path.resample(path, spacing) end visualizer.sampleRate = nil visualizer.duration = nil function visualizer.enableScripting(name) end @@ -338,6 +345,7 @@ function symmetry.getColors() end function symmetry.setBrushes(brushes) end function symmetry.getBrushNames() end function symmetry.getBrushGuids() end +function symmetry.pointsToPolar(transforms) end function symmetry.pointsToPolar(cartesianPoints) end function timer.set(fn, interval, delay, repeats) end function timer.unset(fn) end diff --git a/Assets/Resources/LuaModules/symmetryHueShift.lua b/Assets/Resources/LuaModules/symmetryHueShift.lua new file mode 100644 index 0000000000..e7e67215f7 --- /dev/null +++ b/Assets/Resources/LuaModules/symmetryHueShift.lua @@ -0,0 +1,24 @@ +local symmetryHueShift = {} + +if not Parameters then + Parameters = {} +end + +Parameters["hueShiftFrequency"] = {label="Hue Shift Frequency", type="float", min=0.1, max=6, default=1} +Parameters["hueShiftAmount"] = {label="Hue Shift Amount", type="float", min=0, max=1, default=0.3} + +function symmetryHueShift.generate(copies, initialHsv) + if hueShiftAmount > 0 then + colors = {} + for i = 0, copies - 1 do + t = i / copies + newHue = waveform.triangle(t, hueShiftFrequency) * hueShiftAmount + newColor = unityColor.hsvToRgb(initialHsv.x + newHue, initialHsv.y, initialHsv.z) + table.insert(colors, newColor) + end + symmetry.setColors(colors) + brush.forceNewStroke() + end +end + +return symmetryHueShift diff --git a/Assets/Resources/LuaModules/symmetryHueShift.lua.meta b/Assets/Resources/LuaModules/symmetryHueShift.lua.meta new file mode 100644 index 0000000000..c9f5908207 --- /dev/null +++ b/Assets/Resources/LuaModules/symmetryHueShift.lua.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 879375fa7ded6c84192dc0470644beb6 +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 11500000, guid: 2d1920d65e755cf4f98a93b4a4e952e5, type: 3} diff --git a/Assets/Resources/LuaScriptExamples/BackgroundScript.RotateLayer.lua b/Assets/Resources/LuaScriptExamples/BackgroundScript.RotateLayer.lua index abf4970ed8..917305c684 100644 --- a/Assets/Resources/LuaScriptExamples/BackgroundScript.RotateLayer.lua +++ b/Assets/Resources/LuaScriptExamples/BackgroundScript.RotateLayer.lua @@ -4,15 +4,16 @@ Parameters = { layerNumber={label="Layer Number", type="int", min=0, max=10, default=1}, - speedX={label="X Rotation Speed", type="float", min=0.01, max=20, default=0}, - speedY={label="Y Rotation Speed", type="float", min=0.01, max=20, default=0}, - speedZ={label="Z Rotation Speed", type="float", min=0.01, max=20, default=6}, - radius={label="Radius", type="float", min=0.01, max=5, default=1}, + speedX={label="X Rotation Speed", type="int", min=-10, max=10, default=0}, + speedY={label="Y Rotation Speed", type="int", min=-10, max=10, default=0}, + speedZ={label="Z Rotation Speed", type="int", min=-10, max=10, default=2}, } function Start() - layers.centerPivot(layerNumber) + originalRotation = layers.getRotation(layerNumber) angle = {x = 0, y = 0, z = 0} + layers.centerPivot(layerNumber) + -- layers.showPivot(layerNumber) end function Main() @@ -24,3 +25,7 @@ function Main() rotation = angle layers.setRotation(layerNumber, rotation) end + +function End() + layers.setRotation(layerNumber, originalRotation) +end diff --git a/Assets/Resources/LuaScriptExamples/SymmetryScript.Ducklings.lua b/Assets/Resources/LuaScriptExamples/SymmetryScript.Ducklings.lua index 366c523f52..3c5522a167 100644 --- a/Assets/Resources/LuaScriptExamples/SymmetryScript.Ducklings.lua +++ b/Assets/Resources/LuaScriptExamples/SymmetryScript.Ducklings.lua @@ -1,12 +1,12 @@ +Settings = {space="canvas"} + Parameters = { copies={label="Number of copies", type="int", min=1, max=64, default=12}, delay={label="Delay per copy", type="int", min=1, max=10, default=4}, amount={label="Amount", type="float", min=0, max=1, default=0.5}, - hueShiftFrequency={label="Hue Shift Frequency", type="float", min=0.1, max=6, default=1}, - hueShiftAmount={label="Hue Shift Amount", type="float", min=0, max=1, default=0.1} } -Settings = {space="canvas"} +symmetryHueShift = require "symmetryHueShift" function Start() initialHsv = brush.colorHsv diff --git a/Assets/Resources/LuaScriptExamples/SymmetryScript.EllipseAround.lua b/Assets/Resources/LuaScriptExamples/SymmetryScript.EllipseAround.lua index eb9777fbbf..ffbea7ce86 100644 --- a/Assets/Resources/LuaScriptExamples/SymmetryScript.EllipseAround.lua +++ b/Assets/Resources/LuaScriptExamples/SymmetryScript.EllipseAround.lua @@ -6,10 +6,10 @@ Parameters = { copies={label="Number of copies", type="int", min=1, max=96, default=32}, eccentricity={label="Eccentricity", type="float", min=0.1, max=3, default=2}, axisConsistency={label="Axis Consistency", type="float", min=0, max=2, default=1}, - hueShiftFrequency={label="Hue Shift Frequency", type="float", min=0.1, max=6, default=1}, - hueShiftAmount={label="Hue Shift Amount", type="float", min=0, max=1, default=0.3} } +symmetryHueShift = require "symmetryHueShift" + function Start() initialHsv = brush.colorHsv --symmetry.transform = { @@ -20,9 +20,12 @@ end function Main() + if brush.triggerIsPressedThisFrame then + symmetryHueShift.generate(copies, initialHsv) + end + pointers = {} theta = (math.pi * 2.0) / copies - Colors = {} for i = 0, copies - 1 do angle = (symmetry.rotation.y * unityMathf.deg2Rad) + i * theta @@ -41,20 +44,10 @@ function Main() rotation={0, angle * unityMathf.rad2Deg, 0} } table.insert(pointers, pointer) - - --Colour cycling for the extra pointers - if hueShiftAmount > 0 then - t = i / copies - newHue = waveform.triangle(t, hueShiftFrequency) * hueShiftAmount - newColor = unityColor.hsvToRgb(initialHsv.x + newHue, initialHsv.y, initialHsv.z) - table.insert(Colors, newColor) - end - end return pointers end function End() - --TODO - --color.setHsv(color.HsvToRgb(brush.colorHsv)) + -- TODO fix brush.colorHsv = initialHsv end diff --git a/Assets/Resources/LuaScriptExamples/SymmetryScript.ManyAlong.lua b/Assets/Resources/LuaScriptExamples/SymmetryScript.ManyAlong.lua index 25032b1f10..c1411e5ad6 100644 --- a/Assets/Resources/LuaScriptExamples/SymmetryScript.ManyAlong.lua +++ b/Assets/Resources/LuaScriptExamples/SymmetryScript.ManyAlong.lua @@ -5,18 +5,21 @@ Parameters = { copies={label="Number of copies", type="int", min=1, max=36, default=12}, distance={label="Distance", type="float", min=0, max=20, default=5}, - hueShiftFrequency={label="Hue Shift Frequency", type="float", min=0.1, max=6, default=1}, - hueShiftAmount={label="Hue Shift Amount", type="float", min=0, max=1, default=0.5} } +symmetryHueShift = require "symmetryHueShift" + function Start() initialHsv = brush.colorHsv end function Main() + if brush.triggerIsPressedThisFrame then + symmetryHueShift.generate(copies, initialHsv) + end + pointers = {} - Colors = {} for i = 0, copies - 1 do @@ -29,19 +32,10 @@ function Main() symmetry.brushOffset.z }} table.insert(pointers, pos) - - --Colour cycling for the extra pointers - if hueShiftAmount > 0 then - newHue = waveform.triangle(t, hueShiftFrequency) * hueShiftAmount - newColor = unityColor.hsvToRgb(initialHsv.x + newHue, initialHsv.y, initialHsv.z) - table.insert(Colors, newColor) - end - end return pointers end function End() - --TODO - --color.setHsv(color.HsvToRgb(brush.colorHsv)) + -- TODO fix brush.colorHsv = initialHsv end diff --git a/Assets/Resources/LuaScriptExamples/SymmetryScript.ManyAround.lua b/Assets/Resources/LuaScriptExamples/SymmetryScript.ManyAround.lua index 58dd344925..9e7814b237 100644 --- a/Assets/Resources/LuaScriptExamples/SymmetryScript.ManyAround.lua +++ b/Assets/Resources/LuaScriptExamples/SymmetryScript.ManyAround.lua @@ -4,10 +4,10 @@ Parameters = { copies={label="Number of copies", type="int", min=1, max=96, default=32}, - hueShiftFrequency={label="Hue Shift Frequency", type="float", min=0.1, max=6, default=1}, - hueShiftAmount={label="Hue Shift Amount", type="float", min=0, max=1, default=0.3} } +symmetryHueShift = require "symmetryHueShift" + function Start() initialHsv = brush.colorHsv --symmetry.transform = { @@ -18,9 +18,12 @@ end function Main() + if brush.triggerIsPressedThisFrame then + symmetryHueShift.generate(copies, initialHsv) + end + pointers = {} theta = 360.0 / copies - Colors = {} for i = 0, copies - 1 do angle = i * theta @@ -33,20 +36,10 @@ function Main() rotation={0, angle, 0} } table.insert(pointers, pointer) - - --Colour cycling for the extra pointers - if hueShiftAmount > 0 then - t = i / copies - newHue = waveform.triangle(t, hueShiftFrequency) * hueShiftAmount - newColor = unityColor.hsvToRgb(initialHsv.x + newHue, initialHsv.y, initialHsv.z) - table.insert(Colors, newColor) - end - end return pointers end function End() - --TODO - --color.setHsv(color.HsvToRgb(brush.colorHsv)) + -- TODO fix brush.colorHsv = initialHsv end diff --git a/Assets/Resources/LuaScriptExamples/SymmetryScript.PolygonAround.lua b/Assets/Resources/LuaScriptExamples/SymmetryScript.PolygonAround.lua index 5b4a7a0a66..13dc38a1a5 100644 --- a/Assets/Resources/LuaScriptExamples/SymmetryScript.PolygonAround.lua +++ b/Assets/Resources/LuaScriptExamples/SymmetryScript.PolygonAround.lua @@ -5,19 +5,22 @@ Parameters = { copies={label="Number of copies", type="int", min=1, max=96, default=32}, sides={label="Sides", type="int", min=3 , max=12, default=5}, - hueShiftFrequency={label="Hue Shift Frequency", type="float", min=0.1, max=6, default=1}, - hueShiftAmount={label="Hue Shift Amount", type="float", min=0, max=1, default=0.3} } +symmetryHueShift = require "symmetryHueShift" + function Start() initialHsv = brush.colorHsv end function Main() + if brush.triggerIsPressedThisFrame then + symmetryHueShift.generate(copies, initialHsv) + end + pointers = {} theta = (math.pi * 2.0) / copies - Colors = {} for i = 0, copies - 1 do angle = (symmetry.rotation.y * unityMathf.deg2Rad) + i * theta @@ -32,20 +35,10 @@ function Main() rotation={0, angle * unityMathf.rad2Deg, 0} } table.insert(pointers, pointer) - - --Colour cycling for the extra pointers - if hueShiftAmount > 0 then - t = i / copies - newHue = waveform.triangle(t, hueShiftFrequency) * hueShiftAmount - newColor = unityColor.hsvToRgb(initialHsv.x + newHue, initialHsv.y, initialHsv.z) - table.insert(Colors, newColor) - end - end return pointers end function End() - --TODO - --color.setHsv(color.HsvToRgb(brush.colorHsv)) + -- TODO fix brush.colorHsv = initialHsv end diff --git a/Assets/Resources/LuaScriptExamples/SymmetryScript.RectangleAround.lua b/Assets/Resources/LuaScriptExamples/SymmetryScript.RectangleAround.lua index 615c367d0d..56f041549f 100644 --- a/Assets/Resources/LuaScriptExamples/SymmetryScript.RectangleAround.lua +++ b/Assets/Resources/LuaScriptExamples/SymmetryScript.RectangleAround.lua @@ -7,10 +7,10 @@ Parameters = { numPointsHeight={label="Number of points along height", type="int", min=2, max=32, default=5}, spacing={label="Spacing", type="float", min=0.001 , max=1, default=.2}, exteriorOnly={label="Exterior Only", type="int", min=0, max=1, default=1}, - hueShiftFrequency={label="Hue Shift Frequency", type="float", min=0.1, max=6, default=1}, - hueShiftAmount={label="Hue Shift Amount", type="float", min=0, max=1, default=0.3} } +symmetryHueShift = require "symmetryHueShift" + function Start() initialHsv = brush.colorHsv end @@ -18,18 +18,7 @@ end function Main() if brush.triggerIsPressedThisFrame then - if hueShiftAmount > 0 then - colors = {} - copies = numPointsWidth * numPointsHeight * 2 - for i = 0, copies - 1 do - t = i / copies - newHue = waveform.triangle(t, hueShiftFrequency) * hueShiftAmount - newColor = unityColor.hsvToRgb(initialHsv.x + newHue, initialHsv.y, initialHsv.z) - table.insert(colors, newColor) - end - symmetry.setColors(colors) - brush.forceNewStroke() - end + symmetryHueShift.generate(numPointsWidth * numPointsHeight * 2, initialHsv) end if (exteriorOnly==1) then @@ -77,6 +66,5 @@ end function End() - --TODO - --color.setHsv(color.HsvToRgb(brush.colorHsv)) + -- TODO fix brush.colorHsv = initialHsv end diff --git a/Assets/Resources/LuaScriptExamples/SymmetryScript.SuperEllipseAround.lua b/Assets/Resources/LuaScriptExamples/SymmetryScript.SuperEllipseAround.lua index 7ba870daf8..a003f2dacf 100644 --- a/Assets/Resources/LuaScriptExamples/SymmetryScript.SuperEllipseAround.lua +++ b/Assets/Resources/LuaScriptExamples/SymmetryScript.SuperEllipseAround.lua @@ -7,19 +7,22 @@ Parameters = { n={label="n", type="float", min=0.1 , max=4, default=4}, eccentricity={label="Eccentricity", type="float", min=0.1, max=3, default=1}, axisConsistency={label="Axis Consistency", type="float", min=0, max=2, default=1}, - hueShiftFrequency={label="Hue Shift Frequency", type="float", min=0.1, max=6, default=1}, - hueShiftAmount={label="Hue Shift Amount", type="float", min=0, max=1, default=0.3} } +symmetryHueShift = require "symmetryHueShift" + function Start() initialHsv = brush.colorHsv end function Main() + if brush.triggerIsPressedThisFrame then + symmetryHueShift.generate(copies, initialHsv) + end + pointers = {} theta = (math.pi * 2.0) / copies - Colors = {} for i = 0, copies - 1 do angle = (symmetry.rotation.y * unityMathf.deg2Rad) + i * theta @@ -41,20 +44,10 @@ function Main() rotation={0, angle * unityMathf.rad2Deg, 0} } table.insert(pointers, pointer) - - --Colour cycling for the extra pointers - if hueShiftAmount > 0 then - t = i / copies - newHue = waveform.triangle(t, hueShiftFrequency) * hueShiftAmount - newColor = unityColor.hsvToRgb(initialHsv.x + newHue, initialHsv.y, initialHsv.z) - table.insert(Colors, newColor) - end - end return pointers end function End() - --TODO - --color.setHsv(color.HsvToRgb(brush.colorHsv)) + -- TODO fix brush.colorHsv = initialHsv end diff --git a/Assets/Resources/LuaScriptExamples/SymmetryScript.TooManyAround.lua b/Assets/Resources/LuaScriptExamples/SymmetryScript.TooManyAround.lua index 8709a113e0..482190f929 100644 --- a/Assets/Resources/LuaScriptExamples/SymmetryScript.TooManyAround.lua +++ b/Assets/Resources/LuaScriptExamples/SymmetryScript.TooManyAround.lua @@ -4,10 +4,10 @@ Parameters = { copies={label="Number of copies", type="int", min=1, max=36, default=32}, - hueShiftFrequency={label="Hue Shift Frequency", type="float", min=0.1, max=6, default=1}, - hueShiftAmount={label="Hue Shift Amount", type="float", min=0, max=1, default=0.3} } +symmetryHueShift = require "symmetryHueShift" + function Start() initialHsv = brush.colorHsv --symmetry.transform = { @@ -18,9 +18,12 @@ end function Main() + if brush.triggerIsPressedThisFrame then + symmetryHueShift.generate(copies, initialHsv) + end + pointers = {} theta = 360.0 / copies - Colors = {} for i = 0, copies - 1 do angle = i * theta @@ -33,20 +36,10 @@ function Main() rotation={0, angle, 0} } table.insert(pointers, pointer) - - --Colour cycling for the extra pointers - if hueShiftAmount > 0 then - t = i / copies - newHue = waveform.triangle(t, hueShiftFrequency) * hueShiftAmount - newColor = unityColor.hsvToRgb(initialHsv.x + newHue, initialHsv.y, initialHsv.z) - table.insert(Colors, newColor) - end - end return pointers end function End() - --TODO - --color.setHsv(color.HsvToRgb(brush.colorHsv)) + -- TODO fix brush.colorHsv = initialHsv end diff --git a/Assets/Scripts/API/Lua/LuaApiMethods.cs b/Assets/Scripts/API/Lua/LuaApiMethods.cs index bd3632077a..954c2a0575 100644 --- a/Assets/Scripts/API/Lua/LuaApiMethods.cs +++ b/Assets/Scripts/API/Lua/LuaApiMethods.cs @@ -65,7 +65,7 @@ public static List ScalePath(List path, Vector3 scale) public static List TransformPath(List path, TrTransform tr) { - return path.Select(x => x.TransformBy(tr)).ToList(); + return path.Select(x => tr * x).ToList(); } public static Quaternion VectorToRotation(Vector3 vec) diff --git a/Assets/Scripts/API/Lua/Wrappers/OpenBrushAppWrappers.cs b/Assets/Scripts/API/Lua/Wrappers/OpenBrushAppWrappers.cs index b9257302a1..4e1ddce4e8 100644 --- a/Assets/Scripts/API/Lua/Wrappers/OpenBrushAppWrappers.cs +++ b/Assets/Scripts/API/Lua/Wrappers/OpenBrushAppWrappers.cs @@ -352,6 +352,15 @@ public static List getBrushGuids() ).ToList(); } + public static List pointsToPolar(List transforms) + { + return pointsToPolar(transforms.Select(x => + { + var vector3 = x.translation; + return new Vector2(vector3.x, vector3.y); + }).ToList()); + } + // Converts an array of points centered on the origin to a list of TrTransforms // suitable for use with symmetry scripts default space public static List pointsToPolar(List cartesianPoints) @@ -388,6 +397,128 @@ public static class PathApiWrapper public static List translate(List path, Vector3 amount) => LuaApiMethods.TranslatePath(path, amount); public static List rotate(List path, Quaternion amount) => LuaApiMethods.RotatePath(path, amount); public static List scale(List path, Vector3 amount) => LuaApiMethods.ScalePath(path, amount); + + public static List centered(List path) + { + Vector3 centroid = path.Aggregate( + Vector3.zero, + (acc, x) => acc + x.translation + ) / path.Count; + return path.Select(x => x * TrTransform.T(-centroid)).ToList(); + } + + public static List startingFrom(List path, int index) + { + return path.Skip(index).Concat(path.Take(index)).ToList(); + } + + public static int findClosest(List path, Vector3 point) + { + return path.Select((x, i) => new {i, x}).Aggregate( + (acc, x) => (x.x.translation - point).sqrMagnitude < (acc.x.translation - point).sqrMagnitude ? x : acc + ).i; + } + + public static int findMinimum(List path, int axis) + { + return path + .Select((v, i) => (translation: v.translation[axis], index: i)) + .Aggregate((a, b) => a.translation < b.translation ? a : b) + .index; + } + + public static int findMaximum(List path, int axis) + { + return path + .Select((v, i) => (translation: v.translation[axis], index: i)) + .Aggregate((a, b) => a.translation > b.translation ? a : b) + .index; + } + + public static List normalized(List path) + { + // Find the min and max values for each axis + float minX = path.Min(v => v.translation.x); + float minY = path.Min(v => v.translation.y); + float minZ = path.Min(v => v.translation.z); + + float maxX = path.Max(v => v.translation.x); + float maxY = path.Max(v => v.translation.y); + float maxZ = path.Max(v => v.translation.z); + + // Compute the range for each axis + float rangeX = maxX - minX; + float rangeY = maxY - minY; + float rangeZ = maxZ - minZ; + + // Find the largest range to maintain the aspect ratio + float largestRange = Mathf.Max(rangeX, rangeY, rangeZ); + + // If the largest range is zero, return the original path to avoid division by zero + if (largestRange == 0) + { + return path; + } + + // Compute the uniform scale factor + float scaleFactor = 1 / largestRange; + + // Calculate the center of the original path + Vector3 center = new Vector3( + (minX + maxX) / 2, + (minY + maxY) / 2, + (minZ + maxZ) / 2 + ); + + // Apply the scale factor to each Vector3 in the input list + return path.Select(tr => TrTransform.TRS( + (tr.translation - center) * scaleFactor, + tr.rotation, + tr.scale + )).ToList(); + } + + public static List resample(List path, float spacing) + { + if (path == null || path.Count < 2 || spacing <= 0) + { + return new List(path); + } + + List resampledPath = new List(); + resampledPath.Add(path[0]); + + float accumulatedDistance = 0f; + int originalPathIndex = 0; + var startPoint = path[0]; + + while (originalPathIndex < path.Count - 1) + { + var endPoint = path[originalPathIndex + 1]; + float segmentDistance = Vector3.Distance(startPoint.translation, endPoint.translation); + float remainingDistance = segmentDistance - accumulatedDistance; + + if (accumulatedDistance + segmentDistance >= spacing) + { + float interpolationFactor = (spacing - accumulatedDistance) / segmentDistance; + Vector3 newTranslation = Vector3.Lerp(startPoint.translation, endPoint.translation, interpolationFactor); + Quaternion newRotation = Quaternion.Lerp(startPoint.rotation, endPoint.rotation, interpolationFactor); + float newScale = Mathf.Lerp(startPoint.scale, endPoint.scale, interpolationFactor); + var newPoint = TrTransform.TRS(newTranslation, newRotation, newScale); + resampledPath.Add(newPoint); + startPoint = newPoint; + accumulatedDistance = 0f; + } + else + { + accumulatedDistance += segmentDistance; + startPoint = endPoint; + originalPathIndex++; + } + } + resampledPath.Add(path[^1]); + return resampledPath; + } } [MoonSharpUserData]