diff --git a/.github/gen_readme.js b/.github/gen_readme.js new file mode 100755 index 0000000000..a3345ee1fe --- /dev/null +++ b/.github/gen_readme.js @@ -0,0 +1,45 @@ +#!/usr/bin/env node + +const fs = require("fs"); +const Handlebars = require("handlebars"); +const path = require("path"); + +const slurp = (p) => fs.readFileSync(p).toString(); +const example = (p) => slurp(path.join("packages/examples/src", p)); + +const sub = "tree"; +const sty = "venn"; + +const { trios, domains, styles, substances } = JSON.parse( + example("registry.json") +); + +const matching = trios.filter( + ({ substance, style }) => substance === sub && style === sty +); +if (matching.length !== 1) { + throw Error(`expected exactly one matching trio, got ${matching.length}`); +} +const [{ substance, style, domain, variation }] = matching; + +const dslURI = domains[domain].URI; +const subURI = substances[substance].URI; +const styURI = styles[style].URI; + +for (const name of ["domain", "substance", "style"]) { + Handlebars.registerPartial(name, `{{{${name}}}}`); +} + +fs.writeFileSync( + "README.md", + Handlebars.compile(slurp(".github/readme_template.hbs"))({ + svg: `diagrams/${sub}-${sty}.svg`, + variation, + dsl: path.basename(dslURI), + sub: path.basename(subURI), + sty: path.basename(styURI), + domain: example(dslURI), + substance: example(subURI), + style: example(styURI), + }) +); diff --git a/.github/readme_template.hbs b/.github/readme_template.hbs new file mode 100644 index 0000000000..653dba84ab --- /dev/null +++ b/.github/readme_template.hbs @@ -0,0 +1,45 @@ +# Penrose [![Build](https://github.com/penrose/penrose/actions/workflows/build.yml/badge.svg)](https://github.com/penrose/penrose/actions/workflows/build.yml) [![codecov](https://codecov.io/gh/penrose/penrose/branch/main/graph/badge.svg?token=opGTmY4rkK)](https://codecov.io/gh/penrose/penrose) [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier) [![license](https://img.shields.io/github/license/penrose/penrose)](LICENSE) [![Twitter: @UsePenrose](https://img.shields.io/twitter/follow/UsePenrose?style=social)](https://twitter.com/UsePenrose) + +[Penrose](https://penrose.cs.cmu.edu/) is a platform that enables people to +**create beautiful diagrams just by typing mathematical notation in plain +text.** The goal is to make it easy for non-experts to create and explore +high-quality diagrams and provide deeper insight into challenging technical +concepts. We aim to democratize the process of creating visual intuition. + +Check out our [SIGGRAPH '20 paper](https://penrose.cs.cmu.edu/siggraph20) and +[video](https://vimeo.com/416822487) on Penrose! + +## Usage + +You can [try Penrose in your browser](https://penrose.cs.cmu.edu/try/) without +any installation. For a more detailed step-by-step introduction, check out our +[tutorials](https://penrose.cs.cmu.edu/docs/tutorial/welcome). Or, for more +reference-style information, take a look at our +[documentation](https://penrose.cs.cmu.edu/docs/ref/). + +## Example + +Here's a simple Penrose visualization in the domain of set theory. + + + +It's specified by the following trio of Domain, Substance, and Style programs +(with variation `{{variation}}`): + +- `{{dsl}}`: + + ``` + {{> domain }} + ``` + +- `{{sub}}`: + + ``` + {{> substance }} + ``` + +- `{{sty}}`: + + ``` + {{> style }} + ``` diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 42084ceb83..eb76a1fb63 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,7 +26,7 @@ jobs: with: name: diagrams path: packages/automator/out/ - - name: Copy generated diagrams into diagrams/ + - name: Check that the generated diagrams/ are all up to date run: | rm -r diagrams/ mkdir diagrams/ @@ -35,9 +35,7 @@ jobs: name=${dir%/output.svg} cp "$filename" "diagrams/$name.svg" done - - name: Ensure no generated diagrams have changed - run: | - "$GITHUB_WORKSPACE/.github/report_git_status.sh" + .github/report_git_status.sh build: runs-on: ubuntu-latest @@ -92,6 +90,18 @@ jobs: - name: Lint run: yarn lint + readme: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Install packages + uses: ./.github/actions/packages + - name: Check that the generated README is up to date + run: | + .github/gen_readme.js + .github/report_git_status.sh + storybook: runs-on: ubuntu-latest defaults: diff --git a/.prettierignore b/.prettierignore index 74ef7b1ce7..a5b842f723 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,3 +1,4 @@ +**/*.hbs **/build/ **/coverage/ **/dist/ @@ -14,3 +15,4 @@ packages/core/src/parser/SubstanceParser.ts packages/core/src/renderer/not_found.json packages/docs-site/.docusaurus/ packages/roger/oclif.manifest.json +README.md diff --git a/README.md b/README.md index d0385ff0d8..9b8cfc3fef 100644 --- a/README.md +++ b/README.md @@ -1,71 +1,100 @@ # Penrose [![Build](https://github.com/penrose/penrose/actions/workflows/build.yml/badge.svg)](https://github.com/penrose/penrose/actions/workflows/build.yml) [![codecov](https://codecov.io/gh/penrose/penrose/branch/main/graph/badge.svg?token=opGTmY4rkK)](https://codecov.io/gh/penrose/penrose) [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier) [![license](https://img.shields.io/github/license/penrose/penrose)](LICENSE) [![Twitter: @UsePenrose](https://img.shields.io/twitter/follow/UsePenrose?style=social)](https://twitter.com/UsePenrose) -**Penrose is an early-stage system that is still in development.** Our system is not ready for contributions or public use yet, but hopefully will be soon. Send us an email if you're interested in collaborating. +[Penrose](https://penrose.cs.cmu.edu/) is a platform that enables people to +**create beautiful diagrams just by typing mathematical notation in plain +text.** The goal is to make it easy for non-experts to create and explore +high-quality diagrams and provide deeper insight into challenging technical +concepts. We aim to democratize the process of creating visual intuition. -- See [the site](http://www.penrose.ink/) for more information and examples. -- See the [wiki](https://github.com/penrose/penrose/wiki) for more system-specific information on building, running, testing, and debugging the system. -- For even more documentation, see Nimo Ni's [README](https://github.com/wodeni/notes-public/blob/master/penrose/archive/ramp-down.md). +Check out our [SIGGRAPH '20 paper](https://penrose.cs.cmu.edu/siggraph20) and +[video](https://vimeo.com/416822487) on Penrose! -### Example +## Usage + +You can [try Penrose in your browser](https://penrose.cs.cmu.edu/try/) without +any installation. For a more detailed step-by-step introduction, check out our +[tutorials](https://penrose.cs.cmu.edu/docs/tutorial/welcome). Or, for more +reference-style information, take a look at our +[documentation](https://penrose.cs.cmu.edu/docs/ref/). + +## Example Here's a simple Penrose visualization in the domain of set theory. - + -It's specified by the following Substance and Style programs. +It's specified by the following trio of Domain, Substance, and Style programs +(with variation `CasalViper643`): + +- `setTheory.dsl`: -- `tree.sub` ``` - Set A - Set B - Set C - Set D - Set E - Set F - Set G - Subset B A - Subset C A - Subset D B - Subset E B - Subset F C - Subset G C - NoIntersect E D - NoIntersect F G - NoIntersect B C + type Set + + predicate Not(Prop p1) + predicate Intersecting(Set s1, Set s2) + predicate IsSubset(Set s1, Set s2) ``` -- `venn.sty` + +- `tree.sub`: ``` - Set x { - shape = Circle { } - constraint contains(x, x.label) - } + Set A, B, C, D, E, F, G + + IsSubset(B, A) + IsSubset(C, A) + IsSubset(D, B) + IsSubset(E, B) + IsSubset(F, C) + IsSubset(G, C) + + Not(Intersecting(E, D)) + Not(Intersecting(F, G)) + Not(Intersecting(B, C)) + + AutoLabel All + ``` - Intersect x y { - constraint overlapping(x, y) - constraint disjoint(y.label, x) - constraint disjoint(x.label, y) - } +- `venn.sty`: - NoIntersect x y { - constraint nonOverlapping(x, y) + ``` + canvas { + width = 800 + height = 700 } - - Subset x y { - constraint contains(y, x) - constraint smallerThan(x, y) - constraint disjoint(y.label, x) + + forall Set x { + x.icon = Circle { + strokeWidth : 0 + } + + x.text = Equation { + string : x.label + fontSize : "25px" + } + + ensure contains(x.icon, x.text) + encourage sameCenter(x.text, x.icon) + x.textLayering = x.text above x.icon } - - NoSubset x y { - objective repel(x, y) - constraint disjoint(x, y) - constraint disjoint(y.label, x) - constraint disjoint(x.label, y) - constraint nonOverlapping(x, y) + + forall Set x; Set y + where IsSubset(x, y) { + ensure smallerThan(x.icon, y.icon) + ensure disjoint(y.text, x.icon, 10) + ensure contains(y.icon, x.icon, 5) + x.icon above y.icon + } + + forall Set x; Set y + where Not(Intersecting(x, y)) { + ensure disjoint(x.icon, y.icon) + } + + forall Set x; Set y + where Intersecting(x, y) { + ensure overlapping(x.icon, y.icon) + ensure disjoint(y.text, x.icon) + ensure disjoint(x.text, y.icon) } ``` - -Here's how the optimization looks live in the UI. - - diff --git a/diagrams/tree-venn.svg b/diagrams/tree-venn.svg index 9623e6c745..1f73eb96f4 100644 --- a/diagrams/tree-venn.svg +++ b/diagrams/tree-venn.svg @@ -1,39 +1,39 @@ A.icon C.icon G.icon @@ -72,18 +72,18 @@ F.icon @@ -122,29 +122,29 @@ B.icon E.icon @@ -183,18 +183,18 @@ D.icon @@ -233,7 +233,7 @@ @@ -272,7 +272,7 @@ @@ -311,7 +311,7 @@ diff --git a/package.json b/package.json index 8c661e800b..83e11b1fe2 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ }, "devDependencies": { "cross-env": "^7.0.3", + "handlebars": "^4.7.7", "lerna": "^3.22.1", "prettier": "2.2.1", "prettier-plugin-organize-imports": "^2.3.4", diff --git a/packages/core/src/compiler/Style.test.ts b/packages/core/src/compiler/Style.test.ts index 5a6b06f2cb..4a5a304010 100644 --- a/packages/core/src/compiler/Style.test.ts +++ b/packages/core/src/compiler/Style.test.ts @@ -374,7 +374,7 @@ describe("Compiler", () => { describe("Expected Style errors", () => { const subProg = loadFile("set-theory-domain/twosets-simple.sub"); - const domainProg = loadFile("set-theory-domain/setTheory.dsl"); + const domainProg = loadFile("set-theory-domain/functions.dsl"); // We test variations on this Style program // const styPath = "set-theory-domain/venn.sty"; diff --git a/packages/core/src/compiler/Substance.test.ts b/packages/core/src/compiler/Substance.test.ts index 06598d1b7a..c93d76d994 100644 --- a/packages/core/src/compiler/Substance.test.ts +++ b/packages/core/src/compiler/Substance.test.ts @@ -18,7 +18,7 @@ const outputDir = "/tmp/contexts"; const subPaths = [ // "linear-algebra-domain/twoVectorsPerp.sub", ["set-theory-domain/setTheory.dsl", "set-theory-domain/tree.sub"], - ["set-theory-domain/setTheory.dsl", "set-theory-domain/continuousmap.sub"], + ["set-theory-domain/functions.dsl", "set-theory-domain/continuousmap.sub"], ["set-theory-domain/setTheory.dsl", "set-theory-domain/twosets-simple.sub"], ["set-theory-domain/setTheory.dsl", "set-theory-domain/multisets.sub"], ["set-theory-domain/setTheory.dsl", "set-theory-domain/nested.sub"], diff --git a/packages/core/src/utils/Graph.test.ts b/packages/core/src/utils/Graph.test.ts deleted file mode 100644 index 1502723cdd..0000000000 --- a/packages/core/src/utils/Graph.test.ts +++ /dev/null @@ -1,118 +0,0 @@ -import { - CompressedAdjacencyGraph, - createAdjacencyMatrix, - createCompressedMatrix, - NodeWithEdges, - Vertex, -} from "./Graph"; -class IntNode implements NodeWithEdges { - value: number; - children: NodeWithEdges[] = []; - constructor(value: number) { - this.value = value; - } -} - -describe("Adjacency matrix creation tests", () => { - test("cycle between two nodes", () => { - const one = new IntNode(1); - const two = new IntNode(2); - one.children.push(two); - two.children.push(one); - expect(createAdjacencyMatrix([one, two])).toEqual([ - [false, true], - [true, false], - ]); - }); - test("no edges two nodes", () => { - const one = new IntNode(1); - const two = new IntNode(2); - expect(createAdjacencyMatrix([one, two])).toEqual([ - [false, false], - [false, false], - ]); - }); - test("six node tree", () => { - const one = new IntNode(1); - const two = new IntNode(2); - const three = new IntNode(3); - const four = new IntNode(4); - const five = new IntNode(5); - const six = new IntNode(6); - one.children.push(two); - two.children.push(three); - three.children.push(four); - three.children.push(five); - three.children.push(six); - expect(createAdjacencyMatrix([one, two, three, four, five, six])).toEqual([ - [false, true, false, false, false, false], - [false, false, true, false, false, false], - [false, false, false, true, true, true], - [false, false, false, false, false, false], - [false, false, false, false, false, false], - [false, false, false, false, false, false], - ]); - }); -}); - -describe("Compressed Adjacency Matrix tests", () => { - test("cycle between two nodes", () => { - const adjacencyMatrix = [ - [false, true], - [true, false], - ]; - const cm = createCompressedMatrix(adjacencyMatrix); - expect(cm.rowIndices).toEqual([1, 0]); - expect(cm.cumulativeEntriesByColumn).toEqual([1, 2]); - expect(cm.columnIndices).toEqual([1, 0]); - expect(cm.cumulativeEntriesByRow).toEqual([1, 2]); - }); - test("keenan slide http://15462.courses.cs.cmu.edu/fall2021/lecture/meshes/slide_021", () => { - const adjacencyMatrix = [ - [true, true, false], - [false, false, true], - [false, true, false], - ]; - const cm = createCompressedMatrix(adjacencyMatrix); - expect(cm.rowIndices).toEqual([0, 0, 2, 1]); - expect(cm.cumulativeEntriesByColumn).toEqual([1, 3, 4]); - expect(cm.columnIndices).toEqual([0, 1, 2, 1]); - expect(cm.cumulativeEntriesByRow).toEqual([2, 3, 4]); - }); -}); - -describe("Graph adjacency matrix tests", () => { - test("keenan slide http://15462.courses.cs.cmu.edu/fall2021/lecture/meshes/slide_021", () => { - const adjacencyMatrix = [ - [true, true, false], - [false, false, true], - [false, true, false], - ]; - const cm = createCompressedMatrix(adjacencyMatrix); - const nodeList: Vertex[] = [ - { index: 0, value: 1 }, - { index: 1, value: 2 }, - { index: 2, value: 3 }, - ]; - const g = new CompressedAdjacencyGraph(nodeList, cm); - expect(g.childrenOf(0)).toEqual([0]); - expect(g.childrenOf(1)).toEqual([0, 2]); - expect(g.childrenOf(2)).toEqual([1]); - expect(g.parentsOf(0)).toEqual([0, 1]); - }); - test("one node, no edges test", () => { - const adjacencyMatrix = [[false]]; - const cm = createCompressedMatrix(adjacencyMatrix); - const nodeList: Vertex[] = [{ index: 0, value: 1 }]; - const g = new CompressedAdjacencyGraph(nodeList, cm); - expect(g.childrenOf(0)).toEqual([]); - }); - test("one node, self-edge test", () => { - const adjacencyMatrix = [[true]]; - const cm = createCompressedMatrix(adjacencyMatrix); - const nodeList: Vertex[] = [{ index: 0, value: 1 }]; - const g = new CompressedAdjacencyGraph(nodeList, cm); - expect(g.childrenOf(0)).toEqual([0]); - expect(g.childrenOf(0)).toEqual([0]); - }); -}); diff --git a/packages/core/src/utils/Graph.ts b/packages/core/src/utils/Graph.ts index 7174a15fc0..ed6432c1e7 100644 --- a/packages/core/src/utils/Graph.ts +++ b/packages/core/src/utils/Graph.ts @@ -1,121 +1,5 @@ import * as graphlib from "graphlib"; -//#region compressed matrix - -export interface NodeWithEdges { - value: T; - children: NodeWithEdges[]; -} - -export interface Vertex { - index: number; - value: T; -} - -export const createAdjacencyMatrix = ( - nodeList: NodeWithEdges[] -): boolean[][] => { - const matrix = createSquareMatrix(nodeList.length, false); - let i = 0; - nodeList.forEach((node) => { - node.children.forEach((childNode) => { - const j = nodeList.lastIndexOf(childNode); - matrix[i][j] = true; - }); - i++; - }); - return matrix; -}; - -export interface CompressedMatrix { - rowIndices: Array; - cumulativeEntriesByColumn: Array; - columnIndices: Array; - cumulativeEntriesByRow: Array; -} - -// immutable Graph -export interface Graph { - nodeList: Vertex[]; - parentsOf: (nodeIndex: number) => number[]; - childrenOf: (nodeIndex: number) => number[]; -} - -export class CompressedAdjacencyGraph implements Graph { - nodeList: Vertex[]; - cm: CompressedMatrix; - - constructor(nodeList: Vertex[], cm: CompressedMatrix) { - this.nodeList = nodeList; - this.cm = cm; - } - parentsOf = (nodeIndex: number): number[] => { - let startIndex = 0; - if (nodeIndex > 0) { - startIndex = this.cm.cumulativeEntriesByRow[nodeIndex - 1]; - } - const endIndex = this.cm.cumulativeEntriesByRow[nodeIndex]; - return this.cm.columnIndices.slice(startIndex, endIndex); - }; - childrenOf = (nodeIndex: number): number[] => { - let startIndex = 0; - if (nodeIndex > 0) { - startIndex = this.cm.cumulativeEntriesByColumn[nodeIndex - 1]; - } - const endIndex = this.cm.cumulativeEntriesByColumn[nodeIndex]; - return this.cm.rowIndices.slice(startIndex, endIndex); - }; -} - -export const createCompressedMatrix = ( - adjacencyMatrix: boolean[][] -): CompressedMatrix => { - const cm = { - rowIndices: new Array(), - cumulativeEntriesByColumn: new Array(), - columnIndices: new Array(), - cumulativeEntriesByRow: new Array(), - }; - let cumulativeEntries = 0; - const size = adjacencyMatrix.length; - for (let i = 0; i < size; i++) { - for (let j = 0; j < size; j++) { - if (adjacencyMatrix[j][i]) { - cm.rowIndices.push(j); - cumulativeEntries++; - } - } - cm.cumulativeEntriesByColumn.push(cumulativeEntries); - } - cumulativeEntries = 0; - for (let i = 0; i < size; i++) { - for (let j = 0; j < size; j++) { - if (adjacencyMatrix[i][j]) { - cm.columnIndices.push(j); - cumulativeEntries++; - } - } - cm.cumulativeEntriesByRow.push(cumulativeEntries); - } - return cm; -}; - -function createSquareMatrix(width: number, defaultValue = false): boolean[][] { - const matrix = []; - for (let i = 0; i < width; i++) { - const row: boolean[] = []; - for (let j = 0; j < width; j++) { - row.push(defaultValue); - } - matrix.push(row); - } - return matrix; -} - -//#endregion - -//#region graphlib wrapper - export interface Edge< NodeId extends string, EdgeName extends string | undefined @@ -250,5 +134,3 @@ export class Multidigraph< return graphlib.alg.topsort(this._graph) as NodeId[]; } } - -//#endregion diff --git a/packages/examples/src/registry.json b/packages/examples/src/registry.json index 8f32ef7db9..734ce1dfe8 100644 --- a/packages/examples/src/registry.json +++ b/packages/examples/src/registry.json @@ -5,7 +5,7 @@ "substance": "tree", "style": "venn", "domain": "set-theory", - "variation": "" + "variation": "CasalViper643" }, { "substance": "tree", @@ -16,7 +16,7 @@ { "substance": "continuousmap", "style": "continuousmap", - "domain": "set-theory", + "domain": "functions", "variation": "" }, { @@ -103,6 +103,10 @@ "name": "Set Theory", "URI": "set-theory-domain/setTheory.dsl" }, + "functions": { + "name": "Functions", + "URI": "set-theory-domain/functions.dsl" + }, "linear-algebra": { "name": "Linear Algebra", "URI": "linear-algebra-domain/linear-algebra.dsl" diff --git a/packages/examples/src/set-theory-domain/functions.dsl b/packages/examples/src/set-theory-domain/functions.dsl new file mode 100644 index 0000000000..c40ebb7e9f --- /dev/null +++ b/packages/examples/src/set-theory-domain/functions.dsl @@ -0,0 +1,32 @@ +type Set +type Point +type Map + +constructor Singleton(Point p) -> Set + +function Intersection(Set a, Set b) -> Set +function Union(Set a, Set b) -> Set +function Subtraction(Set a, Set b) -> Set +function CartesianProduct(Set a, Set b) -> Set +function Difference(Set a, Set b) -> Set +function Subset(Set a, Set b) -> Set +function AddPoint(Point p, Set s1) -> Set + +predicate Not(Prop p1) +predicate From(Map f, Set domain, Set codomain) +predicate Empty(Set s) +predicate Intersecting(Set s1, Set s2) +predicate IsSubset(Set s1, Set s2) +predicate Equal(Set s1, Set s2) +predicate PointIn(Set s, Point p) +predicate In(Point p, Set s) +predicate Injection(Map m) +predicate Surjection(Map m) +predicate Bijection(Map m) +predicate PairIn(Point, Point, Map) + +notation "A ⊂ B" ~ "IsSubset(A, B)" +notation "p ∈ A" ~ "PointIn(A, p)" +notation "p ∉ A" ~ "PointNotIn(A, p)" +notation "A ∩ B = ∅" ~ "Not(Intersecting(A, B))" +notation "f: A -> B" ~ "Map f; From(f, A, B)" diff --git a/packages/examples/src/set-theory-domain/setTheory.dsl b/packages/examples/src/set-theory-domain/setTheory.dsl index c40ebb7e9f..309f5ca535 100644 --- a/packages/examples/src/set-theory-domain/setTheory.dsl +++ b/packages/examples/src/set-theory-domain/setTheory.dsl @@ -1,32 +1,5 @@ type Set -type Point -type Map - -constructor Singleton(Point p) -> Set - -function Intersection(Set a, Set b) -> Set -function Union(Set a, Set b) -> Set -function Subtraction(Set a, Set b) -> Set -function CartesianProduct(Set a, Set b) -> Set -function Difference(Set a, Set b) -> Set -function Subset(Set a, Set b) -> Set -function AddPoint(Point p, Set s1) -> Set predicate Not(Prop p1) -predicate From(Map f, Set domain, Set codomain) -predicate Empty(Set s) predicate Intersecting(Set s1, Set s2) predicate IsSubset(Set s1, Set s2) -predicate Equal(Set s1, Set s2) -predicate PointIn(Set s, Point p) -predicate In(Point p, Set s) -predicate Injection(Map m) -predicate Surjection(Map m) -predicate Bijection(Map m) -predicate PairIn(Point, Point, Map) - -notation "A ⊂ B" ~ "IsSubset(A, B)" -notation "p ∈ A" ~ "PointIn(A, p)" -notation "p ∉ A" ~ "PointNotIn(A, p)" -notation "A ∩ B = ∅" ~ "Not(Intersecting(A, B))" -notation "f: A -> B" ~ "Map f; From(f, A, B)" diff --git a/packages/examples/src/set-theory-domain/venn.sty b/packages/examples/src/set-theory-domain/venn.sty index 85fba75380..81368e321b 100644 --- a/packages/examples/src/set-theory-domain/venn.sty +++ b/packages/examples/src/set-theory-domain/venn.sty @@ -4,74 +4,36 @@ canvas { } forall Set x { - x.icon = Circle { - strokeWidth : 0.0 - } + x.icon = Circle { + strokeWidth : 0 + } - x.text = Equation { - string : x.label - fontSize: "25px" - } + x.text = Equation { + string : x.label + fontSize : "25px" + } - ensure contains(x.icon, x.text) - encourage sameCenter(x.text, x.icon) - x.textLayering = x.text above x.icon + ensure contains(x.icon, x.text) + encourage sameCenter(x.text, x.icon) + x.textLayering = x.text above x.icon } forall Set x; Set y where IsSubset(x, y) { - - ensure smallerThan(x.icon, y.icon) - ensure disjoint(y.text, x.icon, 10.0) - ensure contains(y.icon, x.icon, 5.0) - x.icon above y.icon + ensure smallerThan(x.icon, y.icon) + ensure disjoint(y.text, x.icon, 10) + ensure contains(y.icon, x.icon, 5) + x.icon above y.icon } --- TODO: Fix that the resample hack breaks on switching examples since it saves the cached functions... --- TOOD: Also breaks if you resample without generating the function on first sample. Clearly this should be part of the state - ---- - forall Set x; Set y where Not(Intersecting(x, y)) { - ensure disjoint(x.icon, y.icon) + ensure disjoint(x.icon, y.icon) } --- --------- NEW - - forall Set x; Set y where Intersecting(x, y) { - ensure overlapping(x.icon, y.icon) - ensure disjoint(y.text, x.icon) - ensure disjoint(x.text, y.icon) -} - -forall Point p { - p.offset = 20.0 - p.icon = Circle { - strokeWidth : 0.0 - fillColor : rgba(0.0, 0.0, 0.0, 1.0) - r : 3.0 - } - - p.text = Equation { - string : p.label - center : p.icon.center + (p.offset, p.offset) - } - - p.textLayering = p.text above p.icon -} - -Point p -with Set A -where PointIn(A, p) { - ensure contains(A.icon, p.icon, 0.3 * A.icon.r) - p.layering = p.icon above A.icon -} - -Point p -with Set A -where Not(PointIn(A, p)) { - ensure disjoint(A.icon, p.icon) + ensure overlapping(x.icon, y.icon) + ensure disjoint(y.text, x.icon) + ensure disjoint(x.text, y.icon) }