You can solve this by getting the current radius of p3 to the line and scaling that to the desired radius.
Shadertoy (GLSL) code:
float sdSegment( in vec2 p, in vec2 a, in vec2 b ) // from: https://iquilezles.org/articles/distfunctions2d/
{ vec2 pa = p-a, ba = b-a; float h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 ); return length( pa - ba*h ); }
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
// Centered uv from -1 to 1
vec2 uv = vec2((fragCoord.x - 0.5*iResolution.x) / (0.5*iResolution.y), 2. * fragCoord.y / iResolution.y - 1.);
vec2 muv = vec2((iMouse.x - 0.5*iResolution.x) / (0.5*iResolution.y), 2. * iMouse.y / iResolution.y - 1.);
float uvscale = 10.; uv *= uvscale, muv *= uvscale; // scale uv
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Points
vec2 p1 = vec2(-12.37, -7.65), p2 = vec2(15.64, 4.07); // line points
vec2 p3 = vec2(2.8, 8.73); // player point
vec2 A = p1 + 0.3 * (p2 - p1); // A
// Project p3 onto line p1p2
float t = dot(p3 - p1, normalize(p2 - p1)); // time of p3 on line p1p2
vec2 D = p1 + t * normalize(p2 - p1); // projected point
// Radius (input)
float radius = iMouse.x < 1.5 ? 5. : 10. * iMouse.x / iResolution.x; // arbitrary value chosen by user
radius = clamp(radius, 0., distance(p3, D)); // clamps radius to range of p3 to A; not necessary
// Get scale by dividing the desired radius by the distance of p3 to the projected point (i.e. the "current" radius)
float scale = radius / distance(p3, D);
// Scale line *AD* (A to the projected point) by that value; this gives point C
vec2 C = A + scale * (D - A);
// Point B (output) is C offset by the normal of the line (normalized vector of D to p3) multiplied by the desired radius
vec2 B = C + radius * normalize(p3 - D);
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Pixel color
vec3 col = vec3(1.);
// Draw lines
if (sdSegment(uv, p1, p2) < 0.01 * uvscale) col = vec3(0.);
if (sdSegment(uv, p3, p3 + 1.3 * (A - p3)) < 0.01 * uvscale) col = vec3(1., 0., 0.);
if (sdSegment(uv, C, B) < 0.01 * uvscale) col = vec3(0., 1., 0.);
// Draw circle (proof)
if (abs(distance(uv, B) - radius) < 0.01 * uvscale) col = vec3(0., 0., 1.);
// Draw points
//if (distance(uv, p1) < 0.03 * uvscale) col = vec3(0., 0., 0.);
//if (distance(uv, p2) < 0.03 * uvscale) col = vec3(0., 0., 0.);
//if (distance(uv, p3) < 0.03 * uvscale) col = vec3(1., 0., 0.);
if (distance(uv, A) < 0.03 * uvscale) col = vec3(1., 0., 0.);
//if (distance(uv, D) < 0.03 * uvscale) col = vec3(1., 0., 0.);
if (distance(uv, C) < 0.03 * uvscale) col = vec3(0., 1., 0.);
if (distance(uv, B) < 0.03 * uvscale) col = vec3(0., 0., 1.);
// Output to screen
fragColor = vec4(col, 1.);
}
Result
If you don’t know what to do with this, just paste it into https://www.shadertoy.com/new to view & play with it. Moving the mouse left & right will change the radius.
For the code itself, you only need what’s in the commented bars. It’s in GLSL, not C#, but the important part is the math, so it should be easy to convert. It should also work in 3D.
For those who would like to know, here’s the logic behind it:
If we project a point we already know onto line p1p2 (e.g. p3), we will get a projected point on the line with a distance to the original point. If we treat this distance as a radius, then we now have an undesired point (p3) at an undesired radius (distance of p3 to the projected point). So, all we have to do is convert that undesired radius to a desired one. We can do this by scaling the projected point along line p1p2 such that the radius (i.e. the distance from point C to line p3A) is equal to our desired one. Point B is then obtained by simply offsetting point C by the line’s “normal” (normalized vector of the projected point to p3) scaled by the desired radius.