algorithms/inversion.js

/**
 * Created by Alex Bol on 3/7/2017.
 */
"use strict";

import Flatten from "../flatten";
import * as Utils from '../utils/utils';

/**
 * Inversion is a transformation of the Euclidean plane that maps generalized circles
 * (where line is considered as a circle with infinite radius) into generalized circles
 * See also https://en.wikipedia.org/wiki/Inversive_geometry and
 * http://mathworld.wolfram.com/Inversion.html <br/>
 * Inversion also may be considered as a reflection of the point in the plane with respect
 * to inversion circle so that R^2 = OP * OP',
 * where <br/>
 * O - center of inversion circle <br/>
 * R - radius of inversion circle <br/>
 * P - point of plane <br/>
 * P' - inversion of the point P
 *
 * @param {Line | Circle} shape - shape to be transformed
 * @param {Circle} inversion_circle - inversion circle
 * @returns {Line | Circle} - result of transformation
 */
function inverse(shape, inversion_circle) {
    let dist, shortest_segment;
    let dx, dy;
    let s;
    let v;
    let r;
    let d;
    let pt;

    if (shape instanceof Flatten.Line) {
        [dist, shortest_segment] = inversion_circle.pc.distanceTo(shape);
        if (Utils.EQ_0(dist)) {            // Line passing through inversion center, is mapping to itself
            return shape.clone();
        } else {                           // Line not passing through inversion center is mapping into circle
            r = inversion_circle.r * inversion_circle.r / (2 * dist);
            v = new Flatten.Vector(inversion_circle.pc, shortest_segment.end);
            v = v.multiply(r / dist);
            return new Flatten.Circle(inversion_circle.pc.translate(v), r);
        }
    } else if (shape instanceof Flatten.Circle) {
        [dist, shortest_segment] = inversion_circle.pc.distanceTo(shape.pc);
        if (Utils.EQ(dist, shape.r)) {     // Circle passing through inversion center mapped into line
            d = inversion_circle.r * inversion_circle.r / (2 * shape.r);
            v = new Flatten.Vector(shape.pc, inversion_circle.pc);
            v = v.normalize();
            pt = inversion_circle.pc.translate(v.multiply(d));
            return new Flatten.Line(pt, v);
        } else {                           // Circle not passing through inversion center - map into another circle */
            /* Taken from http://mathworld.wolfram.com */

            dx = shape.pc.x - inversion_circle.pc.x;
            dy = shape.pc.y - inversion_circle.pc.y;

            s = inversion_circle.r * inversion_circle.r / (dx * dx + dy * dy - shape.r * shape.r);

            let pc = new Flatten.Point(inversion_circle.pc.x + s * dx, inversion_circle.pc.y + s * dy);

            return new Flatten.Circle(pc, Math.abs(s) * shape.r);
        }
    }
};

export default inverse;