Skip to content
This repository has been archived by the owner on Mar 27, 2024. It is now read-only.

Commit

Permalink
1.0.10
Browse files Browse the repository at this point in the history
- Add fixed-length even spacing mode (will evenly space out objects along the full length of the line with spacing as close as possible to the specified distance).
  • Loading branch information
algernon-A committed Dec 10, 2023
1 parent 913d532 commit 5db1edd
Show file tree
Hide file tree
Showing 14 changed files with 171 additions and 42 deletions.
2 changes: 1 addition & 1 deletion BepInEx/Plugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace LineTool
/// <summary>
/// BepInEx plugin to substitute for IMod support.
/// </summary>
[BepInPlugin(GUID, "Line Tool Lite", "1.0.9")]
[BepInPlugin(GUID, "Line Tool Lite", "1.0.10")]
[HarmonyPatch]
public class Plugin : BaseUnityPlugin
{
Expand Down
4 changes: 2 additions & 2 deletions BepInEx/manifest.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"name": "Line_Tool_Lite",
"version_number": "1.0.9",
"version_number": "1.0.10",
"website_url": "https://github.com/algernon-A/LineToolLite",
"description": "Place objects in lines, curves, or circles",
"description": "Place objects in lines, curves, or circles. A variety of options and controls are availalbe to specify and fine-tune results.",
"dependencies": [
"BepInEx-BepInExPack-5.4.2100"
]
Expand Down
3 changes: 3 additions & 0 deletions Changelog.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
1.0.10
- Add fixed-length even spacing mode (will evenly space out objects along the full length of the line with spacing as close as possible to the specified distance).

1.0.9
- Continuing a curve with shift-click now also locks the starting tangent of the new curve (continuous curves).

Expand Down
12 changes: 6 additions & 6 deletions Code/LineModes/Circle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,15 @@ public Circle(LineBase mode)
/// Calculates the points to use based on this mode.
/// </summary>
/// <param name="currentPos">Selection current position.</param>
/// <param name="fenceMode">Set to <c>true</c> if fence mode is active.</param>
/// <param name="spacing">Spacing setting.</param>
/// <param name="spacingMode">Active spacing mode.</param>
/// <param name="spacing">Spacing distance.</param>
/// <param name="randomSpacing">Random spacing offset maximum.</param>
/// <param name="randomOffset">Random lateral offset maximum.</param>
/// <param name="rotation">Rotation setting.</param>
/// <param name="zBounds">Prefab zBounds.</param>
/// <param name="pointList">List of points to populate.</param>
/// <param name="heightData">Terrain height data reference.</param>
public override void CalculatePoints(float3 currentPos, bool fenceMode, float spacing, float randomSpacing, float randomOffset, int rotation, Bounds1 zBounds, NativeList<PointData> pointList, ref TerrainHeightData heightData)
public override void CalculatePoints(float3 currentPos, SpacingMode spacingMode, float spacing, float randomSpacing, float randomOffset, int rotation, Bounds1 zBounds, NativeList<PointData> pointList, ref TerrainHeightData heightData)
{
// Don't do anything if we don't have valid start.
if (!m_validStart)
Expand All @@ -49,7 +49,7 @@ public override void CalculatePoints(float3 currentPos, bool fenceMode, float sp

// Calculate spacing.
float circumference = radius * math.PI * 2f;
float numPoints = math.floor(circumference / spacing);
float numPoints = spacingMode == SpacingMode.FullLength ? math.round(circumference / spacing) : math.floor(circumference / spacing);
float increment = (math.PI * 2f) / numPoints;
float startAngle = math.atan2(difference.z, difference.x);
System.Random random = new ((int)circumference * 1000);
Expand All @@ -59,7 +59,7 @@ public override void CalculatePoints(float3 currentPos, bool fenceMode, float sp
{
// Apply spacing adjustment.
float adjustedAngle = i;
if (randomSpacing > 0f && !fenceMode)
if (randomSpacing > 0f && spacingMode != SpacingMode.FenceMode)
{
float distanceAdjustment = (float)(random.NextDouble() * randomSpacing * 2f) - randomSpacing;
adjustedAngle += (distanceAdjustment * math.PI * 2f) / circumference;
Expand All @@ -71,7 +71,7 @@ public override void CalculatePoints(float3 currentPos, bool fenceMode, float sp
float3 thisPoint = new (m_startPos.x + xPos, m_startPos.y, m_startPos.z + yPos);

// Apply offset adjustment.
if (randomOffset > 0f && !fenceMode)
if (randomOffset > 0f && spacingMode != SpacingMode.FenceMode)
{
thisPoint += math.normalize(thisPoint - m_startPos) * ((float)(randomOffset * random.NextDouble() * 2f) - randomOffset);
}
Expand Down
39 changes: 28 additions & 11 deletions Code/LineModes/LineBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,15 +92,15 @@ public virtual void ItemsPlaced(float3 position)
/// Calculates the points to use based on this mode.
/// </summary>
/// <param name="currentPos">Selection current position.</param>
/// <param name="fenceMode">Set to <c>true</c> if fence mode is active.</param>
/// <param name="spacing">Spacing setting.</param>
/// <param name="spacingMode">Active spacing mode.</param>
/// <param name="spacing">Spacing distance.</param>
/// <param name="randomSpacing">Random spacing offset maximum.</param>
/// <param name="randomOffset">Random lateral offset maximum.</param>
/// <param name="rotation">Rotation setting.</param>
/// <param name="zBounds">Prefab zBounds.</param>
/// <param name="pointList">List of points to populate.</param>
/// <param name="heightData">Terrain height data reference.</param>
public virtual void CalculatePoints(float3 currentPos, bool fenceMode, float spacing, float randomSpacing, float randomOffset, int rotation, Bounds1 zBounds, NativeList<PointData> pointList, ref TerrainHeightData heightData)
public virtual void CalculatePoints(float3 currentPos, SpacingMode spacingMode, float spacing, float randomSpacing, float randomOffset, int rotation, Bounds1 zBounds, NativeList<PointData> pointList, ref TerrainHeightData heightData)
{
// Don't do anything if we don't have a valid start point.
if (!m_validStart)
Expand All @@ -115,30 +115,37 @@ public virtual void CalculatePoints(float3 currentPos, bool fenceMode, float spa

// Calculate applied rotation (in radians).
float appliedRotation = math.radians(rotation);
if (fenceMode)
if (spacingMode == SpacingMode.FenceMode)
{
appliedRotation = math.atan2(difference.x, difference.z);
}

// Rotation quaternion.
quaternion rotationQuaternion = quaternion.Euler(0f, appliedRotation, 0f);
quaternion qRotation = quaternion.Euler(0f, appliedRotation, 0f);

// Calculate even full-length spacing if needed.
float adjustedSpacing = spacing;
if (spacingMode == SpacingMode.FullLength)
{
adjustedSpacing = length / math.round(length / spacing);
}

// Create points.
float currentDistance = fenceMode ? -zBounds.min : 0f;
float endLength = fenceMode ? length - zBounds.max : length;
float currentDistance = spacingMode == SpacingMode.FenceMode ? -zBounds.min : 0f;
float endLength = spacingMode == SpacingMode.FenceMode ? length - zBounds.max : length;
while (currentDistance < endLength)
{
// Calculate interpolated point.
float spacingAdjustment = 0f;
if (randomSpacing > 0f && !fenceMode)
if (randomSpacing > 0f && spacingMode != SpacingMode.FenceMode)
{
spacingAdjustment = (float)(random.NextDouble() * randomSpacing * 2f) - randomSpacing;
}

float3 thisPoint = math.lerp(m_startPos, currentPos, (currentDistance + spacingAdjustment) / length);

// Apply offset adjustment.
if (randomOffset > 0f && !fenceMode)
if (randomOffset > 0f && spacingMode != SpacingMode.FenceMode)
{
float3 left = math.normalize(new float3(-difference.z, 0f, difference.x));
thisPoint += left * ((float)(randomOffset * random.NextDouble() * 2f) - randomOffset);
Expand All @@ -147,8 +154,18 @@ public virtual void CalculatePoints(float3 currentPos, bool fenceMode, float spa
thisPoint.y = TerrainUtils.SampleHeight(ref heightData, thisPoint);

// Add point to list.
pointList.Add(new PointData { Position = thisPoint, Rotation = rotationQuaternion, });
currentDistance += spacing;
pointList.Add(new PointData { Position = thisPoint, Rotation = qRotation, });
currentDistance += adjustedSpacing;
}

// Final item for full-length mode if required (if there was a distance overshoot).
if (spacingMode == SpacingMode.FullLength && currentDistance < length + adjustedSpacing)
{
float3 thisPoint = currentPos;
thisPoint.y = TerrainUtils.SampleHeight(ref heightData, thisPoint);

// Add point to list.
pointList.Add(new PointData { Position = thisPoint, Rotation = qRotation, });
}
}

Expand Down
39 changes: 30 additions & 9 deletions Code/LineModes/SimpleCurve.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,15 @@ public override void ItemsPlaced(float3 position)
/// Calculates the points to use based on this mode.
/// </summary>
/// <param name="currentPos">Selection current position.</param>
/// <param name="fenceMode">Set to <c>true</c> if fence mode is active.</param>
/// <param name="spacing">Spacing setting.</param>
/// <param name="spacingMode">Active spacing mode.</param>
/// <param name="spacing">Spacing distance.</param>
/// <param name="randomSpacing">Random spacing offset maximum.</param>
/// <param name="randomOffset">Random lateral offset maximum.</param>
/// <param name="rotation">Rotation setting.</param>
/// <param name="zBounds">Prefab zBounds.</param>
/// <param name="pointList">List of points to populate.</param>
/// <param name="heightData">Terrain height data reference.</param>
public override void CalculatePoints(float3 currentPos, bool fenceMode, float spacing, float randomSpacing, float randomOffset, int rotation, Bounds1 zBounds, NativeList<PointData> pointList, ref TerrainHeightData heightData)
public override void CalculatePoints(float3 currentPos, SpacingMode spacingMode, float spacing, float randomSpacing, float randomOffset, int rotation, Bounds1 zBounds, NativeList<PointData> pointList, ref TerrainHeightData heightData)
{
// Don't do anything if we don't have valid start.
if (!m_validStart)
Expand All @@ -105,24 +105,34 @@ public override void CalculatePoints(float3 currentPos, bool fenceMode, float sp
if (!_validElbow)
{
// Constrain as required.
base.CalculatePoints(ConstrainPos(currentPos), fenceMode, spacing, randomSpacing, randomOffset, rotation, zBounds, pointList, ref heightData);
base.CalculatePoints(ConstrainPos(currentPos), spacingMode, spacing, randomSpacing, randomOffset, rotation, zBounds, pointList, ref heightData);
return;
}

// Calculate Bezier.
_thisBezier = NetUtils.FitCurve(new Line3.Segment(m_startPos, _elbowPoint), new Line3.Segment(currentPos, _elbowPoint));

// Calculate even full-length spacing if needed.
float adjustedSpacing = spacing;
float length = MathUtils.Length(_thisBezier);
if (spacingMode == SpacingMode.FullLength)
{
adjustedSpacing = length / math.round(length / spacing);
}

// Default rotation quaternion.
quaternion qRotation = quaternion.Euler(0f, math.radians(rotation), 0f);

// Randomizer.
System.Random random = new ((int)(currentPos.x + currentPos.z) * 1000);

float tFactor = 0f;
float distanceTravelled = 0f;
while (tFactor < 1.0f)
{
// Apply spacing randomization.
float adjustedT = tFactor;
if (randomSpacing > 0f && !fenceMode)
if (randomSpacing > 0f && spacingMode != SpacingMode.FenceMode)
{
float spacingAdjustment = (float)(random.NextDouble() * randomSpacing * 2f) - randomSpacing;
adjustedT = spacingAdjustment < 0f ? BezierStepReverse(tFactor, spacingAdjustment) : BezierStep(tFactor, spacingAdjustment);
Expand All @@ -132,18 +142,19 @@ public override void CalculatePoints(float3 currentPos, bool fenceMode, float sp
float3 thisPoint = MathUtils.Position(_thisBezier, adjustedT);

// Apply offset randomization.
if (randomOffset > 0f && !fenceMode)
if (randomOffset > 0f && spacingMode != SpacingMode.FenceMode)
{
float3 tangent = MathUtils.Tangent(_thisBezier, adjustedT);
float3 left = math.normalize(new float3(-tangent.z, 0f, tangent.x));
thisPoint += left * ((float)(randomOffset * random.NextDouble() * 2f) - randomOffset);
}

// Get next t factor.
tFactor = BezierStep(tFactor, spacing);
tFactor = BezierStep(tFactor, adjustedSpacing);
distanceTravelled += adjustedSpacing;

// Calculate applied rotation for fence mode.
if (fenceMode)
if (spacingMode == SpacingMode.FenceMode)
{
float3 difference = MathUtils.Position(_thisBezier, tFactor) - thisPoint;
qRotation = quaternion.Euler(0f, math.atan2(difference.x, difference.z), 0f);
Expand All @@ -155,6 +166,16 @@ public override void CalculatePoints(float3 currentPos, bool fenceMode, float sp
// Add point to list.
pointList.Add(new PointData { Position = thisPoint, Rotation = qRotation, });
}

// Final item for full-length mode if required (if there was a distance overshoot).
if (spacingMode == SpacingMode.FullLength && distanceTravelled < length + adjustedSpacing)
{
float3 thisPoint = currentPos;
thisPoint.y = TerrainUtils.SampleHeight(ref heightData, thisPoint);

// Add point to list.
pointList.Add(new PointData { Position = thisPoint, Rotation = qRotation, });
}
}

/// <summary>
Expand Down Expand Up @@ -209,7 +230,7 @@ public override void Reset()

/// <summary>
/// Applies any active constraints the given current cursor world position.
/// /// </summary>
/// </summary>
/// <param name="currentPos">Current cursor world position.</param>
/// <returns>Constrained cursor world position.</returns>
private float3 ConstrainPos(float3 currentPos)
Expand Down
27 changes: 27 additions & 0 deletions Code/LineModes/SpacingMode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// <copyright file="SpacingMode.cs" company="algernon (K. Algernon A. Sheppard)">
// Copyright (c) algernon (K. Algernon A. Sheppard). All rights reserved.
// </copyright>

namespace LineTool
{
/// <summary>
/// Line tool modes.
/// </summary>
public enum SpacingMode
{
/// <summary>
/// Manually spaced.
/// </summary>
Manual,

/// <summary>
/// Fence mode.
/// </summary>
FenceMode,

/// <summary>
/// Evenly spaced along entire length of line.
/// </summary>
FullLength,
}
}
24 changes: 16 additions & 8 deletions Code/Systems/LineToolSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ public sealed partial class LineToolSystem : ObjectToolBaseSystem

// Line calculations.
private readonly NativeList<PointData> _points = new (Allocator.Persistent);
private bool _fenceMode = false;
private bool _fixedPreview = false;
private float3 _fixedPos;
private Random _random = new ();
Expand Down Expand Up @@ -70,6 +69,7 @@ public sealed partial class LineToolSystem : ObjectToolBaseSystem
private LineBase _mode;

// Tool settings.
private SpacingMode _spacingMode = SpacingMode.Manual;
private float _spacing = 20f;
private bool _randomRotation = false;
private int _rotation = 0;
Expand Down Expand Up @@ -105,17 +105,17 @@ internal float Spacing
/// <summary>
/// Gets the effective spacing value, taking into account fence mode.
/// </summary>
internal float EffectiveSpacing => _fenceMode ? _zBounds.max - _zBounds.min : _spacing;
internal float EffectiveSpacing => _spacingMode == SpacingMode.FenceMode ? _zBounds.max - _zBounds.min : _spacing;

/// <summary>
/// Gets or sets a value indicating whether fence mode is active.
/// Gets or sets the current spacing mode.
/// </summary>
internal bool FenceMode
internal SpacingMode CurrentSpacingMode
{
get => _fenceMode;
get => _spacingMode;
set
{
_fenceMode = value;
_spacingMode = value;
_dirty = true;
}
}
Expand Down Expand Up @@ -481,7 +481,7 @@ protected override JobHandle OnUpdate(JobHandle inputDeps)
// Update cursor entity if we haven't got an initial position set.
if (!_mode.HasStart)
{
// Create cursor entity if none yet exists.
// Delete any existing cursor entity and create a new one.
if (_cursorEntity != Entity.Null)
{
EntityManager.AddComponent<Deleted>(_cursorEntity);
Expand All @@ -508,6 +508,14 @@ protected override JobHandle OnUpdate(JobHandle inputDeps)
_cursorEntity = Entity.Null;
}
}
else
{
// No valid raycast - hide cursor.
if (_cursorEntity != Entity.Null)
{
EntityManager.AddComponent<Deleted>(_cursorEntity);
}
}

// Render any overlay.
_mode.DrawOverlay(position, _overlayBuffer, _tooltips);
Expand All @@ -525,7 +533,7 @@ protected override JobHandle OnUpdate(JobHandle inputDeps)

// If we got here we're (re)calculating points.
_points.Clear();
_mode.CalculatePoints(position, _fenceMode, EffectiveSpacing, RandomSpacing, RandomOffset, _rotation, _zBounds, _points, ref _terrainHeightData);
_mode.CalculatePoints(position, _spacingMode, EffectiveSpacing, RandomSpacing, RandomOffset, _rotation, _zBounds, _points, ref _terrainHeightData);

// Clear all preview entities.
foreach (Entity entity in _previewEntities)
Expand Down
Loading

0 comments on commit 5db1edd

Please sign in to comment.