public override void NormalizeCylindricCoordinates(ShapeCoordinates coords) { CalculateVolume(); // Update the bezier curve control nodes // The Bezier curve control points are NOT normalized. // p0 = new Vector2(bottomDiameter, -length / 2f); // p3 = new Vector2(topDiameter, length / 2f); // But we do need to normalize the length and shift such that 0 <= t <= 1 float t = (coords.y / length) + (1f / 2); Vector2 Bt = B(t); Debug.Log($"{ModTag} Normalized {coords.y} to {t} (0=bottom, 1=top), B(t)={Bt} as (diameter, length). TopDiameter:{topDiameter} BotDiameter:{bottomDiameter} Len: {length}"); // For a given normalized length (t), B(t).x is the diameter of the surface cross-section at that length, and B(t).y is .. what? coords.y /= length; coords.r /= (Bt.x / 2); }
public abstract Vector3 FromCylindricCoordinates(ShapeCoordinates coords);
public abstract void GetCylindricCoordinates(Vector3 position, ShapeCoordinates coords);
public override Vector3 FromCylindricCoordinates(ShapeCoordinates coords) { Vector3 position = new Vector3(); switch (coords.HeightMode) { case ShapeCoordinates.YMode.RELATIVE_TO_SHAPE: float halfLength = (lastProfile.Last.Value.y - lastProfile.First.Value.y) / 2.0f; position.y = halfLength * coords.y; break; case ShapeCoordinates.YMode.OFFSET_FROM_SHAPE_CENTER: position.y = coords.y; break; case ShapeCoordinates.YMode.OFFSET_FROM_SHAPE_BOTTOM: position.y = coords.y + lastProfile.First.Value.y; break; case ShapeCoordinates.YMode.OFFSET_FROM_SHAPE_TOP: position.y = coords.y + lastProfile.Last.Value.y; break; default: Debug.LogError("Can not handle PartCoordinate attribute: " + coords.HeightMode); position.y = 0.0f; break; } float radius = coords.r; if (coords.RadiusMode != ShapeCoordinates.RMode.OFFSET_FROM_SHAPE_CENTER) { if (position.y < lastProfile.First.Value.y) radius = coords.RadiusMode == ShapeCoordinates.RMode.OFFSET_FROM_SHAPE_RADIUS ? lastProfile.First.Value.dia / 2.0f + coords.r : radius = lastProfile.First.Value.dia / 2.0f * coords.r; else if (position.y > lastProfile.Last.Value.y) radius = coords.RadiusMode == ShapeCoordinates.RMode.OFFSET_FROM_SHAPE_RADIUS ? lastProfile.Last.Value.dia / 2.0f + coords.r : radius = lastProfile.Last.Value.dia / 2.0f * coords.r; else { ProfilePoint pt = lastProfile.First.Value; for (LinkedListNode<ProfilePoint> ptNode = lastProfile.First.Next; ptNode != null; ptNode = ptNode.Next) { if (!ptNode.Value.inCollider) continue; ProfilePoint pv = pt; pt = ptNode.Value; if (position.y >= Mathf.Min(pv.y, pt.y) && position.y < Mathf.Max(pv.y, pt.y)) { float t = Mathf.InverseLerp(Mathf.Min(pv.y, pt.y), Mathf.Max(pv.y, pt.y), position.y); float profileRadius = Mathf.Lerp(pv.dia, pt.dia, t) / 2.0f; radius = coords.RadiusMode == ShapeCoordinates.RMode.OFFSET_FROM_SHAPE_RADIUS ? profileRadius + coords.r : radius = profileRadius * coords.r; } } } } float theta = Mathf.Lerp(0, Mathf.PI * 2f, coords.u); position.x = Mathf.Cos(theta) * radius; position.z = -Mathf.Sin(theta) * radius; return position; }
public override void GetCylindricCoordinates(Vector3 position, ShapeCoordinates result) { Vector2 direction = new Vector2(position.x, position.z); switch(result.HeightMode) { case ShapeCoordinates.YMode.RELATIVE_TO_SHAPE: float halfLength = (lastProfile.Last.Value.y - lastProfile.First.Value.y) / 2.0f; result.y = position.y / halfLength; break; case ShapeCoordinates.YMode.OFFSET_FROM_SHAPE_CENTER: result.y = position.y; break; case ShapeCoordinates.YMode.OFFSET_FROM_SHAPE_BOTTOM: result.y = position.y - lastProfile.First.Value.y; break; case ShapeCoordinates.YMode.OFFSET_FROM_SHAPE_TOP: result.y = position.y - lastProfile.Last.Value.y; break; default: Debug.LogError("Can not handle PartCoordinate attribute: " + result.HeightMode); result.y = 0.0f; break; } result.r = 0; float theta = Mathf.Atan2(-direction.y, direction.x); result.u = (Mathf.InverseLerp(-Mathf.PI, Mathf.PI, theta) + 0.5f) % 1.0f; if(result.RadiusMode == ShapeCoordinates.RMode.OFFSET_FROM_SHAPE_CENTER) { result.r = direction.magnitude; return; } if (position.y <= lastProfile.First.Value.y) result.r = result.RadiusMode == ShapeCoordinates.RMode.OFFSET_FROM_SHAPE_RADIUS ? direction.magnitude - lastProfile.First.Value.dia / 2.0f : direction.magnitude / (lastProfile.First.Value.dia / 2.0f); // RELATIVE_TO_SHAPE_RADIUS else if (position.y >= lastProfile.Last.Value.y) result.r = result.RadiusMode == ShapeCoordinates.RMode.OFFSET_FROM_SHAPE_RADIUS ? direction.magnitude - lastProfile.Last.Value.dia / 2.0f : direction.magnitude / (lastProfile.Last.Value.dia / 2.0f); // RELATIVE_TO_SHAPE_RADIUS else { ProfilePoint pt = lastProfile.First.Value; for (LinkedListNode<ProfilePoint> ptNode = lastProfile.First.Next; ptNode != null; ptNode = ptNode.Next) { if (!ptNode.Value.inCollider) continue; ProfilePoint pv = pt; pt = ptNode.Value; if(position.y >= Mathf.Min(pv.y, pt.y) && position.y < Mathf.Max(pv.y, pt.y)) { float t = Mathf.InverseLerp(Mathf.Min(pv.y, pt.y), Mathf.Max(pv.y, pt.y), position.y); float r = Mathf.Lerp(pv.dia, pt.dia, t) / 2.0f; result.r = result.RadiusMode == ShapeCoordinates.RMode.OFFSET_FROM_SHAPE_RADIUS ? direction.magnitude - r : direction.magnitude / r; } } } // sometimes, if the shapes radius is 0, r rersults in NaN if (float.IsNaN(result.r)) { result.r = 0; } }