From 5c74d0df2f6e639506ce5cb219c52ff5c36a7228 Mon Sep 17 00:00:00 2001 From: Egor Boaghi Date: Tue, 26 Sep 2017 09:21:42 +0300 Subject: [PATCH 01/11] egorbwork challenge 6 implement data models --- challenge #6/egorbwork/CloudCircle.js | 99 ++++++++++++++++++++++++++ challenge #6/egorbwork/CloudTangent.js | 42 +++++++++++ challenge #6/egorbwork/Point.js | 20 ++++++ 3 files changed, 161 insertions(+) create mode 100644 challenge #6/egorbwork/CloudCircle.js create mode 100644 challenge #6/egorbwork/CloudTangent.js create mode 100644 challenge #6/egorbwork/Point.js diff --git a/challenge #6/egorbwork/CloudCircle.js b/challenge #6/egorbwork/CloudCircle.js new file mode 100644 index 0000000..91045fc --- /dev/null +++ b/challenge #6/egorbwork/CloudCircle.js @@ -0,0 +1,99 @@ +class CloudCircle { + /** + * @param {Point} centerPoint + * @param {number} radius + */ + constructor(centerPoint, radius) { + this.centerPoint = centerPoint; + this.radius = radius; + this.tangents = []; + this.tangentCircles = []; + } + + /** + * @param {CloudTangent} tangent + */ + addTangent(tangent) { + this.tangents.push(tangent); + } + + /** + * source: http://2000clicks.com/mathhelp/GeometryConicSectionCircleIntersection.aspx + * @param {CloudCircle} circle + */ + calculateCloudTangentsForCircle(circle) { + let distanceBetweenCircles = this.centerPoint.getDistanceFor(circle.centerPoint); + let areaOfCirclesTriangle = 0.25 * Math.sqrt( + ((this.radius + circle.radius) ** 2 - distanceBetweenCircles ** 2) + * (distanceBetweenCircles ** 2 - (this.radius - circle.radius) ** 2) + ); + let firstTangentPoint = new Point( + 0.5 * (circle.centerPoint.coordinateX + this.centerPoint.coordinateX) + + 0.5 * (circle.centerPoint.coordinateX - this.centerPoint.coordinateX) + * (this.radius ** 2 - circle.radius ** 2) / distanceBetweenCircles ** 2 + + 2 * (circle.centerPoint.coordinateY - this.centerPoint.coordinateY) * areaOfCirclesTriangle + / distanceBetweenCircles ** 2, + 0.5 * (circle.centerPoint.coordinateY + this.centerPoint.coordinateY) + + 0.5 * (circle.centerPoint.coordinateY - this.centerPoint.coordinateY) + * (this.radius ** 2 - circle.radius ** 2) / distanceBetweenCircles ** 2 + - 2 * (circle.centerPoint.coordinateX - this.centerPoint.coordinateX) * areaOfCirclesTriangle + / distanceBetweenCircles ** 2 + ); + let secondTangentPoint = new Point( + 0.5 * (circle.centerPoint.coordinateX + this.centerPoint.coordinateX) + + 0.5 * (circle.centerPoint.coordinateX - this.centerPoint.coordinateX) + * (this.radius ** 2 - circle.radius ** 2) / distanceBetweenCircles ** 2 + - 2 * (circle.centerPoint.coordinateY - this.centerPoint.coordinateY) * areaOfCirclesTriangle + / distanceBetweenCircles ** 2, + 0.5 * (circle.centerPoint.coordinateY + this.centerPoint.coordinateY) + + 0.5 * (circle.centerPoint.coordinateY - this.centerPoint.coordinateY) + * (this.radius ** 2 - circle.radius ** 2) / distanceBetweenCircles ** 2 + + 2 * (circle.centerPoint.coordinateX - this.centerPoint.coordinateX) * areaOfCirclesTriangle + / distanceBetweenCircles ** 2 + ); + let firstTangent = new CloudTangent(firstTangentPoint, this, circle); + this.addTangent(firstTangent) + circle.addTangent(firstTangent); + let secondTangent = new CloudTangent(secondTangentPoint, this, circle); + this.addTangent(secondTangent) + circle.addTangent(secondTangent); + this.tangentCircles.push(circle); + circle.tangentCircles.push(this); + } + + /** + * @param {CloudCircle} circle + * @returns {boolean} + */ + isTangentCircle(circle) { + let distanceBetweenCircles = this.centerPoint.getDistanceFor(circle.centerPoint); + return distanceBetweenCircles < (this.radius + circle.radius); + } + + /** + * @param {Point} point + * @returns {boolean} + */ + isPointInsideOfCircle(point) { + let distanceTillPoint = point.getDistanceFor(this.centerPoint); + return distanceTillPoint < this.radius; + } + + /** + * @returns {Array} + */ + getUsedTangents() { + return this.tangents.filter( + tangent => tangent.usageStatus + ); + } + + /** + * source: https://gamedev.stackexchange.com/questions/33709/get-angle-in-radians-given-a-point-on-a-circle + * @param {Point} point + * @returns {number} + */ + calculateArcAngleForPoint(point) { + return Math.atan2(point.coordinateY - this.centerPoint.coordinateY, point.coordinateX - this.centerPoint.coordinateX); + } +} diff --git a/challenge #6/egorbwork/CloudTangent.js b/challenge #6/egorbwork/CloudTangent.js new file mode 100644 index 0000000..e1a929d --- /dev/null +++ b/challenge #6/egorbwork/CloudTangent.js @@ -0,0 +1,42 @@ +class CloudTangent { + /** + * @param {Point} point + * @param {CloudCircle} firstCircle + * @param {CloudCircle} secondCircle + */ + constructor(point, firstCircle, secondCircle) { + this.point = point; + this.firstCircle = firstCircle; + this.secondCircle = secondCircle; + this.usageStatus = false; + this.drawnWith = []; + } + + /** + * @param {boolean} status + */ + setUsed(status) { + this.usageStatus = status; + } + + /** + * @param {CloudCircle} circle + * + * @returns {CloudCircle|null} + */ + getTangentCircle(circle) { + if (this.firstCircle === circle) { + return this.secondCircle; + } else if (this.secondCircle === circle) { + return this.firstCircle; + } + return null; + } + + /** + * @param {CloudTangent} tangent + */ + addDrawnWith(tangent) { + this.drawnWith.push(tangent); + } +} diff --git a/challenge #6/egorbwork/Point.js b/challenge #6/egorbwork/Point.js new file mode 100644 index 0000000..21f0e33 --- /dev/null +++ b/challenge #6/egorbwork/Point.js @@ -0,0 +1,20 @@ +class Point { + /** + * @param {number} coordinateX + * @param {number} coordinateY + */ + constructor(coordinateX, coordinateY) { + this.coordinateX = coordinateX; + this.coordinateY = coordinateY; + } + + /** + * @param {Point} point + * @returns {number} + */ + getDistanceFor(point) { + return Math.sqrt( + (point.coordinateX - this.coordinateX) ** 2 + (point.coordinateY - this.coordinateY) ** 2 + ); + } +} From 5f8bf833f6e85fadb3b59dfebbffddfdb29cc557 Mon Sep 17 00:00:00 2001 From: Egor Boaghi Date: Tue, 26 Sep 2017 09:23:23 +0300 Subject: [PATCH 02/11] egorbwork challenge 6 implement drawing engine and add test case. --- challenge #6/egorbwork/CloudDrawingEngine.js | 152 +++++++++++++++++++ challenge #6/egorbwork/application.js | 48 ++++++ 2 files changed, 200 insertions(+) create mode 100644 challenge #6/egorbwork/CloudDrawingEngine.js create mode 100644 challenge #6/egorbwork/application.js diff --git a/challenge #6/egorbwork/CloudDrawingEngine.js b/challenge #6/egorbwork/CloudDrawingEngine.js new file mode 100644 index 0000000..de58434 --- /dev/null +++ b/challenge #6/egorbwork/CloudDrawingEngine.js @@ -0,0 +1,152 @@ +class CloudDrawingEngine { + /** + * @param {CanvasRenderingContext2D} drawingContext + */ + constructor(drawingContext) { + this.drawingContext = drawingContext; + this.drawingContext.lineWidth = 5; + this.drawingContext.strokeStyle = 'black'; + } + + /** + * @param {Array} + */ + drawCloud(cloud) { + this.drawingContext.clearRect(0, 0, 5000, 5000); + for(let circle of cloud) { + let currentTangents = circle.getUsedTangents(); + if (currentTangents.length > 2) { + for (let tangentsGroup of this.getGroupedTangents(currentTangents)) { + let {first: firstTangent, second: secondTangent} = tangentsGroup; + this.drawingContext.beginPath(); + this.drawingContext.arc( + circle.centerPoint.coordinateX, + circle.centerPoint.coordinateY, + circle.radius, + // 0, 2 * Math.PI, + circle.calculateArcAngleForPoint(firstTangent.point), + circle.calculateArcAngleForPoint(secondTangent.point), + this.getArcClockWiseStatusForTangent(firstTangent, circle, cloud) + ); + firstTangent.addDrawnWith(secondTangent); + secondTangent.addDrawnWith(firstTangent); + this.drawingContext.stroke(); + } + } else { + let [firstTangent, secondTangent] = currentTangents; + this.drawingContext.beginPath(); + this.drawingContext.arc( + circle.centerPoint.coordinateX, + circle.centerPoint.coordinateY, + circle.radius, + // 0, 2 * Math.PI, + circle.calculateArcAngleForPoint(firstTangent.point), + circle.calculateArcAngleForPoint(secondTangent.point), + this.getArcClockWiseStatusForTangent(firstTangent, circle, cloud) + ); + firstTangent.addDrawnWith(secondTangent); + secondTangent.addDrawnWith(firstTangent); + this.drawingContext.stroke(); + } + } + + } + + /** + * @param {CloudTangent} tangent + * @param {CloudCircle} circle + * @param {Array} circles + * @returns {boolean} + */ + getArcClockWiseStatusForTangent(tangent, circle, circles) { + let testPoint = this.getClockWiseTestPoint(tangent, circle); + for (let testCircle of circles) { + if (testCircle === circle) { + continue; + } else if(testCircle.isPointInsideOfCircle(testPoint)) { + return false; + } + } + return true; + } + + /** + * @param {CloudTangent} tangent + * @param {CloudTangent} secondTangent + * @param {CloudCircle} circle + * @returns {Point} + */ + getClockWiseTestPoint(tangent, circle) { + let modifier = 1; + if (tangent.point.coordinateX > circle.centerPoint.coordinateX + && tangent.point.coordinateY >= circle.centerPoint.coordinateY + ) { + return new Point( + tangent.point.coordinateX + ( + tangent.point.coordinateY === circle.centerPoint.coordinateY + ? (-modifier) : modifier + ), + tangent.point.coordinateY - modifier + ); + } else if (tangent.point.coordinateX >= circle.centerPoint.coordinateX + && tangent.point.coordinateY < circle.centerPoint.coordinateY + ) { + return new Point( + tangent.point.coordinateX - modifier, + tangent.point.coordinateY + ( + tangent.point.coordinateX === circle.centerPoint.coordinateX + ? modifier : (-modifier) + ) + ); + } else if (tangent.point.coordinateX < circle.centerPoint.coordinateX + && tangent.point.coordinateY <= circle.centerPoint.coordinateY + ) { + return new Point( + tangent.point.coordinateX + ( + tangent.point.coordinateY === circle.centerPoint.coordinateY + ? modifier : (-modifier) + ), + tangent.point.coordinateY + modifier + ); + } else { + return new Point( + tangent.point.coordinateX + modifier, + tangent.point.coordinateY + ( + tangent.point.coordinateX === circle.centerPoint.coordinateX + ? (-modifier) : modifier + ) + ); + } + } + + /** + * @param {Array} tangents + */ + * getGroupedTangents(tangents) { + let usedTangents = []; + let remainingTangents = tangents.slice(0); + for (let tangent of tangents) { + remainingTangents.splice(remainingTangents.indexOf(tangent), 1); + if (tangent.drawnWith.length === 2 || usedTangents.includes(tangent)) { + continue; + } + let availableTangent; + let minimumDistance; + for (let comparableTangent of remainingTangents) { + if (comparableTangent.drawnWith.length === 2 || comparableTangent.drawnWith.includes(tangent) + || usedTangents.includes(comparableTangent) + ) { + continue; + } + let distanceBetweenTangentPoints = tangent.point.getDistanceFor(comparableTangent.point); + if (!minimumDistance || minimumDistance > distanceBetweenTangentPoints) { + minimumDistance = distanceBetweenTangentPoints; + availableTangent = comparableTangent; + } + } + usedTangents.push(availableTangent); + + yield {first: tangent, second: availableTangent}; + } + } +} diff --git a/challenge #6/egorbwork/application.js b/challenge #6/egorbwork/application.js new file mode 100644 index 0000000..d0aef62 --- /dev/null +++ b/challenge #6/egorbwork/application.js @@ -0,0 +1,48 @@ +document.addEventListener("DOMContentLoaded", function() { + // Test data: + let drawingEngine = new CloudDrawingEngine(document.getElementById('cloud').getContext('2d')); + let a = new CloudCircle(new Point(200, 200), 70); + let b = new CloudCircle(new Point(100, 100), 100); + let c = new CloudCircle(new Point(250, 250), 100); + let e = new CloudCircle(new Point(300, 300), 80); + let f = new CloudCircle(new Point(300, 240), 90); + + a.calculateCloudTangentsForCircle(b); + a.calculateCloudTangentsForCircle(c); + e.calculateCloudTangentsForCircle(c); + f.calculateCloudTangentsForCircle(e); + f.calculateCloudTangentsForCircle(c); + let cloud = [ + a, + b, + c, + e, + f + ]; + for (let d of a.tangents) { + d.setUsed(true); + for (let circle of cloud) { + if ((circle !== d.firstCircle && circle !== d.secondCircle) && circle.isPointInsideOfCircle(d.point)) { + d.setUsed(false); + } + } + + } + for (let d of e.tangents) { + d.setUsed(true); + for (let circle of cloud) { + if ((circle !== d.firstCircle && circle !== d.secondCircle) && circle.isPointInsideOfCircle(d.point)) { + d.setUsed(false); + } + } + } + for (let d of f.tangents) { + d.setUsed(true); + for (let circle of cloud) { + if ((circle !== d.firstCircle && circle !== d.secondCircle) && circle.isPointInsideOfCircle(d.point)) { + d.setUsed(false); + } + } + } + drawingEngine.drawCloud(cloud); +}); From 666f47deb16d4fe5c3285709b47ecfe3242ccc35 Mon Sep 17 00:00:00 2001 From: Egor Boaghi Date: Tue, 26 Sep 2017 09:25:32 +0300 Subject: [PATCH 03/11] egorbwork challenge 6 add html file. --- challenge #6/egorbwork/cloud.html | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 challenge #6/egorbwork/cloud.html diff --git a/challenge #6/egorbwork/cloud.html b/challenge #6/egorbwork/cloud.html new file mode 100644 index 0000000..a9454fe --- /dev/null +++ b/challenge #6/egorbwork/cloud.html @@ -0,0 +1,22 @@ + + + + + Cloud Generator + + + + + + + + +
+ +
+ To be able to see content, please use a modern version of Chrome or Firefox! +
+
+
+ + From 93001ae2fbaed619dc8a620a4d40682732226637 Mon Sep 17 00:00:00 2001 From: Egor Boaghi Date: Wed, 27 Sep 2017 08:24:12 +0300 Subject: [PATCH 04/11] egorbwork challenge 6 Improve cloud models logic. --- challenge #6/egorbwork/CloudCircle.js | 14 ++++++++++++-- challenge #6/egorbwork/CloudTangent.js | 16 +--------------- challenge #6/egorbwork/Point.js | 8 ++++++++ 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/challenge #6/egorbwork/CloudCircle.js b/challenge #6/egorbwork/CloudCircle.js index 91045fc..06ce66f 100644 --- a/challenge #6/egorbwork/CloudCircle.js +++ b/challenge #6/egorbwork/CloudCircle.js @@ -67,7 +67,7 @@ class CloudCircle { */ isTangentCircle(circle) { let distanceBetweenCircles = this.centerPoint.getDistanceFor(circle.centerPoint); - return distanceBetweenCircles < (this.radius + circle.radius); + return distanceBetweenCircles < (this.radius + circle.radius) && !this.isCircleInside(circle); } /** @@ -76,7 +76,7 @@ class CloudCircle { */ isPointInsideOfCircle(point) { let distanceTillPoint = point.getDistanceFor(this.centerPoint); - return distanceTillPoint < this.radius; + return distanceTillPoint <= this.radius; } /** @@ -88,6 +88,16 @@ class CloudCircle { ); } + /** + * @param {CloudCircle} circle + * @returns {boolean} + */ + isCircleInside(circle) { + return this.isPointInsideOfCircle(circle.centerPoint) + && this.radius > circle.radius + && (circle.centerPoint.getDistanceFor(this.centerPoint) + circle.radius) < this.radius; + } + /** * source: https://gamedev.stackexchange.com/questions/33709/get-angle-in-radians-given-a-point-on-a-circle * @param {Point} point diff --git a/challenge #6/egorbwork/CloudTangent.js b/challenge #6/egorbwork/CloudTangent.js index e1a929d..8e3b753 100644 --- a/challenge #6/egorbwork/CloudTangent.js +++ b/challenge #6/egorbwork/CloudTangent.js @@ -8,7 +8,7 @@ class CloudTangent { this.point = point; this.firstCircle = firstCircle; this.secondCircle = secondCircle; - this.usageStatus = false; + this.usageStatus = true; this.drawnWith = []; } @@ -19,20 +19,6 @@ class CloudTangent { this.usageStatus = status; } - /** - * @param {CloudCircle} circle - * - * @returns {CloudCircle|null} - */ - getTangentCircle(circle) { - if (this.firstCircle === circle) { - return this.secondCircle; - } else if (this.secondCircle === circle) { - return this.firstCircle; - } - return null; - } - /** * @param {CloudTangent} tangent */ diff --git a/challenge #6/egorbwork/Point.js b/challenge #6/egorbwork/Point.js index 21f0e33..50a4729 100644 --- a/challenge #6/egorbwork/Point.js +++ b/challenge #6/egorbwork/Point.js @@ -17,4 +17,12 @@ class Point { (point.coordinateX - this.coordinateX) ** 2 + (point.coordinateY - this.coordinateY) ** 2 ); } + + /** + * @param {Point} point + * @returns {boolean} + */ + isSamePoint(point) { + return this.coordinateX === point.coordinateX && this.coordinateY === point.coordinateY; + } } From 5cfcb711518fc3e1648d365f0216b14e43ed2da0 Mon Sep 17 00:00:00 2001 From: Egor Boaghi Date: Wed, 27 Sep 2017 08:25:53 +0300 Subject: [PATCH 05/11] egorbwork challenge 6 add tangent preparation inside of drawing engine --- challenge #6/egorbwork/CloudDrawingEngine.js | 46 +++++++++++++++++++- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/challenge #6/egorbwork/CloudDrawingEngine.js b/challenge #6/egorbwork/CloudDrawingEngine.js index de58434..62c2496 100644 --- a/challenge #6/egorbwork/CloudDrawingEngine.js +++ b/challenge #6/egorbwork/CloudDrawingEngine.js @@ -1,10 +1,11 @@ class CloudDrawingEngine { /** * @param {CanvasRenderingContext2D} drawingContext + * @param {number} drawingContext */ - constructor(drawingContext) { + constructor(drawingContext, lineWidth) { this.drawingContext = drawingContext; - this.drawingContext.lineWidth = 5; + this.drawingContext.lineWidth = lineWidth; this.drawingContext.strokeStyle = 'black'; } @@ -12,6 +13,7 @@ class CloudDrawingEngine { * @param {Array} */ drawCloud(cloud) { + this.prepareCloud(cloud); this.drawingContext.clearRect(0, 0, 5000, 5000); for(let circle of cloud) { let currentTangents = circle.getUsedTangents(); @@ -23,6 +25,7 @@ class CloudDrawingEngine { circle.centerPoint.coordinateX, circle.centerPoint.coordinateY, circle.radius, + // Full circle visibility for Testing // 0, 2 * Math.PI, circle.calculateArcAngleForPoint(firstTangent.point), circle.calculateArcAngleForPoint(secondTangent.point), @@ -31,6 +34,11 @@ class CloudDrawingEngine { firstTangent.addDrawnWith(secondTangent); secondTangent.addDrawnWith(firstTangent); this.drawingContext.stroke(); + // Tangent Points drawing for testing + // this.drawingContext.beginPath(); + // this.drawingContext.fillStyle = 'red'; + // this.drawingContext.fillRect(firstTangent.point.coordinateX, firstTangent.point.coordinateY, 5, 5); + // this.drawingContext.fillRect(secondTangent.point.coordinateX, secondTangent.point.coordinateY, 5, 5); } } else { let [firstTangent, secondTangent] = currentTangents; @@ -39,6 +47,7 @@ class CloudDrawingEngine { circle.centerPoint.coordinateX, circle.centerPoint.coordinateY, circle.radius, + // Full circle visibility for Testing // 0, 2 * Math.PI, circle.calculateArcAngleForPoint(firstTangent.point), circle.calculateArcAngleForPoint(secondTangent.point), @@ -47,11 +56,44 @@ class CloudDrawingEngine { firstTangent.addDrawnWith(secondTangent); secondTangent.addDrawnWith(firstTangent); this.drawingContext.stroke(); + // Tangent Points drawing for testing + // this.drawingContext.beginPath(); + // this.drawingContext.fillStyle = 'red'; + // this.drawingContext.fillRect(firstTangent.point.coordinateX, firstTangent.point.coordinateY, 5, 5); + // this.drawingContext.fillRect(secondTangent.point.coordinateX, secondTangent.point.coordinateY, 5, 5); } } } + /** + * @param {Array} + */ + prepareCloud(cloud) { + let preparedList = []; + for (let circle of cloud) { + preparedList.push(circle); + // Calculate tangents + let remainingCircles = cloud.filter(remainingCircle => !preparedList.includes(remainingCircle)); + for (let remainingCircle of remainingCircles) { + if (circle.isTangentCircle(remainingCircle)) { + circle.calculateCloudTangentsForCircle(remainingCircle); + } + } + // Mark not used in cloud tangents + for (let tangent of circle.tangents) { + for (let remainingCircle of cloud) { + if (remainingCircle !== tangent.firstCircle + && remainingCircle !== tangent.secondCircle + && remainingCircle.isPointInsideOfCircle(tangent.point) + ) { + tangent.setUsed(false); + } + } + } + } + } + /** * @param {CloudTangent} tangent * @param {CloudCircle} circle From 16f2cc1e5fb6bb6c33c0b5f2fbf3495a16e0d161 Mon Sep 17 00:00:00 2001 From: Egor Boaghi Date: Wed, 27 Sep 2017 08:26:52 +0300 Subject: [PATCH 06/11] egorbwork challenge 6 Implement CloudGeneratorBuilder. --- .../egorbwork/CloudGeneratorBuilder.js | 215 ++++++++++++++++++ 1 file changed, 215 insertions(+) create mode 100644 challenge #6/egorbwork/CloudGeneratorBuilder.js diff --git a/challenge #6/egorbwork/CloudGeneratorBuilder.js b/challenge #6/egorbwork/CloudGeneratorBuilder.js new file mode 100644 index 0000000..9f10573 --- /dev/null +++ b/challenge #6/egorbwork/CloudGeneratorBuilder.js @@ -0,0 +1,215 @@ +class CloudGeneratorBuilder { + /** + * @param {number} radiusMinimum + * @param {number} radiusMaximum + * @param {number} lineWidthConstraint + */ + constructor(radiusMinimum, radiusMaximum, lineWidthConstraint) { + this.radiusMinimum = radiusMinimum; + this.radiusMaximum = radiusMaximum; + this.coordinatesRandomGenerator = this.getCoordinatesRandomGenerator(); + this.numberOfCircles = 2; + this.lineWidthConstraint = lineWidthConstraint; + } + + setNumberOfCircles(numberOfCircles) { + this.numberOfCircles = numberOfCircles; + } + + /** + * @returns {number} + */ + getRandomRadius() { + return this.getRandomNumber(this.radiusMinimum, this.radiusMaximum / (2 * this.numberOfCircles)); + } + + /** + * @param {number} minimum + * @param {number} maximum + * @returns {number} + */ + getRandomNumber(minimum, maximum) { + return Math.floor(Math.random() * (maximum - minimum)) + minimum; + } + + /** + * @param {number} minimum + * @param {number} maximum + */ + * getCoordinatesRandomGenerator() { + let minimum = 400; + let maximum = 800; + while (true) { + ({minimum, maximum} = yield this.getRandomNumber(minimum, maximum) || {minimum, maximum}); + } + } + + * getCloudGenerator() { + do { + let cloud = []; + for (let counter = 0; counter < this.numberOfCircles; counter++) { + let circle; + do { + circle = this.generateCircleForCloud(cloud); + } while (!this.isValidCircleForCloud(circle, cloud)); + cloud.push(circle) + } + yield cloud; + } while(true); + } + + /** + * @param {Array} cloud + * @returns {CloudCircle} + */ + generateCircleForCloud(cloud) { + let radius = this.getRandomRadius(); + let coordinateX = this.coordinatesRandomGenerator.next( + { + minimum: cloud.reduce( + (minimum, circle) => { + return minimum < (circle.centerPoint.coordinateX - circle.radius) + ? minimum + : circle.centerPoint.coordinateX - circle.radius; + } + , 400 + ) - 2 * radius + this.lineWidthConstraint * 2, + maximum: cloud.reduce( + (maximum, circle) => { + return maximum > (circle.centerPoint.coordinateX + circle.radius) + ? maximum + : circle.centerPoint.coordinateX + circle.radius; + } + , 800 + ) + 2 * radius - this.lineWidthConstraint * 2 + } + ).value; + let coordinateY = this.coordinatesRandomGenerator.next( + { + minimum: cloud.reduce( + (minimum, circle) => { + return minimum < (circle.centerPoint.coordinateY - circle.radius) + ? minimum + : circle.centerPoint.coordinateY - circle.radius; + } + , 400 + ) - 2 * radius + this.lineWidthConstraint * 2, + maximum: cloud.reduce( + (maximum, circle) => { + return maximum > (circle.centerPoint.coordinateY + circle.radius) + ? maximum + : circle.centerPoint.coordinateY + circle.radius; + } + , 800 + ) + 2 * radius - this.lineWidthConstraint * 2 + } + ).value; + let centerPoint = new Point(coordinateX, coordinateY); + + return new CloudCircle(centerPoint, radius); + } + + /** + * @param {CloudCircle} circle + * @param {Array} cloud + * @returns {boolean} + */ + isValidCircleForCloud(circle, cloud) { + // For empty cloud circle tangent status is true + let tangentStatus = cloud.length === 0; + for (let currentCircle of cloud) { + if (currentCircle.centerPoint.isSamePoint(circle.centerPoint)) { + return false; + } + if (currentCircle.isCircleInside(circle)) { + return false; + } + if (currentCircle.isTangentCircle(circle)) { + tangentStatus = true; + } + } + let insideCloudStatus = this.isCirclePointsInsideOfExistingCloud(circle, cloud); + let cloudCirclesInsideStatus = this.isCirclesPointsInsideOfExtendedCloud(circle, cloud); + return tangentStatus && !insideCloudStatus && !cloudCirclesInsideStatus; + } + + /** + * @param {CloudCircle} circle + * @param {Array} cloud + * @returns {boolean} + */ + isCirclesPointsInsideOfExtendedCloud(circle, cloud) { + let extendedCloud = cloud.slice(0); + extendedCloud.push(circle); + for (let currentCircle of extendedCloud) { + let remainingCloud = extendedCloud.filter(remainingCircle => remainingCircle !== currentCircle); + if (this.isCirclePointsInsideOfExistingCloud(currentCircle, remainingCloud)) { + return true; + } + } + return false; + } + + /** + * @param {CloudCircle} circle + * @param {Array} cloud + * @returns {boolean} + */ + isCirclePointsInsideOfExistingCloud(circle, cloud) { + let northPoint = new Point( + circle.centerPoint.coordinateX, + circle.centerPoint.coordinateY - circle.radius + ); + let southPoint = new Point( + circle.centerPoint.coordinateX, + circle.centerPoint.coordinateY + circle.radius + ); + let eastPoint = new Point( + circle.centerPoint.coordinateX + circle.radius, + circle.centerPoint.coordinateY + ); + let westPoint = new Point( + circle.centerPoint.coordinateX - circle.radius, + circle.centerPoint.coordinateY + ); + // Not accurate but better than nothing + let northEastPoint = new Point( + circle.centerPoint.coordinateX + circle.radius / Math.sqrt(2), + circle.centerPoint.coordinateY - circle.radius / Math.sqrt(2) + ); + let northWestPoint = new Point( + circle.centerPoint.coordinateX - circle.radius / Math.sqrt(2), + circle.centerPoint.coordinateY - circle.radius / Math.sqrt(2) + ); + let southEastPoint = new Point( + circle.centerPoint.coordinateX + circle.radius / Math.sqrt(2), + circle.centerPoint.coordinateY + circle.radius / Math.sqrt(2) + ); + let southWestPoint = new Point( + circle.centerPoint.coordinateX - circle.radius / Math.sqrt(2), + circle.centerPoint.coordinateY + circle.radius / Math.sqrt(2) + ); + + return this.isPointInsideOfExistingCloud(northPoint, cloud) + && this.isPointInsideOfExistingCloud(southPoint, cloud) + && this.isPointInsideOfExistingCloud(eastPoint, cloud) + && this.isPointInsideOfExistingCloud(westPoint, cloud) + && this.isPointInsideOfExistingCloud(northEastPoint, cloud) + && this.isPointInsideOfExistingCloud(northWestPoint, cloud) + && this.isPointInsideOfExistingCloud(southEastPoint, cloud) + && this.isPointInsideOfExistingCloud(southWestPoint, cloud); + } + + /** + * @param {Point} point + * @param {Array} cloud + * @returns {boolean} + */ + isPointInsideOfExistingCloud(point, cloud) { + let isPointInsideOfCloud = false; + for (let currentCircle of cloud) { + isPointInsideOfCloud = isPointInsideOfCloud || currentCircle.isPointInsideOfCircle(point); + } + return isPointInsideOfCloud; + } +} From 8a75d0d93ff51b18a59411c41256ff54ebe511a9 Mon Sep 17 00:00:00 2001 From: Egor Boaghi Date: Wed, 27 Sep 2017 08:27:43 +0300 Subject: [PATCH 07/11] egorbwork challenge 6 adjust application for cloud generator usage and add it to the cloud html. --- challenge #6/egorbwork/application.js | 63 ++++++++------------------- challenge #6/egorbwork/cloud.html | 2 +- 2 files changed, 20 insertions(+), 45 deletions(-) diff --git a/challenge #6/egorbwork/application.js b/challenge #6/egorbwork/application.js index d0aef62..8f4098b 100644 --- a/challenge #6/egorbwork/application.js +++ b/challenge #6/egorbwork/application.js @@ -1,48 +1,23 @@ +let lineWidth = 5; +let cloudGeneratorBuilder = new CloudGeneratorBuilder(10, 1000, lineWidth); +let cloudGenerator = cloudGeneratorBuilder.getCloudGenerator(); +cloudGeneratorBuilder.setNumberOfCircles(8); document.addEventListener("DOMContentLoaded", function() { + let cloud = cloudGenerator.next().value; // Test data: - let drawingEngine = new CloudDrawingEngine(document.getElementById('cloud').getContext('2d')); - let a = new CloudCircle(new Point(200, 200), 70); - let b = new CloudCircle(new Point(100, 100), 100); - let c = new CloudCircle(new Point(250, 250), 100); - let e = new CloudCircle(new Point(300, 300), 80); - let f = new CloudCircle(new Point(300, 240), 90); - - a.calculateCloudTangentsForCircle(b); - a.calculateCloudTangentsForCircle(c); - e.calculateCloudTangentsForCircle(c); - f.calculateCloudTangentsForCircle(e); - f.calculateCloudTangentsForCircle(c); - let cloud = [ - a, - b, - c, - e, - f - ]; - for (let d of a.tangents) { - d.setUsed(true); - for (let circle of cloud) { - if ((circle !== d.firstCircle && circle !== d.secondCircle) && circle.isPointInsideOfCircle(d.point)) { - d.setUsed(false); - } - } - - } - for (let d of e.tangents) { - d.setUsed(true); - for (let circle of cloud) { - if ((circle !== d.firstCircle && circle !== d.secondCircle) && circle.isPointInsideOfCircle(d.point)) { - d.setUsed(false); - } - } - } - for (let d of f.tangents) { - d.setUsed(true); - for (let circle of cloud) { - if ((circle !== d.firstCircle && circle !== d.secondCircle) && circle.isPointInsideOfCircle(d.point)) { - d.setUsed(false); - } - } - } + // let a = new CloudCircle(new Point(200, 200), 70); + // let b = new CloudCircle(new Point(100, 100), 100); + // let c = new CloudCircle(new Point(250, 250), 100); + // let e = new CloudCircle(new Point(300, 300), 80); + // let f = new CloudCircle(new Point(300, 240), 90); + // + // let testCloud = [ + // a, + // b, + // c, + // e, + // f + // ]; + let drawingEngine = new CloudDrawingEngine(document.getElementById('cloud').getContext('2d'), lineWidth); drawingEngine.drawCloud(cloud); }); diff --git a/challenge #6/egorbwork/cloud.html b/challenge #6/egorbwork/cloud.html index a9454fe..6a99794 100644 --- a/challenge #6/egorbwork/cloud.html +++ b/challenge #6/egorbwork/cloud.html @@ -3,7 +3,7 @@ Cloud Generator - + From daa19eb714c8bc168d4e1fb20286b8881a77d989 Mon Sep 17 00:00:00 2001 From: egor Date: Wed, 27 Sep 2017 16:40:30 +0300 Subject: [PATCH 08/11] egorbwork challenge 6 add presorting of the cloud circles. --- challenge #6/egorbwork/CloudDrawingEngine.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/challenge #6/egorbwork/CloudDrawingEngine.js b/challenge #6/egorbwork/CloudDrawingEngine.js index 62c2496..60a1ebc 100644 --- a/challenge #6/egorbwork/CloudDrawingEngine.js +++ b/challenge #6/egorbwork/CloudDrawingEngine.js @@ -15,7 +15,7 @@ class CloudDrawingEngine { drawCloud(cloud) { this.prepareCloud(cloud); this.drawingContext.clearRect(0, 0, 5000, 5000); - for(let circle of cloud) { + for(let circle of cloud.sort(this.compareCircleByUsedTangents)) { let currentTangents = circle.getUsedTangents(); if (currentTangents.length > 2) { for (let tangentsGroup of this.getGroupedTangents(currentTangents)) { @@ -191,4 +191,13 @@ class CloudDrawingEngine { yield {first: tangent, second: availableTangent}; } } + + /** + * @param {CloudCircle} firstCircle + * @param {CloudCircle} secondCircle + * @returns {number} + */ + compareCircleByUsedTangents(firstCircle, secondCircle) { + return firstCircle.getUsedTangents().length - secondCircle.getUsedTangents().length; + } } From eee7ab3c580ac9177348c21f82fb81dfa3e83b54 Mon Sep 17 00:00:00 2001 From: Egor Boaghi Date: Sat, 17 Feb 2018 17:05:19 +0200 Subject: [PATCH 09/11] Add simple solution which doesn't cover all situations. --- .../simple-stupid-solution/CloudCircle.js | 40 +++ .../CloudDrawingEngine.js | 65 +++++ .../CloudGeneratorBuilder.js | 236 ++++++++++++++++++ .../egorbwork/simple-stupid-solution/Point.js | 28 +++ .../simple-stupid-solution/application.js | 53 ++++ .../simple-stupid-solution/cloud.html | 27 ++ 6 files changed, 449 insertions(+) create mode 100644 challenge #6/egorbwork/simple-stupid-solution/CloudCircle.js create mode 100644 challenge #6/egorbwork/simple-stupid-solution/CloudDrawingEngine.js create mode 100644 challenge #6/egorbwork/simple-stupid-solution/CloudGeneratorBuilder.js create mode 100644 challenge #6/egorbwork/simple-stupid-solution/Point.js create mode 100644 challenge #6/egorbwork/simple-stupid-solution/application.js create mode 100644 challenge #6/egorbwork/simple-stupid-solution/cloud.html diff --git a/challenge #6/egorbwork/simple-stupid-solution/CloudCircle.js b/challenge #6/egorbwork/simple-stupid-solution/CloudCircle.js new file mode 100644 index 0000000..7c192c9 --- /dev/null +++ b/challenge #6/egorbwork/simple-stupid-solution/CloudCircle.js @@ -0,0 +1,40 @@ +class CloudCircle { + /** + * @param {Point} centerPoint + * @param {number} radius + */ + constructor(centerPoint, radius) { + this.centerPoint = centerPoint; + this.radius = radius; + this.tangents = []; + this.tangentCircles = []; + } + + /** + * @param {CloudCircle} circle + * @returns {boolean} + */ + isTangentCircle(circle) { + let distanceBetweenCircles = this.centerPoint.getDistanceFor(circle.centerPoint); + return distanceBetweenCircles < (this.radius + circle.radius) && !this.isCircleInside(circle); + } + + /** + * @param {Point} point + * @returns {boolean} + */ + isPointInsideOfCircle(point) { + let distanceTillPoint = point.getDistanceFor(this.centerPoint); + return distanceTillPoint <= this.radius; + } + + /** + * @param {CloudCircle} circle + * @returns {boolean} + */ + isCircleInside(circle) { + return this.isPointInsideOfCircle(circle.centerPoint) + && this.radius > circle.radius + && (circle.centerPoint.getDistanceFor(this.centerPoint) + circle.radius) < this.radius; + } +} diff --git a/challenge #6/egorbwork/simple-stupid-solution/CloudDrawingEngine.js b/challenge #6/egorbwork/simple-stupid-solution/CloudDrawingEngine.js new file mode 100644 index 0000000..5e695d1 --- /dev/null +++ b/challenge #6/egorbwork/simple-stupid-solution/CloudDrawingEngine.js @@ -0,0 +1,65 @@ +class CloudDrawingEngine { + /** + * @param {CanvasRenderingContext2D} drawingContext + * @param {number} lineWidth + */ + constructor(drawingContext, lineWidth) { + this.drawingContext = drawingContext; + this.drawingContext.lineWidth = lineWidth; + this.drawingContext.fillStyle = 'white'; + this.drawingContext.strokeStyle = 'black'; + } + + /** + * @param {Array} cloud + */ + drawCloud(cloud) { + this.drawCloudScheme(cloud); + this.removeOverlappingParts(cloud); + } + + /** + * Clear canvas and Draw scheme with all circles + * @param {Array} cloud + */ + drawCloudScheme(cloud) { + this.drawingContext.clearRect(0, 0, 5000, 5000); + this.drawScheme(cloud); + } + + /** + * For each circle in cloud draw + * @param {Array} cloud + */ + drawScheme(cloud) { + for (let circle of cloud) { + this.drawingContext.beginPath(); + this.drawingContext.arc( + circle.centerPoint.coordinateX, + circle.centerPoint.coordinateY, + circle.radius, + 0, + 2 * Math.PI + ); + this.drawingContext.stroke(); + } + } + + /** + * @param {Array} cloud + */ + removeOverlappingParts(cloud) { + for (let circle of cloud) { + this.drawingContext.beginPath(); + this.drawingContext.arc( + circle.centerPoint.coordinateX, + circle.centerPoint.coordinateY, + // For more clear image it is better to have it a bit bigger radius + circle.radius, + 0, + 2 * Math.PI + ); + this.drawingContext.fill(); + } + } +} diff --git a/challenge #6/egorbwork/simple-stupid-solution/CloudGeneratorBuilder.js b/challenge #6/egorbwork/simple-stupid-solution/CloudGeneratorBuilder.js new file mode 100644 index 0000000..809e2d4 --- /dev/null +++ b/challenge #6/egorbwork/simple-stupid-solution/CloudGeneratorBuilder.js @@ -0,0 +1,236 @@ +class CloudGeneratorBuilder { + /** + * @param {number} radiusMinimum + * @param {number} radiusMaximum + * @param {number} lineWidthConstraint + */ + constructor(radiusMinimum, radiusMaximum, lineWidthConstraint) { + this.radiusMinimum = radiusMinimum; + this.radiusMaximum = radiusMaximum; + this.coordinatesRandomGenerator = this.getCoordinatesRandomGenerator(); + this.numberOfCircles = 2; + this.lineWidthConstraint = lineWidthConstraint; + } + + /** + * @param {number} numberOfCircles + */ + setNumberOfCircles(numberOfCircles) { + this.numberOfCircles = numberOfCircles; + } + + /** + * @returns {number} + */ + getRandomRadius() { + return this.getRandomNumber(this.radiusMinimum, this.radiusMaximum / this.numberOfCircles); + } + + /** + * @param {number} minimum + * @param {number} maximum + * @returns {number} + */ + getRandomNumber(minimum, maximum) { + return Math.floor(Math.random() * (maximum - minimum)) + minimum; + } + + /** + * @param {number} minimum + * @param {number} maximum + */ + * getCoordinatesRandomGenerator() { + let minimum = 200; + let maximum = 900; + while (true) { + ({minimum, maximum} = yield this.getRandomNumber(minimum, maximum) || {minimum, maximum}); + } + } + + /** + * @returns {iterator} + */ + * getCloudGenerator() { + do { + let cloud = []; + for (let counter = 0; counter < this.numberOfCircles; counter++) { + let circle; + do { + circle = this.generateCircleForCloud(cloud); + } while (!this.isValidCircleForCloud(circle, cloud)); + cloud.push(circle) + } + yield cloud; + } while(true); + } + + /** + * @param {Array} cloud + * @returns {CloudCircle} + */ + generateCircleForCloud(cloud) { + let radius = this.getRandomRadius(); + let coordinateX = this.coordinatesRandomGenerator.next( + { + minimum: cloud.reduce( + (minimum, circle) => { + return minimum < (circle.centerPoint.coordinateX - circle.radius) + ? minimum + : circle.centerPoint.coordinateX - circle.radius; + } + , 400 + ) - 2 * radius + this.lineWidthConstraint * 2, + maximum: cloud.reduce( + (maximum, circle) => { + return maximum > (circle.centerPoint.coordinateX + circle.radius) + ? maximum + : circle.centerPoint.coordinateX + circle.radius; + } + , 800 + ) + 2 * radius - this.lineWidthConstraint * 2 + } + ).value; + let coordinateY = this.coordinatesRandomGenerator.next( + { + minimum: cloud.reduce( + (minimum, circle) => { + return minimum < (circle.centerPoint.coordinateY - circle.radius) + ? minimum + : circle.centerPoint.coordinateY - circle.radius; + } + , 400 + ) - 2 * radius + this.lineWidthConstraint * 2, + maximum: cloud.reduce( + (maximum, circle) => { + return maximum > (circle.centerPoint.coordinateY + circle.radius) + ? maximum + : circle.centerPoint.coordinateY + circle.radius; + } + , 800 + ) + 2 * radius - this.lineWidthConstraint * 2 + } + ).value; + let centerPoint = new Point(coordinateX, coordinateY); + + return new CloudCircle(centerPoint, radius); + } + + /** + * @param {CloudCircle} circle + * @param {Array} cloud + * @returns {boolean} + */ + isValidCircleForCloud(circle, cloud) { + // For empty cloud circle tangent status is true + let tangentStatus = cloud.length === 0; + for (let currentCircle of cloud) { + if (currentCircle.centerPoint.isSamePoint(circle.centerPoint)) { + return false; + } + if (currentCircle.isCircleInside(circle)) { + return false; + } + if (currentCircle.isTangentCircle(circle)) { + tangentStatus = true; + } + } + let insideCloudStatus = this.isCirclePointsInsideOfExistingCloud(circle, cloud); + let cloudCirclesInsideStatus = this.isCirclesPointsInsideOfExtendedCloud(circle, cloud); + return tangentStatus && !(insideCloudStatus || cloudCirclesInsideStatus); + } + + /** + * @param {CloudCircle} circle + * @param {Array} cloud + * @returns {boolean} + */ + isCirclesPointsInsideOfExtendedCloud(circle, cloud) { + let extendedCloud = cloud.slice(0); + extendedCloud.push(circle); + for (let currentCircle of extendedCloud) { + let remainingCloud = extendedCloud.filter(remainingCircle => remainingCircle !== currentCircle); + if (this.isCirclePointsInsideOfExistingCloud(currentCircle, remainingCloud)) { + return true; + } + } + return false; + } + + /** + * @param {CloudCircle} circle + * @param {Array} cloud + * @returns {boolean} + */ + isCirclePointsInsideOfExistingCloud(circle, cloud) { + // Can be used only radius but than some of the cases will look like overlapping from line Width + // Line width is doubled to be sure that it looks fine with simplified version + let deltaRadius = circle.radius- this.lineWidthConstraint / 2 - 1; + let northPoint = new Point( + circle.centerPoint.coordinateX, + circle.centerPoint.coordinateY - deltaRadius + ); + let southPoint = new Point( + circle.centerPoint.coordinateX, + circle.centerPoint.coordinateY + deltaRadius + ); + let eastPoint = new Point( + circle.centerPoint.coordinateX + deltaRadius, + circle.centerPoint.coordinateY + ); + let westPoint = new Point( + circle.centerPoint.coordinateX - deltaRadius, + circle.centerPoint.coordinateY + ); + // Not accurate but better than nothing + // Using for point coordinates calculation angle of 45 degrees + let sin45 = Math.sin(this.convertDegreesToRadians(45)); + let cos45 = Math.cos(this.convertDegreesToRadians(45)); + + let northEastPoint = new Point( + circle.centerPoint.coordinateX + deltaRadius * cos45, + circle.centerPoint.coordinateY - deltaRadius * sin45 + ); + let northWestPoint = new Point( + circle.centerPoint.coordinateX - deltaRadius * cos45, + circle.centerPoint.coordinateY - deltaRadius * sin45 + ); + let southEastPoint = new Point( + circle.centerPoint.coordinateX + deltaRadius * cos45, + circle.centerPoint.coordinateY + deltaRadius * sin45 + ); + let southWestPoint = new Point( + circle.centerPoint.coordinateX - deltaRadius * cos45, + circle.centerPoint.coordinateY + deltaRadius * sin45 + ); + + return this.isPointInsideOfExistingCloud(northPoint, cloud) + && this.isPointInsideOfExistingCloud(southPoint, cloud) + && this.isPointInsideOfExistingCloud(eastPoint, cloud) + && this.isPointInsideOfExistingCloud(westPoint, cloud) + && this.isPointInsideOfExistingCloud(northEastPoint, cloud) + && this.isPointInsideOfExistingCloud(northWestPoint, cloud) + && this.isPointInsideOfExistingCloud(southEastPoint, cloud) + && this.isPointInsideOfExistingCloud(southWestPoint, cloud); + } + + /** + * @param {Point} point + * @param {Array} cloud + * @returns {boolean} + */ + isPointInsideOfExistingCloud(point, cloud) { + let isPointInsideOfCloud = false; + for (let currentCircle of cloud) { + isPointInsideOfCloud = isPointInsideOfCloud || currentCircle.isPointInsideOfCircle(point); + } + return isPointInsideOfCloud; + } + + /** + * @param {number} degrees + * @returns {number} + */ + convertDegreesToRadians(degrees) { + return (Math.PI / 180) * degrees; + } +} diff --git a/challenge #6/egorbwork/simple-stupid-solution/Point.js b/challenge #6/egorbwork/simple-stupid-solution/Point.js new file mode 100644 index 0000000..50a4729 --- /dev/null +++ b/challenge #6/egorbwork/simple-stupid-solution/Point.js @@ -0,0 +1,28 @@ +class Point { + /** + * @param {number} coordinateX + * @param {number} coordinateY + */ + constructor(coordinateX, coordinateY) { + this.coordinateX = coordinateX; + this.coordinateY = coordinateY; + } + + /** + * @param {Point} point + * @returns {number} + */ + getDistanceFor(point) { + return Math.sqrt( + (point.coordinateX - this.coordinateX) ** 2 + (point.coordinateY - this.coordinateY) ** 2 + ); + } + + /** + * @param {Point} point + * @returns {boolean} + */ + isSamePoint(point) { + return this.coordinateX === point.coordinateX && this.coordinateY === point.coordinateY; + } +} diff --git a/challenge #6/egorbwork/simple-stupid-solution/application.js b/challenge #6/egorbwork/simple-stupid-solution/application.js new file mode 100644 index 0000000..8584a32 --- /dev/null +++ b/challenge #6/egorbwork/simple-stupid-solution/application.js @@ -0,0 +1,53 @@ +/** + * Simple solution, has problem with overlapping + */ + +let lineWidth = 5; +let cloudGeneratorBuilder = new CloudGeneratorBuilder(10, 800, lineWidth); +let cloudGenerator = cloudGeneratorBuilder.getCloudGenerator(); +let scheme = false; +let drawingEngine, cloud; + +document.addEventListener("DOMContentLoaded", function() { + cloudGeneratorBuilder.setNumberOfCircles(8); + drawingEngine = new CloudDrawingEngine(document.getElementById('cloud').getContext('2d'), lineWidth); + cloud = cloudGenerator.next().value; + drawingEngine.drawCloud(cloud); + + addSchemeButtonHandler(); + addRedrawButtonHandler(); +}); + +function addSchemeButtonHandler() { + let schemeButton = document.getElementById('scheme'); + + schemeButton.addEventListener('click', () => { + let alternativeText = schemeButton.getAttribute('data-text'); + schemeButton.setAttribute('data-text', schemeButton.textContent); + schemeButton.textContent = alternativeText; + scheme = !scheme; + if (scheme) { + drawingEngine.drawScheme(cloud); + } else { + drawingEngine.drawCloud(cloud); + } + }); +} + +function addRedrawButtonHandler() { + let redrawButton = document.getElementById('redraw'); + let inputNumber = document.getElementById('numberOfCircles'); + + redrawButton.addEventListener('click', () => { + let numberOfCircles = inputNumber.value; + cloudGeneratorBuilder.setNumberOfCircles(numberOfCircles); + cloud = cloudGenerator.next().value; + if (scheme) { + drawingEngine.drawCloudScheme(cloud); + } else { + drawingEngine.drawCloud(cloud); + } + }); +} + + diff --git a/challenge #6/egorbwork/simple-stupid-solution/cloud.html b/challenge #6/egorbwork/simple-stupid-solution/cloud.html new file mode 100644 index 0000000..0ae321e --- /dev/null +++ b/challenge #6/egorbwork/simple-stupid-solution/cloud.html @@ -0,0 +1,27 @@ + + + + + Cloud Generator + + + + + + + +
+ + + + +
+
+ +
+ To be able to see content, please use a modern version of Chrome or Firefox! +
+
+
+ + From 1a8ae89ca5223c60f1d8eeeda3d82e6e01a15d0a Mon Sep 17 00:00:00 2001 From: Egor Boaghi Date: Sun, 18 Feb 2018 00:12:46 +0200 Subject: [PATCH 10/11] Add solution for 6 challenge. More correct but a bit ugly. --- .../rocket-since-solution/CloudCircle.js | 86 +++++++ .../CloudDrawingEngine.js | 107 ++++++++ .../CloudGeneratorBuilder.js | 233 ++++++++++++++++++ .../rocket-since-solution/CloudTangentCase.js | 67 +++++ .../egorbwork/rocket-since-solution/Point.js | 28 +++ .../rocket-since-solution/application.js | 53 ++++ .../rocket-since-solution/cloud.html | 28 +++ 7 files changed, 602 insertions(+) create mode 100644 challenge #6/egorbwork/rocket-since-solution/CloudCircle.js create mode 100644 challenge #6/egorbwork/rocket-since-solution/CloudDrawingEngine.js create mode 100644 challenge #6/egorbwork/rocket-since-solution/CloudGeneratorBuilder.js create mode 100644 challenge #6/egorbwork/rocket-since-solution/CloudTangentCase.js create mode 100644 challenge #6/egorbwork/rocket-since-solution/Point.js create mode 100644 challenge #6/egorbwork/rocket-since-solution/application.js create mode 100644 challenge #6/egorbwork/rocket-since-solution/cloud.html diff --git a/challenge #6/egorbwork/rocket-since-solution/CloudCircle.js b/challenge #6/egorbwork/rocket-since-solution/CloudCircle.js new file mode 100644 index 0000000..28db4b9 --- /dev/null +++ b/challenge #6/egorbwork/rocket-since-solution/CloudCircle.js @@ -0,0 +1,86 @@ +class CloudCircle { + /** + * @param {Point} centerPoint + * @param {number} radius + */ + constructor(centerPoint, radius) { + this.centerPoint = centerPoint; + this.radius = radius; + } + + /** + * @param {CloudCircle} circle + * @returns {boolean} + */ + isTangentCircle(circle) { + let distanceBetweenCircles = this.centerPoint.getDistanceFor(circle.centerPoint); + return distanceBetweenCircles < (this.radius + circle.radius) && !this.isCircleInside(circle); + } + + /** + * @param {Point} point + * @returns {boolean} + */ + isPointInsideOfCircle(point) { + let distanceTillPoint = point.getDistanceFor(this.centerPoint); + return distanceTillPoint <= this.radius; + } + + /** + * @param {CloudCircle} circle + * @returns {boolean} + */ + isCircleInside(circle) { + return this.isPointInsideOfCircle(circle.centerPoint) + && this.radius > circle.radius + && (circle.centerPoint.getDistanceFor(this.centerPoint) + circle.radius) < this.radius; + } + + /** + * source: http://2000clicks.com/mathhelp/GeometryConicSectionCircleIntersection.aspx + * @param {CloudCircle} circle + * @returns {CloudTangentCase} + */ + calculateCloudTangentCaseForCircle(circle) { + let distanceBetweenCircles = this.centerPoint.getDistanceFor(circle.centerPoint); + let areaOfCirclesTriangle = 0.25 * Math.sqrt( + ((this.radius + circle.radius) ** 2 - distanceBetweenCircles ** 2) + * (distanceBetweenCircles ** 2 - (this.radius - circle.radius) ** 2) + ); + let firstTangentPoint = new Point( + 0.5 * (circle.centerPoint.coordinateX + this.centerPoint.coordinateX) + + 0.5 * (circle.centerPoint.coordinateX - this.centerPoint.coordinateX) + * (this.radius ** 2 - circle.radius ** 2) / distanceBetweenCircles ** 2 + + 2 * (circle.centerPoint.coordinateY - this.centerPoint.coordinateY) * areaOfCirclesTriangle + / distanceBetweenCircles ** 2, + 0.5 * (circle.centerPoint.coordinateY + this.centerPoint.coordinateY) + + 0.5 * (circle.centerPoint.coordinateY - this.centerPoint.coordinateY) + * (this.radius ** 2 - circle.radius ** 2) / distanceBetweenCircles ** 2 + - 2 * (circle.centerPoint.coordinateX - this.centerPoint.coordinateX) * areaOfCirclesTriangle + / distanceBetweenCircles ** 2 + ); + let secondTangentPoint = new Point( + 0.5 * (circle.centerPoint.coordinateX + this.centerPoint.coordinateX) + + 0.5 * (circle.centerPoint.coordinateX - this.centerPoint.coordinateX) + * (this.radius ** 2 - circle.radius ** 2) / distanceBetweenCircles ** 2 + - 2 * (circle.centerPoint.coordinateY - this.centerPoint.coordinateY) * areaOfCirclesTriangle + / distanceBetweenCircles ** 2, + 0.5 * (circle.centerPoint.coordinateY + this.centerPoint.coordinateY) + + 0.5 * (circle.centerPoint.coordinateY - this.centerPoint.coordinateY) + * (this.radius ** 2 - circle.radius ** 2) / distanceBetweenCircles ** 2 + + 2 * (circle.centerPoint.coordinateX - this.centerPoint.coordinateX) * areaOfCirclesTriangle + / distanceBetweenCircles ** 2 + ); + + return new CloudTangentCase(firstTangentPoint, secondTangentPoint, this, circle); + } + + /** + * source: https://gamedev.stackexchange.com/questions/33709/get-angle-in-radians-given-a-point-on-a-circle + * @param {Point} point + * @returns {number} + */ + calculateArcAngleForPoint(point) { + return Math.atan2(point.coordinateY - this.centerPoint.coordinateY, point.coordinateX - this.centerPoint.coordinateX); + } +} diff --git a/challenge #6/egorbwork/rocket-since-solution/CloudDrawingEngine.js b/challenge #6/egorbwork/rocket-since-solution/CloudDrawingEngine.js new file mode 100644 index 0000000..2fda8c6 --- /dev/null +++ b/challenge #6/egorbwork/rocket-since-solution/CloudDrawingEngine.js @@ -0,0 +1,107 @@ +class CloudDrawingEngine { + /** + * @param {CanvasRenderingContext2D} drawingContext + * @param {number} lineWidth + */ + constructor(drawingContext, lineWidth) { + this.drawingContext = drawingContext; + this.drawingContext.lineWidth = lineWidth; + this.drawingContext.fillStyle = 'white'; + } + + /** + * @param {Array} cloud + */ + drawCloud(cloud) { + this.drawCloudScheme(cloud); + this.removeOverlappingParts(cloud); + } + + /** + * Clear canvas and Draw scheme with all circles + * @param cloud + */ + drawCloudScheme(cloud) { + this.drawingContext.clearRect(0, 0, 5000, 5000); + this.drawScheme(cloud); + } + + /** + * For each circle in cloud draw + * @param cloud + */ + drawScheme(cloud) { + this.drawingContext.strokeStyle = 'black'; + for (let circle of cloud) { + this.drawingContext.beginPath(); + this.drawingContext.arc( + circle.centerPoint.coordinateX, + circle.centerPoint.coordinateY, + circle.radius, + 0, + 2 * Math.PI + ); + this.drawingContext.stroke(); + } + } + + /** + * Draw at overlapping parts arcs to cover them with white. + * @param {Array} cloud + */ + removeOverlappingParts(cloud) { + this.drawingContext.strokeStyle = 'white'; + // Strange But the white is one point less at drawing + this.drawingContext.lineWidth = this.drawingContext.lineWidth + 1; + // A bit improves images, and multiple number of elements is still ugly + let deltaRadians = 0.000005; + let tangentCases = this.generateTangentCases(cloud); + for (let tangentCase of tangentCases) { + // First circle + this.drawingContext.beginPath(); + this.drawingContext.arc( + tangentCase.firstCircle.centerPoint.coordinateX, + tangentCase.firstCircle.centerPoint.coordinateY, + tangentCase.firstCircle.radius, + tangentCase.firstCircle.calculateArcAngleForPoint(tangentCase.firstPoint) + deltaRadians, + tangentCase.firstCircle.calculateArcAngleForPoint(tangentCase.secondPoint) - deltaRadians, + tangentCase.firstCircleTangentOrientation + ); + this.drawingContext.stroke(); + + // Second circle + this.drawingContext.beginPath(); + this.drawingContext.arc( + tangentCase.secondCircle.centerPoint.coordinateX, + tangentCase.secondCircle.centerPoint.coordinateY, + tangentCase.secondCircle.radius, + tangentCase.secondCircle.calculateArcAngleForPoint(tangentCase.firstPoint) + deltaRadians, + tangentCase.secondCircle.calculateArcAngleForPoint(tangentCase.secondPoint) - deltaRadians, + tangentCase.secondCircleTangentOrientation + ); + this.drawingContext.stroke(); + } + this.drawingContext.lineWidth = this.drawingContext.lineWidth - 1; + } + + /** + * @param {Array} cloud + * @returns {Array} + */ + generateTangentCases(cloud) { + let tangentCases = []; + let processedList = []; + for (let circle of cloud) { + processedList.push(circle); + // Calculate tangents + let remainingCircles = cloud.filter(remainingCircle => !processedList.includes(remainingCircle)); + for (let remainingCircle of remainingCircles) { + if (circle.isTangentCircle(remainingCircle)) { + tangentCases.push(circle.calculateCloudTangentCaseForCircle(remainingCircle)); + } + } + } + + return tangentCases; + } +} diff --git a/challenge #6/egorbwork/rocket-since-solution/CloudGeneratorBuilder.js b/challenge #6/egorbwork/rocket-since-solution/CloudGeneratorBuilder.js new file mode 100644 index 0000000..a2d3da9 --- /dev/null +++ b/challenge #6/egorbwork/rocket-since-solution/CloudGeneratorBuilder.js @@ -0,0 +1,233 @@ +class CloudGeneratorBuilder { + /** + * @param {number} radiusMinimum + * @param {number} radiusMaximum + * @param {number} lineWidthConstraint + */ + constructor(radiusMinimum, radiusMaximum, lineWidthConstraint) { + this.radiusMinimum = radiusMinimum; + this.radiusMaximum = radiusMaximum; + this.coordinatesRandomGenerator = this.getCoordinatesRandomGenerator(); + this.numberOfCircles = 2; + this.lineWidthConstraint = lineWidthConstraint; + } + + setNumberOfCircles(numberOfCircles) { + this.numberOfCircles = numberOfCircles; + } + + /** + * @returns {number} + */ + getRandomRadius() { + return this.getRandomNumber(this.radiusMinimum, this.radiusMaximum / this.numberOfCircles); + } + + /** + * @param {number} minimum + * @param {number} maximum + * @returns {number} + */ + getRandomNumber(minimum, maximum) { + return Math.floor(Math.random() * (maximum - minimum)) + minimum; + } + + /** + * @param {number} minimum + * @param {number} maximum + */ + * getCoordinatesRandomGenerator() { + let minimum = 400; + let maximum = 900; + while (true) { + ({minimum, maximum} = yield this.getRandomNumber(minimum, maximum) || {minimum, maximum}); + } + } + + /** + * @returns {iterator} + */ + * getCloudGenerator() { + do { + let cloud = []; + for (let counter = 0; counter < this.numberOfCircles; counter++) { + let circle; + do { + circle = this.generateCircleForCloud(cloud); + } while (!this.isValidCircleForCloud(circle, cloud)); + cloud.push(circle) + } + yield cloud; + } while(true); + } + + /** + * @param {Array} cloud + * @returns {CloudCircle} + */ + generateCircleForCloud(cloud) { + let radius = this.getRandomRadius(); + let coordinateX = this.coordinatesRandomGenerator.next( + { + minimum: cloud.reduce( + (minimum, circle) => { + return minimum < (circle.centerPoint.coordinateX - circle.radius) + ? minimum + : circle.centerPoint.coordinateX - circle.radius; + } + , 400 + ) - 2 * radius + this.lineWidthConstraint * 2, + maximum: cloud.reduce( + (maximum, circle) => { + return maximum > (circle.centerPoint.coordinateX + circle.radius) + ? maximum + : circle.centerPoint.coordinateX + circle.radius; + } + , 800 + ) + 2 * radius - this.lineWidthConstraint * 2 + } + ).value; + let coordinateY = this.coordinatesRandomGenerator.next( + { + minimum: cloud.reduce( + (minimum, circle) => { + return minimum < (circle.centerPoint.coordinateY - circle.radius) + ? minimum + : circle.centerPoint.coordinateY - circle.radius; + } + , 400 + ) - 2 * radius + this.lineWidthConstraint * 2, + maximum: cloud.reduce( + (maximum, circle) => { + return maximum > (circle.centerPoint.coordinateY + circle.radius) + ? maximum + : circle.centerPoint.coordinateY + circle.radius; + } + , 800 + ) + 2 * radius - this.lineWidthConstraint * 2 + } + ).value; + let centerPoint = new Point(coordinateX, coordinateY); + + return new CloudCircle(centerPoint, radius); + } + + /** + * @param {CloudCircle} circle + * @param {Array} cloud + * @returns {boolean} + */ + isValidCircleForCloud(circle, cloud) { + // For empty cloud circle tangent status is true + let tangentStatus = cloud.length === 0; + for (let currentCircle of cloud) { + if (currentCircle.centerPoint.isSamePoint(circle.centerPoint)) { + return false; + } + if (currentCircle.isCircleInside(circle)) { + return false; + } + if (currentCircle.isTangentCircle(circle)) { + tangentStatus = true; + } + } + let insideCloudStatus = this.isCirclePointsInsideOfExistingCloud(circle, cloud); + let cloudCirclesInsideStatus = this.isCirclesPointsInsideOfExtendedCloud(circle, cloud); + return tangentStatus && !(insideCloudStatus || cloudCirclesInsideStatus); + } + + /** + * @param {CloudCircle} circle + * @param {Array} cloud + * @returns {boolean} + */ + isCirclesPointsInsideOfExtendedCloud(circle, cloud) { + let extendedCloud = cloud.slice(0); + extendedCloud.push(circle); + for (let currentCircle of extendedCloud) { + let remainingCloud = extendedCloud.filter(remainingCircle => remainingCircle !== currentCircle); + if (this.isCirclePointsInsideOfExistingCloud(currentCircle, remainingCloud)) { + return true; + } + } + return false; + } + + /** + * @param {CloudCircle} circle + * @param {Array} cloud + * @returns {boolean} + */ + isCirclePointsInsideOfExistingCloud(circle, cloud) { + // Can be used only radius but than some of the cases will look like overlapping from line Width + // Line width is doubled to be sure that it looks fine with simplified version + let deltaRadius = circle.radius- this.lineWidthConstraint / 2 - 1; + let northPoint = new Point( + circle.centerPoint.coordinateX, + circle.centerPoint.coordinateY - deltaRadius + ); + let southPoint = new Point( + circle.centerPoint.coordinateX, + circle.centerPoint.coordinateY + deltaRadius + ); + let eastPoint = new Point( + circle.centerPoint.coordinateX + deltaRadius, + circle.centerPoint.coordinateY + ); + let westPoint = new Point( + circle.centerPoint.coordinateX - deltaRadius, + circle.centerPoint.coordinateY + ); + // Not accurate but better than nothing + // Using for point coordinates calculation angle of 45 degrees + let sin45 = Math.sin(this.convertDegreesToRadians(45)); + let cos45 = Math.cos(this.convertDegreesToRadians(45)); + + let northEastPoint = new Point( + circle.centerPoint.coordinateX + deltaRadius * cos45, + circle.centerPoint.coordinateY - deltaRadius * sin45 + ); + let northWestPoint = new Point( + circle.centerPoint.coordinateX - deltaRadius * cos45, + circle.centerPoint.coordinateY - deltaRadius * sin45 + ); + let southEastPoint = new Point( + circle.centerPoint.coordinateX + deltaRadius * cos45, + circle.centerPoint.coordinateY + deltaRadius * sin45 + ); + let southWestPoint = new Point( + circle.centerPoint.coordinateX - deltaRadius * cos45, + circle.centerPoint.coordinateY + deltaRadius * sin45 + ); + + return this.isPointInsideOfExistingCloud(northPoint, cloud) + && this.isPointInsideOfExistingCloud(southPoint, cloud) + && this.isPointInsideOfExistingCloud(eastPoint, cloud) + && this.isPointInsideOfExistingCloud(westPoint, cloud) + && this.isPointInsideOfExistingCloud(northEastPoint, cloud) + && this.isPointInsideOfExistingCloud(northWestPoint, cloud) + && this.isPointInsideOfExistingCloud(southEastPoint, cloud) + && this.isPointInsideOfExistingCloud(southWestPoint, cloud); + } + + /** + * @param {Point} point + * @param {Array} cloud + * @returns {boolean} + */ + isPointInsideOfExistingCloud(point, cloud) { + let isPointInsideOfCloud = false; + for (let currentCircle of cloud) { + isPointInsideOfCloud = isPointInsideOfCloud || currentCircle.isPointInsideOfCircle(point); + } + return isPointInsideOfCloud; + } + + /** + * @param {number} degrees + * @returns {number} + */ + convertDegreesToRadians(degrees) { + return (Math.PI / 180) * degrees; + } +} diff --git a/challenge #6/egorbwork/rocket-since-solution/CloudTangentCase.js b/challenge #6/egorbwork/rocket-since-solution/CloudTangentCase.js new file mode 100644 index 0000000..df55082 --- /dev/null +++ b/challenge #6/egorbwork/rocket-since-solution/CloudTangentCase.js @@ -0,0 +1,67 @@ +class CloudTangentCase { + /** + * @param {Point} firstPoint + * @param {Point} secondPoint + * @param {CloudCircle} firstCircle + * @param {CloudCircle} secondCircle + */ + constructor(firstPoint, secondPoint, firstCircle, secondCircle) { + // Order the tangent points from bottom to top and left to right + if ((firstPoint.coordinateX > secondPoint.coordinateX) + || (firstPoint.coordinateX === secondPoint.coordinateX && firstPoint.coordinateY < secondPoint.coordinateY) + ) { + this.firstPoint = secondPoint; + this.secondPoint = firstPoint; + } else { + this.firstPoint = firstPoint; + this.secondPoint = secondPoint; + } + // Order the circles vice versa from right to left + if (firstCircle.centerPoint.coordinateX > secondCircle.centerPoint.coordinateX) { + this.firstCircle = firstCircle; + this.secondCircle = secondCircle; + } else { + this.firstCircle = secondCircle; + this.secondCircle = firstCircle; + } + this.firstCircleTangentOrientation = false; + this.secondCircleTangentOrientation = false; + this.calculateOverlapOrientations(); + } + + /** + * Calculates when the overlapped part of tangent circles should be covered clockwise and anti-clockwise + */ + calculateOverlapOrientations() { + let firstTestPoint = this.calculateTestPointForCircle(this.firstCircle); + // For case of the same coordinateX the anti-clockwise should be applied for negated case + this.firstCircleTangentOrientation = (this.firstPoint.coordinateX !== this.secondPoint.coordinateX + && this.secondCircle.isPointInsideOfCircle(firstTestPoint)) || (this.firstPoint.coordinateX === this.secondPoint.coordinateX + && !this.secondCircle.isPointInsideOfCircle(firstTestPoint)); + this.secondCircleTangentOrientation = !this.firstCircleTangentOrientation; + } + + /** + * Generates a test point for checking orientation of the arc + * @param {CloudCircle} circle + * @returns {Point} + */ + calculateTestPointForCircle(circle) { + let testPositionX, testPositionY; + if (this.firstPoint.coordinateX !== this.secondPoint.coordinateX) { + testPositionX = (this.firstPoint.coordinateX + this.secondPoint.coordinateX) / 2; + let relativeY = Math.sqrt( + circle.radius ** 2 - (testPositionX - circle.centerPoint.coordinateX) ** 2 + ); + testPositionY = circle.centerPoint.coordinateY + relativeY; + } else { + testPositionY = (this.firstPoint.coordinateY + this.secondPoint.coordinateY) / 2; + let relativeX = Math.sqrt( + circle.radius ** 2 - (testPositionY - circle.centerPoint.coordinateY) ** 2 + ); + testPositionX = circle.centerPoint.coordinateX - relativeX; + } + + return new Point(testPositionX, testPositionY); + } +} diff --git a/challenge #6/egorbwork/rocket-since-solution/Point.js b/challenge #6/egorbwork/rocket-since-solution/Point.js new file mode 100644 index 0000000..50a4729 --- /dev/null +++ b/challenge #6/egorbwork/rocket-since-solution/Point.js @@ -0,0 +1,28 @@ +class Point { + /** + * @param {number} coordinateX + * @param {number} coordinateY + */ + constructor(coordinateX, coordinateY) { + this.coordinateX = coordinateX; + this.coordinateY = coordinateY; + } + + /** + * @param {Point} point + * @returns {number} + */ + getDistanceFor(point) { + return Math.sqrt( + (point.coordinateX - this.coordinateX) ** 2 + (point.coordinateY - this.coordinateY) ** 2 + ); + } + + /** + * @param {Point} point + * @returns {boolean} + */ + isSamePoint(point) { + return this.coordinateX === point.coordinateX && this.coordinateY === point.coordinateY; + } +} diff --git a/challenge #6/egorbwork/rocket-since-solution/application.js b/challenge #6/egorbwork/rocket-since-solution/application.js new file mode 100644 index 0000000..1c376aa --- /dev/null +++ b/challenge #6/egorbwork/rocket-since-solution/application.js @@ -0,0 +1,53 @@ +/** + * More correct solution, but looks a bit ugly and have sometimes same problems with overlapping + */ + +let lineWidth = 5; +let cloudGeneratorBuilder = new CloudGeneratorBuilder(10, 800, lineWidth); +let cloudGenerator = cloudGeneratorBuilder.getCloudGenerator(); +let scheme = false; +let drawingEngine, cloud; + +document.addEventListener("DOMContentLoaded", function() { + cloudGeneratorBuilder.setNumberOfCircles(8); + drawingEngine = new CloudDrawingEngine(document.getElementById('cloud').getContext('2d'), lineWidth); + cloud = cloudGenerator.next().value; + drawingEngine.drawCloud(cloud); + + addSchemeButtonHandler(); + addRedrawButtonHandler(); +}); + +function addSchemeButtonHandler() { + let schemeButton = document.getElementById('scheme'); + + schemeButton.addEventListener('click', () => { + let alternativeText = schemeButton.getAttribute('data-text'); + schemeButton.setAttribute('data-text', schemeButton.textContent); + schemeButton.textContent = alternativeText; + scheme = !scheme; + if (scheme) { + drawingEngine.drawScheme(cloud); + } else { + drawingEngine.drawCloud(cloud); + } + }); +} + +function addRedrawButtonHandler() { + let redrawButton = document.getElementById('redraw'); + let inputNumber = document.getElementById('numberOfCircles'); + + redrawButton.addEventListener('click', () => { + let numberOfCircles = inputNumber.value; + cloudGeneratorBuilder.setNumberOfCircles(numberOfCircles); + cloud = cloudGenerator.next().value; + if (scheme) { + drawingEngine.drawCloudScheme(cloud); + } else { + drawingEngine.drawCloud(cloud); + } + }); +} + + diff --git a/challenge #6/egorbwork/rocket-since-solution/cloud.html b/challenge #6/egorbwork/rocket-since-solution/cloud.html new file mode 100644 index 0000000..33be206 --- /dev/null +++ b/challenge #6/egorbwork/rocket-since-solution/cloud.html @@ -0,0 +1,28 @@ + + + + + Cloud Generator + + + + + + + + +
+ + + + +
+
+ +
+ To be able to see content, please use a modern version of Chrome or Firefox! +
+
+
+ + From dcee8ac671e28aa735334397c221ee52440aaaa6 Mon Sep 17 00:00:00 2001 From: Egor Boaghi Date: Sun, 18 Feb 2018 00:17:55 +0200 Subject: [PATCH 11/11] Fix folder name of better solution of the 6 challenge. --- .../CloudCircle.js | 0 .../CloudDrawingEngine.js | 0 .../CloudGeneratorBuilder.js | 0 .../CloudTangentCase.js | 0 .../{rocket-since-solution => rocket-sience-solution}/Point.js | 0 .../application.js | 0 .../{rocket-since-solution => rocket-sience-solution}/cloud.html | 0 7 files changed, 0 insertions(+), 0 deletions(-) rename challenge #6/egorbwork/{rocket-since-solution => rocket-sience-solution}/CloudCircle.js (100%) rename challenge #6/egorbwork/{rocket-since-solution => rocket-sience-solution}/CloudDrawingEngine.js (100%) rename challenge #6/egorbwork/{rocket-since-solution => rocket-sience-solution}/CloudGeneratorBuilder.js (100%) rename challenge #6/egorbwork/{rocket-since-solution => rocket-sience-solution}/CloudTangentCase.js (100%) rename challenge #6/egorbwork/{rocket-since-solution => rocket-sience-solution}/Point.js (100%) rename challenge #6/egorbwork/{rocket-since-solution => rocket-sience-solution}/application.js (100%) rename challenge #6/egorbwork/{rocket-since-solution => rocket-sience-solution}/cloud.html (100%) diff --git a/challenge #6/egorbwork/rocket-since-solution/CloudCircle.js b/challenge #6/egorbwork/rocket-sience-solution/CloudCircle.js similarity index 100% rename from challenge #6/egorbwork/rocket-since-solution/CloudCircle.js rename to challenge #6/egorbwork/rocket-sience-solution/CloudCircle.js diff --git a/challenge #6/egorbwork/rocket-since-solution/CloudDrawingEngine.js b/challenge #6/egorbwork/rocket-sience-solution/CloudDrawingEngine.js similarity index 100% rename from challenge #6/egorbwork/rocket-since-solution/CloudDrawingEngine.js rename to challenge #6/egorbwork/rocket-sience-solution/CloudDrawingEngine.js diff --git a/challenge #6/egorbwork/rocket-since-solution/CloudGeneratorBuilder.js b/challenge #6/egorbwork/rocket-sience-solution/CloudGeneratorBuilder.js similarity index 100% rename from challenge #6/egorbwork/rocket-since-solution/CloudGeneratorBuilder.js rename to challenge #6/egorbwork/rocket-sience-solution/CloudGeneratorBuilder.js diff --git a/challenge #6/egorbwork/rocket-since-solution/CloudTangentCase.js b/challenge #6/egorbwork/rocket-sience-solution/CloudTangentCase.js similarity index 100% rename from challenge #6/egorbwork/rocket-since-solution/CloudTangentCase.js rename to challenge #6/egorbwork/rocket-sience-solution/CloudTangentCase.js diff --git a/challenge #6/egorbwork/rocket-since-solution/Point.js b/challenge #6/egorbwork/rocket-sience-solution/Point.js similarity index 100% rename from challenge #6/egorbwork/rocket-since-solution/Point.js rename to challenge #6/egorbwork/rocket-sience-solution/Point.js diff --git a/challenge #6/egorbwork/rocket-since-solution/application.js b/challenge #6/egorbwork/rocket-sience-solution/application.js similarity index 100% rename from challenge #6/egorbwork/rocket-since-solution/application.js rename to challenge #6/egorbwork/rocket-sience-solution/application.js diff --git a/challenge #6/egorbwork/rocket-since-solution/cloud.html b/challenge #6/egorbwork/rocket-sience-solution/cloud.html similarity index 100% rename from challenge #6/egorbwork/rocket-since-solution/cloud.html rename to challenge #6/egorbwork/rocket-sience-solution/cloud.html