A triangular grid with some triangles merged together.
Log in to post a comment.
class Point { constructor(x, y) { this.x = x; this.y = y; } eq(other) { return ((this.x == other.x) && (this.y == other.y)); } toArray() { return [this.x, this.y]; } toString() { return `Point(${this.x}, ${this.y})`; } } class LineSegment { constructor(p1, p2) { this.p1 = p1; this.p2 = p2; } eq(other) { if ((this.p1.eq(other.p1)) && (this.p2.eq(other.p2))) { return true; } if ((this.p1.eq(other.p2)) && (this.p2.eq(other.p1))) { return true; } return false; } draw() { turtle.goto(this.p1.toArray()); turtle.pendown(); turtle.goto(this.p2.toArray()); turtle.penup(); } toString() { return `LineSegment(${this.p1.toString()}, ${this.p2.toString()})`; } } // Just a list of line segments for now // This will be slow (TODO: make less slow) class Polygon { constructor(edges) { this.edges = edges; } // In this case, two polygons are adjacent if they share any whole edge adjacent(other) { for (var i = 0; i < this.edges.length; i++) { let thisEdge = this.edges[i]; for (var j = 0; j < other.edges.length; j++) { let otherEdge = other.edges[j]; if (thisEdge.eq(otherEdge)) { return true; } } } return false; } // Returns a new polygon with edges that are in both this and other intersection(other) { let commonEdges = []; for (var i = 0; i < this.edges.length; i++) { let thisEdge = this.edges[i]; for (var j = 0; j < other.edges.length; j++) { let otherEdge = other.edges[j]; if (thisEdge.eq(otherEdge)) { commonEdges.push(thisEdge); } } } return new Polygon(commonEdges); } // Returns a new polygon with edges that are in either this or other union(other) { return new Polygon(this.edges.concat(other.edges)); } // Returns a new polygon with only those edges in exactly one of this or other merge(other) { let unionEdges = this.union(other).edges; let intersectionEdges = this.intersection(other).edges; let mergedEdges = []; for (var i = 0; i < unionEdges.length; i++) { let unionEdge = unionEdges[i]; let unique = true; for (var j = 0; j < intersectionEdges.length; j++) { let intersectionEdge = intersectionEdges[j]; if (unionEdge.eq(intersectionEdge)) { unique = false; break; } } if (unique) { mergedEdges.push(unionEdge); } } return new Polygon(mergedEdges); } toString() { return `Polygon[${this.edges.map(edge => edge.toString())}]`; } } class Vector2d { constructor(x, y) { this.x = x; this.y = y; } scale(n) { return new Vector2d(this.x * n, this.y * n); } add(other) { return new Vector2d(this.x + other.x, this.y + other.y); } subtract(other) { return new Vector2d(this.x - other.x, this.y - other.y); } } function randomIndex(list) { return Math.floor(list.length * Math.random()); } function randomIndexList(list) { let indexList = []; for (var n = 0; n < list.length; n++) { indexList.push(n); } let i = 0; let j = 0; let temp; for (i = indexList.length - 1; i > 0; i-=1) { j = Math.floor(Math.random() * (i + 1)); temp = indexList[i]; indexList[i] = indexList[j]; indexList[j] = temp; } return indexList; } function weightedRandomIndex(list, f) { let total = 0; let cumulativeSums = []; for (var i = 0; i < list.length; i++) { total += f(list[i]); cumulativeSums.push(total); } let rand = Math.random() * total; let idx = 0; while (cumulativeSums[idx] < rand) { idx++; } return idx; } function makeTriangle(startX, startY, inverted) { let vertices = []; vertices.push(new Point(startX, startY)); vertices.push(new Point(startX + edgeLength, startY)); if (inverted) { vertices.push(new Point(startX + edgeLength / 2, startY - edgeLength * sqrt3 / 2)); } else { vertices.push(new Point(startX + edgeLength / 2, startY + edgeLength * sqrt3 / 2)); } let lineSegments = [ new LineSegment(vertices[0], vertices[1]), new LineSegment(vertices[1], vertices[2]), new LineSegment(vertices[2], vertices[0]) ]; return new Polygon(lineSegments); } // Parameters Canvas.setpenopacity(1); const edgeLength = 6; const fractionToMerge = 0.9; const weightFunction = function(p) { return 1 / (p.edges.length ** 2); } const turtle = new Turtle(); turtle.penup(); const sqrt3 = Math.sqrt(3); const canvasSize = 100; const horizontalVector = new Vector2d(edgeLength, 0); const diagonalVector = new Vector2d(edgeLength / 2, edgeLength * sqrt3 / 2); let minIndex = Math.floor(-canvasSize * sqrt3 / edgeLength); let maxIndex = Math.ceil(canvasSize * sqrt3 / edgeLength) // Create all of the triangles let polygons = []; for (var i = minIndex; i < maxIndex; i++) { for (var j = minIndex; j < maxIndex; j++) { let start = horizontalVector.scale(i).add(diagonalVector.scale(j)); polygons.push(makeTriangle(start.x, start.y, false)); polygons.push(makeTriangle(start.x, start.y, true)); } } // Merge some of the triangles let numMerges = polygons.length * fractionToMerge; for (var m = 0; m < numMerges; m++) { let i = weightedRandomIndex(polygons, weightFunction); let polygonToMerge = polygons[i]; let mergeOrder = randomIndexList(polygons); for (var n = 0; n < mergeOrder.length; n++) { j = mergeOrder[n]; if (i != j) { let otherPolygon = polygons[j]; if (polygonToMerge.adjacent(otherPolygon)) { let mergedPolygon = polygonToMerge.merge(otherPolygon); polygons.splice(Math.max(i, j), 1); polygons.splice(Math.min(i, j), 1); polygons.push(mergedPolygon); break; } } } } function walk(i) { for (i = 0; i < polygons.length; i++) { let edges = polygons[i].edges; for (j = 0; j < edges.length; j++) { edges[j].draw(); } } return false; }