/** * Copyright (c) 2011-2018 by Andrew Mustun. All rights reserved. * * This file is part of the QCAD project. * * QCAD is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * QCAD is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with QCAD. */ include("scripts/library.js"); include("scripts/ShapeAlgorithms.js"); /** * \class Apollonius implementation for fining tangent circles to * three shapes (points, lines, circles). */ function Apollonius() { } Apollonius.constructionShapes = []; /** * \return Solutions for circles (<=8) that are tangential to the three given shapes. */ Apollonius.getSolutions = function(shape1, shape2, shape3) { if (isArray(shape1) && isNull(shape2) && isNull(shape3)) { if (shape1.length!==3) { debugger; return []; } return Apollonius.getSolutions(shape1[0], shape1[1], shape1[2]); } var points = []; var lines = []; var circles = []; var shapes = [ shape1, shape2, shape3 ]; for (var i=0; i0 && nIps13>0 && nIps23>0 && (nIps12===2 || nIps13===2 || nIps23===2)) { var ips12 = circle1.getIntersectionPoints(circle2); var ips13 = circle1.getIntersectionPoints(circle3); var ips23 = circle2.getIntersectionPoints(circle3); var inversionCircles = []; var r; if (ips12.length===2) { r = ips12[0].getDistanceTo(ips12[1]); inversionCircles.push(new RCircle(ips12[0], r)); inversionCircles.push(new RCircle(ips12[1], r)); } if (ips13.length===2) { r = ips13[0].getDistanceTo(ips13[1]); inversionCircles.push(new RCircle(ips13[0], r)); inversionCircles.push(new RCircle(ips13[1], r)); } if (ips23.length===2) { r = ips23[0].getDistanceTo(ips23[1]); inversionCircles.push(new RCircle(ips23[0], r)); inversionCircles.push(new RCircle(ips23[1], r)); } for (var i=0; i0) { ps.push(arr1[0]); } if (arr2.length>0) { ps.push(arr2[0]); } if (arr3.length>0) { ps.push(arr3[0]); } if (ps.length>=2) { return new RLine(ps[0], ps[1]); } return undefined; }; /** * \return Power center of the three given circles. */ Apollonius.getPowerCenter = function(c1, c2, c3) { var radicalAxis1 = Apollonius.getRadicalAxis(c1, c2); //Apollonius.constructionShapes.push(radicalAxis1); var radicalAxis2 = Apollonius.getRadicalAxis(c1, c3); //Apollonius.constructionShapes.push(radicalAxis2); var ips = radicalAxis1.getIntersectionPoints(radicalAxis2, false); if (ips.length===0) { return undefined; } return ips[0]; }; /** * \return Radical axis of circles c1 and c2 with given length. */ Apollonius.getRadicalAxis = function(c1, c2, length) { if (isNull(length)) { length = 100.0; } var c2c = new RLine(c1.center, c2.center); var dir = c2c.getAngle() + Math.PI/2; var center; var ips = c1.getIntersectionPoints(c2, false); if (ips.length===2) { center = RVector.getAverage(ips[0], ips[1]); } else { //Apollonius.constructionShapes.push(c2c); var v = RVector.createPolar(c1.radius, c2c.getAngle() + Math.PI/2); var p1 = c1.center.operator_add(v); v = RVector.createPolar(c2.radius, c2c.getAngle() + Math.PI/2); var p3 = c2.center.operator_add(v); var p2 = c2c.getMiddlePoint(); var helperCircle = RCircle.createFrom3Points(p1, p2, p3); //Apollonius.constructionShapes.push(helperCircle); var ra1 = Apollonius.getRadicalAxis(c1, helperCircle); //Apollonius.constructionShapes.push(ra1); var ra2 = Apollonius.getRadicalAxis(c2, helperCircle); //Apollonius.constructionShapes.push(ra2); var rips = ra1.getIntersectionPoints(ra2, false); if (rips.length===0) { debugger; } center = c2c.getClosestPointOnShape(rips[0], false); } var dirV = RVector.createPolar(length*0.5, dir); return new RLine(center.operator_subtract(dirV), center.operator_add(dirV)); }; /** * \return Solutions for circles (<=4) that are tangential to the three given lines. */ Apollonius.getSolutionsLLL = function(line1, line2, line3) { if (!isLineBasedShape(line1) || !isLineBasedShape(line2) || !isLineBasedShape(line3)) { return []; } /* situations: 0: all lines are parallel (no solutions) 3: lines 2 and 3 are parallel 5: lines 1 and 3 are parallel 6: lines 1 and 2 are parallel 7: none of the lines are parallel (4 solutions) */ var situation = 0; var angleBisectors1, angleBisectors2, angleBisectors3; if (line1.intersectsWith(line2, false)) { situation+=1; } if (line1.intersectsWith(line3, false)) { situation+=2; } if (line2.intersectsWith(line3, false)) { situation+=4; } if (situation === 3 || situation === 5 || situation === 7) { angleBisectors1 = Apollonius.getAngleBisectors(line1, line2); } if (situation == 3 || situation == 6 || situation == 7) { angleBisectors2 = Apollonius.getAngleBisectors(line1, line3); } if (situation == 5 || situation == 6) { angleBisectors3 = Apollonius.getAngleBisectors(line2, line3); } var centerPoints = []; if (situation == 3 || situation == 7) { centerPoints = centerPoints.concat(angleBisectors1[0].getIntersectionPoints(angleBisectors2[0], false)); centerPoints = centerPoints.concat(angleBisectors1[0].getIntersectionPoints(angleBisectors2[1], false)); centerPoints = centerPoints.concat(angleBisectors1[1].getIntersectionPoints(angleBisectors2[0], false)); centerPoints = centerPoints.concat(angleBisectors1[1].getIntersectionPoints(angleBisectors2[1], false)); } else if (situation == 5) { centerPoints = centerPoints.concat(angleBisectors1[0].getIntersectionPoints(angleBisectors3[0], false)); centerPoints = centerPoints.concat(angleBisectors1[0].getIntersectionPoints(angleBisectors3[1], false)); centerPoints = centerPoints.concat(angleBisectors1[1].getIntersectionPoints(angleBisectors3[0], false)); centerPoints = centerPoints.concat(angleBisectors1[1].getIntersectionPoints(angleBisectors3[1], false)); } else if (situation == 6) { centerPoints = centerPoints.concat(angleBisectors2[0].getIntersectionPoints(angleBisectors3[0], false)); centerPoints = centerPoints.concat(angleBisectors2[0].getIntersectionPoints(angleBisectors3[1], false)); centerPoints = centerPoints.concat(angleBisectors2[1].getIntersectionPoints(angleBisectors3[0], false)); centerPoints = centerPoints.concat(angleBisectors2[1].getIntersectionPoints(angleBisectors3[1], false)); } var ret = []; var radius; var c; for (var i=0; i circle2.radius) { var tmp = circle1; circle1 = circle2; circle2 = tmp; } // find solutions for tangent to: // center point of smaller circle, // concentric circles for larger circle with distance = radius of smaller circle // parallels to line with distance = radius of smaller circle var arr1 = []; var arr2 = []; var arr3 = []; var arr4 = []; arr1.push(new RPoint(circle1.center)); arr2.push(new RPoint(circle1.center)); arr3.push(new RPoint(circle1.center)); arr4.push(new RPoint(circle1.center)); var circle21 = circle2.clone(); circle21.radius += circle1.radius; arr1.push(circle21); arr3.push(circle21); if (RMath.fuzzyCompare(circle1.radius, circle2.radius)) { arr2.push(new RPoint(circle2.center)); arr3.push(new RPoint(circle2.center)); } else { var circle22 = circle2.clone(); circle22.radius = Math.abs(circle22.radius - circle1.radius); arr2.push(circle22); arr4.push(circle22); } var parallels = ShapeAlgorithms.getOffsetShapes(line, circle1.radius, 1, RS.BothSides); arr1.push(parallels[0]); arr2.push(parallels[0]); arr3.push(parallels[1]); arr4.push(parallels[1]); var cArr1 = Apollonius.getSolutions(arr1[0], arr1[1], arr1[2]); var cArr2 = Apollonius.getSolutions(arr2[0], arr2[1], arr2[2]); var cArr3 = Apollonius.getSolutions(arr3[0], arr3[1], arr3[2]); var cArr4 = Apollonius.getSolutions(arr4[0], arr4[1], arr4[2]); // qDebug("cArr1:\n" + cArr1 + "\n\n"); // qDebug("cArr2:\n" + cArr2 + "\n\n"); // qDebug("cArr3:\n" + cArr3 + "\n\n"); // qDebug("cArr4:\n" + cArr4 + "\n\n"); var cArrs = [ cArr1, cArr2, cArr3, cArr4 ]; var ret = []; var tmpCircle, obj1, obj2; for (var i=0; i1) { p = ps[1]; } var ap = new RLine(a, p); var apOrtho = ap.copy(); apOrtho.rotate(Math.PI/2, ap.getMiddlePoint()); var tangents = Apollonius.getTangentsThroughPoint(efa, m); for (var k=0; k G, H: var ipsLC = line.getIntersectionPoints(circle, false); if (ipsLC.length!==2) { continue; } var g = ipsLC[0]; var h = ipsLC[1]; // two lines L1, L2 with same angle as EG, EH through point: var l1 = new RLine(e, g); var l2 = new RLine(e, h); l1.move(point.position.operator_subtract(l1.getStartPoint())); l2.move(point.position.operator_subtract(l2.getStartPoint())); // intersection of angle bisector and lines L1, L2 are centers of candidates: for (k=0; k 1) { ret[1] = Apollonius.getParallelLinesWithDistance(tangents2[1], c1.radius)[0]; ret[2] = Apollonius.getParallelLinesWithDistance(tangents2[0], c1.radius)[1]; } //Apollonius.constructionShapes = Apollonius.constructionShapes.concat(ret); return ret; }; Apollonius.getTangentsThroughPoint = function(circle, p) { if (!isCircleShape(circle)) { return []; } // used when creating tangential circles to two parallel lines and point: if (Math.abs(circle.radius) 1.0e6) { if (!preview) { Apollonius.error = qsTr("Invalid radius"); } return undefined; } var i,k,ips,s; if (isNull(candidates)) { candidates = []; } if (candidates.length===0) { var offset1 = ShapeAlgorithms.getOffsetShapes(shape1, radius, 1, RS.BothSides); var offset2 = ShapeAlgorithms.getOffsetShapes(shape2, radius, 1, RS.BothSides); if (isCircleShape(shape1) || isArcShape(shape1)) { if (radius>shape1.getRadius()) { s = shape1.clone(); s.setRadius(radius - shape1.getRadius()); offset1.push(s); } } if (isCircleShape(shape2) || isArcShape(shape2)) { if (radius>shape2.getRadius()) { s = shape2.clone(); s.setRadius(radius - shape2.getRadius()); offset2.push(s); } } var centerPoints = []; for (i=0; i