<div class="notebook"> <div class="nb-cell markdown" name="md1"> # Triangle Since it has been a few years I started working on my [music notation grammar](https://github.com/kwon-young/music), I thought about how I could explain what I am doing to other peoples. I believe that for anyone else than me, some of the prolog code in that repository might be quite hard to understand. That's why I thought of a simplified example that could explain some of the logic and design decision behind the project. This post is only part 1 of a series I plan to do on the subject. ## Introduction So first, here is what we want to do. The basic idea of the project is to write a program that relates graphical primitives with high level semantic structure. Let's take concrete examples of program that do exactly that: * programs like LaTeX, MuseScore or even Word take a high level description of a document and can produce a graphical representation of the document consisting of a few graphical primitives like glyphes, segment, etc. * programs like OCRs can take an image of a document and can recognize the text inside of the document image in order to produce a high level representation of the document. Although OCRs takes as input an image of a document instead of graphical primitives, most of them will first recognize a small number of graphical primitives like letters or words and then reconstruct high level semantic structures like paragraph, titles etc. * My own project linked above target is to write a graphical grammar of the common music notation. This notation is used to typeset music score since the early of 18th century and is a very complex set of rules specifying the use and placement of music symbols to transcribe music on paper. Therefore, the goal I have for this project is to be able to write a prolog program that relates music scores in the MEI format (a format akin to MusicXML) with a set of graphical primitives like music symbols and segments. Music notation is a very complicated beast so we will simplify things a lot in this first post and try something simpler: middle school geometry! ## Basics Let's start with a much more basic program. A program that relates basic 2D euclidean geometrical figures with their graphical primitives. So, for example, let's take the most basic geometric figure: the triangle. And for graphical primitives, let's limit ourselves to points and segments. We will use prolog compound terms to manipulate these data structures: * A 2D point with coordinates X, Y: `point(X, Y)` * A segment, which is a pair of points Start and End: `seg(Start, End)` * A triangle, which can be represented as three points: `triangle(A, B, C)` The graphical representation of a triangle ABC is done by drawing three segments: * one from point A to point B * one from point B to point C * one from point C to point A </div> <div class="nb-cell html" name="htm1"> <svg style="overflow: hidden; display: block;" width="400" height="400"><defs><filter id="jxgbox_f1" width="300%" height="300%" filterUnits="userSpaceOnUse"><feOffset in="SourceGraphic" result="offOut" dx="5" dy="5"></feOffset><feColorMatrix in="offOut" result="colorOut" type="matrix" values="0.1 0 0 0 0 0 0.1 0 0 0 0 0 0.1 0 0 0 0 0 1 0"></feColorMatrix><feGaussianBlur in="colorOut" result="blurOut" stdDeviation="3"></feGaussianBlur><feBlend in="SourceGraphic" in2="blurOut" mode="normal"></feBlend></filter><marker id="jxgbox_jxgBoard1L3TriangleEnd1" style="position: absolute;" stroke="#666666" stroke-opacity="1" fill="#666666" fill-opacity="1" stroke-width="0" orient="auto" markerUnits="strokeWidth" refY="5" refX="0.05" viewBox="0 0 10 10" markerHeight="8" markerWidth="8" display="inherit"><path d="M 0,0 L 10,5 L 0,10 z"></path></marker><marker id="jxgbox_jxgBoard1L13TriangleEnd1" style="position: absolute;" stroke="#666666" stroke-opacity="1" fill="#666666" fill-opacity="1" stroke-width="0" orient="auto" markerUnits="strokeWidth" refY="5" refX="0.05" viewBox="0 0 10 10" markerHeight="8" markerWidth="8" display="inherit"><path d="M 0,0 L 10,5 L 0,10 z"></path></marker></defs><g><text id="jxgbox_licenseText" style="font-family:Arial,Helvetica,sans-serif; font-size:12px; fill:#356AA0; opacity:0.3;" x="20px" y="14px">JSXGraph v1.8.0 Copyright (C) see https://jsxgraph.org</text></g><g></g><g><line id="jxgbox_jxgBoard1L3" style="position: absolute; transition: fill 100ms, fill-opacity 100ms, stroke 100ms, stroke-opacity 100ms, stroke-width 100ms; visibility: inherit;" clip-path="view-box inset(0px 0px 0px 0px)" stroke="#666666" stroke-opacity="1" stroke-width="1px" pointer-events="visibleStroke" fill-opacity="0" tabindex="null" marker-end="url(#jxgbox_jxgBoard1L3TriangleEnd1)" x1="4" y1="200.00000000000003" x2="388" y2="200.00000000000003" stroke-linecap="butt" display="inline"></line><path id="jxgbox_jxgBoard1L3_ticks_1" style="position: absolute; visibility: inherit;" stroke-linecap="round" stroke-linejoin="round" fill-rule="evenodd" stroke="#666666" fill="none" stroke-opacity="0.25" stroke-width="1" d=" M 211.42857142857144 200 L 211.42857142857144 200 L 211.42857142857144 205 M 222.85714285714286 200 L 222.85714285714286 200 L 222.85714285714286 205 M 234.28571428571428 200 L 234.28571428571428 200 L 234.28571428571428 205 M 245.71428571428572 200 L 245.71428571428572 200 L 245.71428571428572 205 M 257.14285714285717 0 L 257.14285714285717 400 M 268.57142857142856 200 L 268.57142857142856 200 L 268.57142857142856 205 M 280 200 L 280 200 L 280 205 M 291.42857142857144 200 L 291.42857142857144 200 L 291.42857142857144 205 M 302.85714285714283 200 L 302.85714285714283 200 L 302.85714285714283 205 M 314.2857142857143 0 L 314.2857142857143 400 M 325.7142857142857 200 L 325.7142857142857 200 L 325.7142857142857 205 M 337.1428571428571 200 L 337.1428571428571 200 L 337.1428571428571 205 M 348.57142857142856 200 L 348.57142857142856 200 L 348.57142857142856 205 M 360 200 L 360 200 L 360 205 M 371.42857142857144 0 L 371.42857142857144 400 M 382.8571428571429 200 L 382.8571428571429 200 L 382.8571428571429 205 M 188.57142857142856 200 L 188.57142857142856 200 L 188.57142857142856 205 M 177.14285714285714 200 L 177.14285714285714 200 L 177.14285714285714 205 M 165.71428571428572 200 L 165.71428571428572 200 L 165.71428571428572 205 M 154.28571428571428 200 L 154.28571428571428 200 L 154.28571428571428 205 M 142.85714285714286 0 L 142.85714285714286 400 M 131.42857142857144 200 L 131.42857142857144 200 L 131.42857142857144 205 M 120 200 L 120 200 L 120 205 M 108.57142857142857 200 L 108.57142857142857 200 L 108.57142857142857 205 M 97.14285714285715 200 L 97.14285714285715 200 L 97.14285714285715 205 M 85.71428571428572 0 L 85.71428571428572 400 M 74.28571428571429 200 L 74.28571428571429 200 L 74.28571428571429 205 M 62.85714285714286 200 L 62.85714285714286 200 L 62.85714285714286 205 M 51.428571428571416 200 L 51.428571428571416 200 L 51.428571428571416 205 M 39.99999999999997 200 L 39.99999999999997 200 L 39.99999999999997 205 M 28.571428571428527 0 L 28.571428571428527 400 M 17.14285714285711 200 L 17.14285714285711 200 L 17.14285714285711 205 M 5.714285714285666 200 L 5.714285714285666 200 L 5.714285714285666 205" clip-path="view-box inset(0px 0px 0px 0px)" display="inline"></path><line id="jxgbox_jxgBoard1L13" style="position: absolute; transition: fill 100ms, fill-opacity 100ms, stroke 100ms, stroke-opacity 100ms, stroke-width 100ms; visibility: inherit;" clip-path="view-box inset(0px 0px 0px 0px)" stroke="#666666" stroke-opacity="1" stroke-width="1px" pointer-events="visibleStroke" fill-opacity="0" tabindex="null" marker-end="url(#jxgbox_jxgBoard1L13TriangleEnd1)" x1="200.00000000000003" y1="396" x2="200.00000000000003" y2="12" stroke-linecap="butt" display="inline"></line><path id="jxgbox_jxgBoard1L13_ticks_1" style="position: absolute; visibility: inherit;" stroke-linecap="round" stroke-linejoin="round" fill-rule="evenodd" stroke="#666666" fill="none" stroke-opacity="0.25" stroke-width="1" d=" M 195 188.57142857142856 L 200 188.57142857142856 L 200 188.57142857142856 M 195 177.14285714285714 L 200 177.14285714285714 L 200 177.14285714285714 M 195 165.71428571428572 L 200 165.71428571428572 L 200 165.71428571428572 M 195 154.28571428571428 L 200 154.28571428571428 L 200 154.28571428571428 M 0 142.85714285714286 L 400 142.85714285714286 M 195 131.42857142857144 L 200 131.42857142857144 L 200 131.42857142857144 M 195 120 L 200 120 L 200 120 M 195 108.57142857142857 L 200 108.57142857142857 L 200 108.57142857142857 M 195 97.14285714285715 L 200 97.14285714285715 L 200 97.14285714285715 M 0 85.71428571428572 L 400 85.71428571428572 M 195 74.28571428571429 L 200 74.28571428571429 L 200 74.28571428571429 M 195 62.85714285714286 L 200 62.85714285714286 L 200 62.85714285714286 M 195 51.428571428571416 L 200 51.428571428571416 L 200 51.428571428571416 M 195 39.99999999999997 L 200 39.99999999999997 L 200 39.99999999999997 M 0 28.571428571428527 L 400 28.571428571428527 M 195 17.14285714285711 L 200 17.14285714285711 L 200 17.14285714285711 M 195 211.42857142857144 L 200 211.42857142857144 L 200 211.42857142857144 M 195 222.85714285714286 L 200 222.85714285714286 L 200 222.85714285714286 M 195 234.28571428571428 L 200 234.28571428571428 L 200 234.28571428571428 M 195 245.71428571428572 L 200 245.71428571428572 L 200 245.71428571428572 M 0 257.14285714285717 L 400 257.14285714285717 M 195 268.57142857142856 L 200 268.57142857142856 L 200 268.57142857142856 M 195 280 L 200 280 L 200 280 M 195 291.42857142857144 L 200 291.42857142857144 L 200 291.42857142857144 M 195 302.85714285714283 L 200 302.85714285714283 L 200 302.85714285714283 M 0 314.2857142857143 L 400 314.2857142857143 M 195 325.7142857142857 L 200 325.7142857142857 L 200 325.7142857142857 M 195 337.1428571428571 L 200 337.1428571428571 L 200 337.1428571428571 M 195 348.57142857142856 L 200 348.57142857142856 L 200 348.57142857142856 M 195 360 L 200 360 L 200 360 M 0 371.42857142857144 L 400 371.42857142857144 M 195 382.8571428571429 L 200 382.8571428571429 L 200 382.8571428571429 M 195 394.28571428571433 L 200 394.28571428571433 L 200 394.28571428571433" clip-path="view-box inset(0px 0px 0px 0px)" display="inline"></path></g><g></g><g></g><g></g><g></g><g><line id="jxgbox_jxgBoard1L27" style="position: absolute; transition: fill 100ms, fill-opacity 100ms, stroke 100ms, stroke-opacity 100ms, stroke-width 100ms; visibility: inherit;" clip-path="view-box inset(0px 0px 0px 0px)" stroke="#000000" stroke-opacity="1" stroke-width="2px" pointer-events="visibleStroke" fill-opacity="0" tabindex="0" x1="114.28571428571428" y1="114.28571428571428" x2="285.7142857142857" y2="114.28571428571428" stroke-linecap="butt" display="inline"></line><line id="jxgbox_jxgBoard1L28" style="position: absolute; transition: fill 100ms, fill-opacity 100ms, stroke 100ms, stroke-opacity 100ms, stroke-width 100ms; visibility: inherit;" clip-path="view-box inset(0px 0px 0px 0px)" stroke="#000000" stroke-opacity="1" stroke-width="2px" pointer-events="visibleStroke" fill-opacity="0" tabindex="0" x1="285.7142857142857" y1="114.28571428571428" x2="257.14285714285717" y2="257.14285714285717" stroke-linecap="butt" display="inline"></line><line id="jxgbox_jxgBoard1L29" style="position: absolute; transition: fill 100ms, fill-opacity 100ms, stroke 100ms, stroke-opacity 100ms, stroke-width 100ms; visibility: inherit;" clip-path="view-box inset(0px 0px 0px 0px)" stroke="#000000" stroke-opacity="1" stroke-width="2px" pointer-events="visibleStroke" fill-opacity="0" tabindex="0" x1="257.14285714285717" y1="257.14285714285717" x2="114.28571428571428" y2="114.28571428571428" stroke-linecap="butt" display="inline"></line></g><g></g><g><ellipse id="jxgbox_jxgBoard1P1" style="position: absolute; transition: fill 100ms, fill-opacity 100ms, stroke 100ms, stroke-opacity 100ms, stroke-width 100ms; visibility: hidden;" clip-path="view-box inset(0px 0px 0px 0px)" stroke="#D55E00" stroke-opacity="1" stroke-width="2px" fill="#D55E00" pointer-events="visiblePainted" fill-opacity="1" tabindex="null" cx="200" cy="200" rx="4" ry="4" display="none"></ellipse><ellipse id="jxgbox_jxgBoard1P2" style="position: absolute; transition: fill 100ms, fill-opacity 100ms, stroke 100ms, stroke-opacity 100ms, stroke-width 100ms; visibility: hidden;" clip-path="view-box inset(0px 0px 0px 0px)" stroke="#D55E00" stroke-opacity="1" stroke-width="2px" fill="#D55E00" pointer-events="visiblePainted" fill-opacity="1" tabindex="null" cx="228.57142857142858" cy="200" rx="4" ry="4" display="none"></ellipse><text id="jxgbox_jxgBoard1L3_ticks_15Label1" style="position: absolute; white-space: nowrap; font-family: Arial, Helvetica, Geneva, sans-serif; font-size: 12px; transition: color 100ms, opacity 100ms; visibility: inherit;" tabindex="null" clip-path="view-box inset(0px 0px 0px 0px)" fill="#000000" pointer-events="visiblePainted" fill-opacity="1" class="JXGtext" x="257.14285714285717px" text-anchor="middle" y="203px" dy="1.6ex" dominant-baseline="auto" display="inline" stroke-width="2px">2</text><text id="jxgbox_jxgBoard1L3_ticks_110Label2" style="position: absolute; white-space: nowrap; font-family: Arial, Helvetica, Geneva, sans-serif; font-size: 12px; transition: color 100ms, opacity 100ms; visibility: inherit;" tabindex="null" clip-path="view-box inset(0px 0px 0px 0px)" fill="#000000" pointer-events="visiblePainted" fill-opacity="1" class="JXGtext" x="314.2857142857143px" text-anchor="middle" y="203px" dy="1.6ex" dominant-baseline="auto" display="inline">4</text><text id="jxgbox_jxgBoard1L3_ticks_115Label3" style="position: absolute; white-space: nowrap; font-family: Arial, Helvetica, Geneva, sans-serif; font-size: 12px; transition: color 100ms, opacity 100ms; visibility: inherit;" tabindex="null" clip-path="view-box inset(0px 0px 0px 0px)" fill="#000000" pointer-events="visiblePainted" fill-opacity="1" class="JXGtext" x="371.42857142857144px" text-anchor="middle" y="203px" dy="1.6ex" dominant-baseline="auto" display="inline">6</text><text id="jxgbox_jxgBoard1L3_ticks_121Label4" style="position: absolute; white-space: nowrap; font-family: Arial, Helvetica, Geneva, sans-serif; font-size: 12px; transition: color 100ms, opacity 100ms; visibility: inherit;" tabindex="null" clip-path="view-box inset(0px 0px 0px 0px)" fill="#000000" pointer-events="visiblePainted" fill-opacity="1" class="JXGtext" x="142.85714285714286px" text-anchor="middle" y="203px" dy="1.6ex" dominant-baseline="auto" display="inline">−2</text><text id="jxgbox_jxgBoard1L3_ticks_126Label5" style="position: absolute; white-space: nowrap; font-family: Arial, Helvetica, Geneva, sans-serif; font-size: 12px; transition: color 100ms, opacity 100ms; visibility: inherit;" tabindex="null" clip-path="view-box inset(0px 0px 0px 0px)" fill="#000000" pointer-events="visiblePainted" fill-opacity="1" class="JXGtext" x="85.71428571428572px" text-anchor="middle" y="203px" dy="1.6ex" dominant-baseline="auto" display="inline">−4</text><text id="jxgbox_jxgBoard1L3_ticks_131Label6" style="position: absolute; white-space: nowrap; font-family: Arial, Helvetica, Geneva, sans-serif; font-size: 12px; transition: color 100ms, opacity 100ms; visibility: inherit;" tabindex="null" clip-path="view-box inset(0px 0px 0px 0px)" fill="#000000" pointer-events="visiblePainted" fill-opacity="1" class="JXGtext" x="28.571428571428527px" text-anchor="middle" y="203px" dy="1.6ex" dominant-baseline="auto" display="inline">−6</text><ellipse id="jxgbox_jxgBoard1P11" style="position: absolute; transition: fill 100ms, fill-opacity 100ms, stroke 100ms, stroke-opacity 100ms, stroke-width 100ms; visibility: hidden;" clip-path="view-box inset(0px 0px 0px 0px)" stroke="#D55E00" stroke-opacity="1" stroke-width="2px" fill="#D55E00" pointer-events="visiblePainted" fill-opacity="1" tabindex="null" cx="200" cy="200" rx="4" ry="4" display="none"></ellipse><ellipse id="jxgbox_jxgBoard1P12" style="position: absolute; transition: fill 100ms, fill-opacity 100ms, stroke 100ms, stroke-opacity 100ms, stroke-width 100ms; visibility: hidden;" clip-path="view-box inset(0px 0px 0px 0px)" stroke="#D55E00" stroke-opacity="1" stroke-width="2px" fill="#D55E00" pointer-events="visiblePainted" fill-opacity="1" tabindex="null" cx="200" cy="171.42857142857142" rx="4" ry="4" display="none"></ellipse><text id="jxgbox_jxgBoard1L13_ticks_15Label1" style="position: absolute; white-space: nowrap; font-family: Arial, Helvetica, Geneva, sans-serif; font-size: 12px; transition: color 100ms, opacity 100ms; visibility: inherit;" tabindex="null" clip-path="view-box inset(0px 0px 0px 0px)" fill="#000000" pointer-events="visiblePainted" fill-opacity="1" class="JXGtext" x="194px" text-anchor="end" y="142.85714285714286px" dy="0.6ex" dominant-baseline="auto" display="inline">2</text><text id="jxgbox_jxgBoard1L13_ticks_110Label2" style="position: absolute; white-space: nowrap; font-family: Arial, Helvetica, Geneva, sans-serif; font-size: 12px; transition: color 100ms, opacity 100ms; visibility: inherit;" tabindex="null" clip-path="view-box inset(0px 0px 0px 0px)" fill="#000000" pointer-events="visiblePainted" fill-opacity="1" class="JXGtext" x="194px" text-anchor="end" y="85.71428571428572px" dy="0.6ex" dominant-baseline="auto" display="inline">4</text><text id="jxgbox_jxgBoard1L13_ticks_115Label3" style="position: absolute; white-space: nowrap; font-family: Arial, Helvetica, Geneva, sans-serif; font-size: 12px; transition: color 100ms, opacity 100ms; visibility: inherit;" tabindex="null" clip-path="view-box inset(0px 0px 0px 0px)" fill="#000000" pointer-events="visiblePainted" fill-opacity="1" class="JXGtext" x="194px" text-anchor="end" y="28.571428571428527px" dy="0.6ex" dominant-baseline="auto" display="inline" stroke-width="2px">6</text><text id="jxgbox_jxgBoard1L13_ticks_121Label4" style="position: absolute; white-space: nowrap; font-family: Arial, Helvetica, Geneva, sans-serif; font-size: 12px; transition: color 100ms, opacity 100ms; visibility: inherit;" tabindex="null" clip-path="view-box inset(0px 0px 0px 0px)" fill="#000000" pointer-events="visiblePainted" fill-opacity="1" class="JXGtext" x="194px" text-anchor="end" y="257.14285714285717px" dy="0.6ex" dominant-baseline="auto" display="inline">−2</text><text id="jxgbox_jxgBoard1L13_ticks_126Label5" style="position: absolute; white-space: nowrap; font-family: Arial, Helvetica, Geneva, sans-serif; font-size: 12px; transition: color 100ms, opacity 100ms; visibility: inherit;" tabindex="null" clip-path="view-box inset(0px 0px 0px 0px)" fill="#000000" pointer-events="visiblePainted" fill-opacity="1" class="JXGtext" x="194px" text-anchor="end" y="314.2857142857143px" dy="0.6ex" dominant-baseline="auto" display="inline">−4</text><text id="jxgbox_jxgBoard1L13_ticks_131Label6" style="position: absolute; white-space: nowrap; font-family: Arial, Helvetica, Geneva, sans-serif; font-size: 12px; transition: color 100ms, opacity 100ms; visibility: inherit;" tabindex="null" clip-path="view-box inset(0px 0px 0px 0px)" fill="#000000" pointer-events="visiblePainted" fill-opacity="1" class="JXGtext" x="194px" text-anchor="end" y="371.42857142857144px" dy="0.6ex" dominant-baseline="auto" display="inline">−6</text><ellipse id="jxgbox_jxgBoard1P21" style="position: absolute; transition: fill 100ms, fill-opacity 100ms, stroke 100ms, stroke-opacity 100ms, stroke-width 100ms, width 100ms, height 100ms, rx 100ms, ry 100ms; visibility: inherit;" clip-path="view-box inset(0px 0px 0px 0px)" stroke="#D55E00" stroke-opacity="1" stroke-width="2px" fill="#D55E00" pointer-events="visiblePainted" fill-opacity="1" tabindex="0" cx="114.28571428571428" cy="114.28571428571428" rx="4" ry="4" display="inline"></ellipse><ellipse id="jxgbox_jxgBoard1P23" style="position: absolute; transition: fill 100ms, fill-opacity 100ms, stroke 100ms, stroke-opacity 100ms, stroke-width 100ms, width 100ms, height 100ms, rx 100ms, ry 100ms; visibility: inherit;" clip-path="view-box inset(0px 0px 0px 0px)" stroke="#D55E00" stroke-opacity="1" stroke-width="2px" fill="#D55E00" pointer-events="visiblePainted" fill-opacity="1" tabindex="0" cx="285.7142857142857" cy="114.28571428571428" rx="4" ry="4" display="inline"></ellipse><ellipse id="jxgbox_jxgBoard1P25" style="position: absolute; transition: fill 100ms, fill-opacity 100ms, stroke 100ms, stroke-opacity 100ms, stroke-width 100ms, width 100ms, height 100ms, rx 100ms, ry 100ms; visibility: inherit;" clip-path="view-box inset(0px 0px 0px 0px)" stroke="#D55E00" stroke-opacity="1" stroke-width="2px" fill="#D55E00" pointer-events="visiblePainted" fill-opacity="1" tabindex="0" cx="257.14285714285717" cy="257.14285714285717" rx="4" ry="4" display="inline"></ellipse></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><foreignObject display="none" x="0" y="0" width="100%" height="100%" id="jxgbox_foreignObj"></foreignObject></svg> </div> <div class="nb-cell markdown" name="md2"> Let's translate this to prolog: </div> <div class="nb-cell program" name="p1"> triangle_cond(triangle(A, B, C), seg(A, B), seg(B, C), seg(C, A)). </div> <div class="nb-cell markdown" name="md3"> > **NOTE**: > A little note about code convention in this post: predicates that relates geometrical figures with graphical primitives will always end in `_cond` Interestingly, this knowledge can be reduced to a single prolog fact. One thing I love about this line of code is that prolog gives us the ability to specify only the knowledge we are interested in. Notice how we didn't had to define how a point is represented here. We could use this code with 2D points, but also 3D or in any number of dimension. We could also use atoms as points, like `'A'`, if we are doing some abstract geometry application. Finally, by using variables instead of ground term for points, we can represent any triangle in the infinite set of all ground triangles! Let's move on to the next step, which is to relate this triangle with a list of graphical primitives. Since, we want to compose and manipulate multiple geometrical figures with a single list of graphical primitives, we will use the prolog DCG notation which is well suited for this use case: </div> <div class="nb-cell program" name="p2"> triangle_cond(triangle(A, B, C), seg(A, B), seg(B, C), seg(C, A)). triangle_dcg(Triangle) --> { triangle_cond(Triangle, Seg1, Seg2, Seg3) }, [Seg1, Seg2, Seg3]. </div> <div class="nb-cell markdown" name="md4"> > **NOTE**: > A little note about code convention in this post: DCG predicates that could be confused with plain compound terms will always end in `_dcg` For those that are not familiar with the DCG notation, [here is a good primer](https://www.metalevel.at/prolog/dcg). If you are already familiar with DCGs, you can just skip the paragraph below. To just explain briefly, a DCG predicate can be used to describe a list. It has this weird neck `-->` and the arity is annotated with a double shlash like this: `triangle_dcg//1`. The reason for the double slash is because, internally, prolog translate DCG predicates by adding 2 additional parameters to the head of the clause. This two additional predicates represent a difference list, which is threaded through inside the body of the DCG predicate. If you don't know what is a difference list, it's okay, we won't need it here, but it is a very useful prolog pattern to learn. The braces `{}` around `triangle_cond/4` is there to tell prolog that `triangle_cond/4` is not a DCG predicate but a plain prolog predicate to call normally. > **NOTE**: > A little remark on this idea to relates a list of graphical primitives with high level construct with a grammar. > I believe this type of idea was very popular in the 90s. > I was exposed to it through my PhD adviser, Bertrand Coüasnon, which implemented this idea in his thesis with the [DMOS system](https://ieeexplore.ieee.org/abstract/document/953786) in a lambda prolog dialect with a grammar formalism called EPF. Let's see how we can use this DCG in the prolog top level by posting the most general query: </div> <div class="nb-cell query" name="q1"> phrase(triangle_dcg(Triangle), L, R). </div> <div class="nb-cell markdown" name="md6"> </div> <div class="nb-cell markdown" name="md5"> We can see that prolog has build us a `triangle` compound term together with 3 segments with the correct set of points. As you can see the pair `L` and `R` is a difference list where `R` is the tail of the list `L`. This allows us to compose multiple DCG predicates as we will see later. If we want to close the list, we can just call the `phrase/2` predicate without the `R` argument. We can already do a lot of different things with this predicate. We can _draw_ a specific triangle: </div> <div class="nb-cell query" name="q2"> phrase(triangle_dcg(triangle('A', 'B', 'C')), L, R). </div> <div class="nb-cell markdown" name="md7"> We can recognize a triangle from a set of segments: </div> <div class="nb-cell query" name="q3"> phrase(triangle_dcg(Triangle), [seg('A', 'B'), seg('B', 'C'), seg('C', 'A')]). </div> <div class="nb-cell markdown" name="md8"> Or reject sets of graphical primitives that do not form a triangle: </div> <div class="nb-cell query" name="q4"> phrase(triangle_dcg(Triangle), [seg('A', 'B'), seg('B', 'C')]). </div> <div class="nb-cell markdown" name="md9"> Or test that a specific triangle is related to a set of segments: </div> <div class="nb-cell query" name="q5"> phrase(triangle_dcg(triangle('A', 'B', 'C')), [seg('A', 'B'), seg('B', 'C'), seg('C', 'A')]). </div> <div class="nb-cell query" name="q6"> phrase(triangle_dcg(triangle('A', 'B', 'C')), [seg('A', 'B'), seg('B', 'C')]). </div> <div class="nb-cell markdown" name="md10"> ## The ordering problem Unfortunately, our DCG clause `triangle_dcg` is too strict about the possible segment lists we can use to build a triangle. For example, the following query fails: </div> <div class="nb-cell query" name="q7"> phrase(triangle_dcg(Triangle), [seg('A', 'B'), seg('C', 'A'), seg('B', 'C')]). </div> <div class="nb-cell markdown" name="md11"> There is no reason why segment BC should be before segment CA. This is especially true when manipulating graphical primitives. There is no evident ordering of the primitives, and we cannot assume a specific ordering when defining our grammar. In order to fix this, we will define a predicate `term` which job will be to find and consume a given graphical primitive: </div> <div class="nb-cell program" data-background="true" name="p3"> term(X) --> [X]. term(X), [CurX] --> [CurX], term(X). </div> <div class="nb-cell markdown" name="md12"> The first clause tries to match the given primitive `X`. If it fails, we go to the second clause and skip the current element `CurX` and try to match the next element by doing a recursive call. Coincidentally, the standard `select/3` predicate can be used instead transparently and will do exactly the same thing. Now, let's rewrite our `triangle_dcg//1` DCG predicate to use `term//1`: </div> <div class="nb-cell program" name="p4"> triangle_cond(triangle(A, B, C), seg(A, B), seg(B, C), seg(C, A)). triangle_dcg(Triangle) --> { triangle_cond(Triangle, Seg1, Seg2, Seg3) }, term(Seg1), term(Seg2), term(Seg3). </div> <div class="nb-cell markdown" name="md13"> Let's retry our most general query: </div> <div class="nb-cell query" name="q8"> phrase(triangle_dcg(Triangle), L, R). </div> <div class="nb-cell markdown" name="md14"> As you can see, the first solution is the same as our previous result but now, we get multiple other solutions. In fact, we get an infinity many more solutions. From this little change in the code, we went from a single deterministic solution to infinitely many. This can be good, because it will allow us to get more solutions as we will see below. This could be also bad, since the complexity of the code has risen dramatically and it could be quite easy to be trapped into an infinite recursion loop. But we will worry about this later. One new characteristic of the second results is that the grammar is now resistant to noise, i.e. non relevant graphical primitives. In the query above, we enumerate the solutions in a depth first manner, lengthening the list to infinity by searching for the last segment deeper and deeper. Instead, a breadth first search strategy could give us some more interesting solutions. This can be done by using the `length/2` predicate before the call to `phrase/3`. Let's see all solutions with a list `L` of length 3: </div> <div class="nb-cell query" name="q9"> length(L, 3), phrase(triangle_dcg(Triangle), L, R). </div> <div class="nb-cell markdown" name="md15"> If you count them, that's 6 solutions which are all the permutations of list of length 3 with no replacements. So, we can post our previous wrongly failing query it should now work: </div> <div class="nb-cell query" name="q10"> phrase(triangle_dcg(Triangle), [seg('A', 'B'), seg('C', 'A'), seg('B', 'C')]). </div> <div class="nb-cell markdown" name="md16"> Great, it even gave us all the different representation of the ABC triangle! Let's move on to another type of triangle, the equilateral triangle. ## The equilateral triangle The property of an equilateral triangle is that all three sides have the same length. Let's define the compound term for such a triangle: * an equilateral triangle ABC: `equilateral_triangle(A, B, C)` And let's extend the `triangle_cond/4` predicate to relate such a triangle with its three sides: </div> <div class="nb-cell program" name="p5"> triangle_cond(triangle(A, B, C), seg(A, B), seg(B, C), seg(C, A)). triangle_cond(equilateral_triangle(A, B, C), Seg1, Seg2, Seg3) :- triangle_cond(triangle(A, B, C), Seg1, Seg2, Seg3), seg_length(Seg1, Len), seg_length(Seg2, Len), seg_length(Seg3, Len). </div> <div class="nb-cell markdown" name="md17"> We first reuse the `triangle_cond/4` first clause since an equilateral triangle is itself a triangle. This will correctly setup the three segments with the triangle points. Next, we unify all three segment length to the same length `Len` using the yet to be defined `seg_length/2` predicate. To compute a segment length, we will use the well known Pythagoras theorem: </div> <div class="nb-cell program" name="p6"> seg_length(seg(point(X1, Y1), point(X2, Y2)), Len) :- Len is sqrt((X2 - X1)**2 + (Y2 - Y1)**2). triangle_cond(triangle(A, B, C), seg(A, B), seg(B, C), seg(C, A)). triangle_cond(equilateral_triangle(A, B, C), Seg1, Seg2, Seg3) :- triangle_cond(triangle(A, B, C), Seg1, Seg2, Seg3), seg_length(Seg1, Len), seg_length(Seg2, Len), seg_length(Seg3, Len). triangle_dcg(Triangle) --> { triangle_cond(Triangle, Seg1, Seg2, Seg3) }, term(Seg1), term(Seg2), term(Seg3). </div> <div class="nb-cell markdown" name="md18"> This predicate will only work on 2D segments, but we have to start from somewhere. Let's try our new equilateral triangle predicate with the most general query: </div> <div class="nb-cell query" name="q11"> phrase(triangle_dcg(equilateral_triangle(A, B, C)), L, R). </div> <div class="nb-cell markdown" name="md19"> Well... As you probably would have predicted, reality hit us on the face with the fact that prolog arithmetic is not pure. It will only work in a single direction. So one solution would be to try and modify the order of predicates in the DCG like this: </div> <div class="nb-cell program" name="p7"> triangle_post_dcg(Triangle) --> term(Seg1), term(Seg2), term(Seg3), { triangle_cond(Triangle, Seg1, Seg2, Seg3) }. </div> <div class="nb-cell markdown" name="md20"> In this case, `triangle_cond/4` would become a post condition instead of a precondition. However, the search strategy of the grammar would become a kind of generate and test strategy, which would be intractable with only a small number of graphical primitives. In situation like this, modern prolog has come up with a solution called **Constraints**. ## Constraints programming The idea of constraint programming is to specify as early as possible a maximum of knowledge about the problem at hand. This knowledge can be used to prune the search tree and find results with less computation. A very well known type of constraint programming is constraints over integers with libraries like [`clpfd`](https://www.swi-prolog.org/pldoc/man?section=clpfd). A very useful byproduct of such library is that it introduce pure arithmetic, with arithmetic operations that can be used in any direction. That's perfect for what we want to do! However, our problem domain is not over integers but over reals. Coordinates or segment lengths are continuous values in the euclidean 2D space. From my current knowledge about the swi-prolog landscape, I only know of 2 libraries that does constraint programming over reals: * [`clpqr`](https://www.swi-prolog.org/pldoc/man?section=clpqr) installed by default in the swi-prolog * [`clpBNR`](https://github.com/ridgeworks/clpBNR) which you can install as a pack in swi-prolog `clpqr` idea is to delay the evaluation of an operation until all input arguments are ground. This make the constraint propagation very weak, which is a big problem for us. On the other hand, `clpBNR` takes another approach, closer to the one used by `clpfd` by using interval arithmetic. In this case, constraint propagation are much stronger at the expense of results precision. If you want to know more, you should have a read at the [Guide to `clpBNR`](https://ridgeworks.github.io/clpBNR/CLP_BNR_Guide/CLP_BNR_Guide.html). To come back to our grammar, `cplBNR` will allow us to use real arithmetic in all directions. This will come at the expense of efficiency and somewhat imprecise results. Let's try to rework our `seg_length/2` predicate to use `clpBNR`. </div> <div class="nb-cell program" data-background="true" name="p11"> :- use_module(library(clpBNR)). </div> <div class="nb-cell program" name="p8"> seg_length(seg(point(X1, Y1), point(X2, Y2)), Len) :- { Len == sqrt((X2 - X1)**2 + (Y2 - Y1)**2) }. </div> <div class="nb-cell markdown" name="md21"> First of all, the braces `{}` wrapping the mathematical expression is to indicate that we are using `clpBNR` arithmetic. Next, we have replace the `is` operator with `==` equality operator. With pure arithmetic, it does not make sense anymore to have an evaluation predicate since there are no predefined direction on how to evaluate things. All you can do is to use comparison predicate such as equality `==` to say that both sides should be equal. Let's try our new predicate with the most general query: </div> <div class="nb-cell query" name="q12"> seg_length(Seg, Len). </div> <div class="nb-cell markdown" name="md22"> The last four lines tells us that the segment coordinates can take real values going from -infinity to infinity. If we look at the `Len` variable, we can already see some constraint propagation happening! `clpBNR` correctly deduced that `Len` is related to the output of the square root function which can only be positive... Well, not in math, but in computers, the `sqrt` often only returns the non negative square root only. In our application of computing the length of a segment, this is exactly what we want, since a negative segment length would not make sense. Let's try some more queries, by adding some more information about the segment first point and the segment length _after_ the call to `seg_length/2` to see some more constraint propagations: </div> <div class="nb-cell query" name="q13"> seg_length(Seg, Len), Seg = seg(P1, P2), P1 = point(0, 0), Len=1. </div> <div class="nb-cell markdown" name="md23"> With this, `clpBNR` has correctly deduced that the end point of `Seg` can by anywhere between `(-1, 1)`. In fact, this second point has to be on the _circle_ of center (0, 0) and radius 1: </div> <div class="nb-cell html" name="htm2"> <svg style="overflow: hidden; display: block;" width="400" height="400"><defs><filter id="jxgbox2_f1" width="300%" height="300%" filterUnits="userSpaceOnUse"><feOffset in="SourceGraphic" result="offOut" dx="5" dy="5"></feOffset><feColorMatrix in="offOut" result="colorOut" type="matrix" values="0.1 0 0 0 0 0 0.1 0 0 0 0 0 0.1 0 0 0 0 0 1 0"></feColorMatrix><feGaussianBlur in="colorOut" result="blurOut" stdDeviation="3"></feGaussianBlur><feBlend in="SourceGraphic" in2="blurOut" mode="normal"></feBlend></filter><marker id="jxgbox2_jxgBoard40650L3TriangleEnd1" style="position: absolute;" stroke="#666666" stroke-opacity="1" fill="#666666" fill-opacity="1" stroke-width="0" orient="auto" markerUnits="strokeWidth" refY="5" refX="0.05" viewBox="0 0 10 10" markerHeight="8" markerWidth="8" display="inherit"><path d="M 0,0 L 10,5 L 0,10 z"></path></marker><marker id="jxgbox2_jxgBoard40650L13TriangleEnd1" style="position: absolute;" stroke="#666666" stroke-opacity="1" fill="#666666" fill-opacity="1" stroke-width="0" orient="auto" markerUnits="strokeWidth" refY="5" refX="0.05" viewBox="0 0 10 10" markerHeight="8" markerWidth="8" display="inherit"><path d="M 0,0 L 10,5 L 0,10 z"></path></marker></defs><g><text id="jxgbox2_licenseText" style="font-family:Arial,Helvetica,sans-serif; font-size:12px; fill:#356AA0; opacity:0.3;" x="20px" y="14px">JSXGraph v1.8.0 Copyright (C) see https://jsxgraph.org</text></g><g></g><g><line id="jxgbox2_jxgBoard40650L3" style="position: absolute; transition: fill 100ms, fill-opacity 100ms, stroke 100ms, stroke-opacity 100ms, stroke-width 100ms; visibility: inherit;" clip-path="view-box inset(0px 0px 0px 0px)" stroke="#666666" stroke-opacity="1" stroke-width="1px" pointer-events="visibleStroke" fill-opacity="0" tabindex="null" marker-end="url(#jxgbox2_jxgBoard40650L3TriangleEnd1)" x1="4" y1="200" x2="388" y2="200" stroke-linecap="butt" display="inline"></line><path id="jxgbox2_jxgBoard40650L3_ticks_1" style="position: absolute; visibility: inherit;" stroke-linecap="round" stroke-linejoin="round" fill-rule="evenodd" stroke="#666666" fill="none" stroke-opacity="0.25" stroke-width="1" d=" M 210 200 L 210 200 L 210 205 M 220 200 L 220 200 L 220 205 M 230 200 L 230 200 L 230 205 M 240 200 L 240 200 L 240 205 M 250 0 L 250 400 M 260 200 L 260 200 L 260 205 M 270 200 L 270 200 L 270 205 M 280 200 L 280 200 L 280 205 M 290 200 L 290 200 L 290 205 M 300 0 L 300 400 M 310 200 L 310 200 L 310 205 M 320 200 L 320 200 L 320 205 M 330 200 L 330 200 L 330 205 M 340 200 L 340 200 L 340 205 M 350 0 L 350 400 M 360 200 L 360 200 L 360 205 M 370 200 L 370 200 L 370 205 M 380.00000000000006 200 L 380.00000000000006 200 L 380.00000000000006 205 M 190 200 L 190 200 L 190 205 M 180 200 L 180 200 L 180 205 M 170 200 L 170 200 L 170 205 M 160 200 L 160 200 L 160 205 M 150 0 L 150 400 M 140 200 L 140 200 L 140 205 M 130 200 L 130 200 L 130 205 M 120 200 L 120 200 L 120 205 M 110.00000000000001 200 L 110.00000000000001 200 L 110.00000000000001 205 M 100.00000000000001 0 L 100.00000000000001 400 M 90.00000000000001 200 L 90.00000000000001 200 L 90.00000000000001 205 M 80 200 L 80 200 L 80 205 M 70 200 L 70 200 L 70 205 M 60 200 L 60 200 L 60 205 M 49.99999999999997 0 L 49.99999999999997 400 M 39.99999999999997 200 L 39.99999999999997 200 L 39.99999999999997 205 M 29.99999999999997 200 L 29.99999999999997 200 L 29.99999999999997 205 M 19.999999999999943 200 L 19.999999999999943 200 L 19.999999999999943 205 M 9.999999999999943 200 L 9.999999999999943 200 L 9.999999999999943 205" clip-path="view-box inset(0px 0px 0px 0px)" display="inline"></path><line id="jxgbox2_jxgBoard40650L13" style="position: absolute; transition: fill 100ms, fill-opacity 100ms, stroke 100ms, stroke-opacity 100ms, stroke-width 100ms; visibility: inherit;" clip-path="view-box inset(0px 0px 0px 0px)" stroke="#666666" stroke-opacity="1" stroke-width="1px" pointer-events="visibleStroke" fill-opacity="0" tabindex="null" marker-end="url(#jxgbox2_jxgBoard40650L13TriangleEnd1)" x1="200" y1="396" x2="200" y2="12" stroke-linecap="butt" display="inline"></line><path id="jxgbox2_jxgBoard40650L13_ticks_1" style="position: absolute; visibility: inherit;" stroke-linecap="round" stroke-linejoin="round" fill-rule="evenodd" stroke="#666666" fill="none" stroke-opacity="0.25" stroke-width="1" d=" M 195 190 L 200 190 L 200 190 M 195 180 L 200 180 L 200 180 M 195 170 L 200 170 L 200 170 M 195 160 L 200 160 L 200 160 M 0 150 L 400 150 M 195 140 L 200 140 L 200 140 M 195 130 L 200 130 L 200 130 M 195 120 L 200 120 L 200 120 M 195 110.00000000000001 L 200 110.00000000000001 L 200 110.00000000000001 M 0 100.00000000000001 L 400 100.00000000000001 M 195 90.00000000000001 L 200 90.00000000000001 L 200 90.00000000000001 M 195 80 L 200 80 L 200 80 M 195 70 L 200 70 L 200 70 M 195 60 L 200 60 L 200 60 M 0 49.99999999999997 L 400 49.99999999999997 M 195 39.99999999999997 L 200 39.99999999999997 L 200 39.99999999999997 M 195 29.99999999999997 L 200 29.99999999999997 L 200 29.99999999999997 M 195 19.999999999999943 L 200 19.999999999999943 L 200 19.999999999999943 M 195 210 L 200 210 L 200 210 M 195 220 L 200 220 L 200 220 M 195 230 L 200 230 L 200 230 M 195 240 L 200 240 L 200 240 M 0 250 L 400 250 M 195 260 L 200 260 L 200 260 M 195 270 L 200 270 L 200 270 M 195 280 L 200 280 L 200 280 M 195 290 L 200 290 L 200 290 M 0 300 L 400 300 M 195 310 L 200 310 L 200 310 M 195 320 L 200 320 L 200 320 M 195 330 L 200 330 L 200 330 M 195 340 L 200 340 L 200 340 M 0 350 L 400 350 M 195 360 L 200 360 L 200 360 M 195 370 L 200 370 L 200 370 M 195 380.00000000000006 L 200 380.00000000000006 L 200 380.00000000000006 M 195 390.00000000000006 L 200 390.00000000000006 L 200 390.00000000000006" clip-path="view-box inset(0px 0px 0px 0px)" display="inline"></path></g><g></g><g></g><g></g><g><ellipse id="jxgbox2_jxgBoard40650C23" style="position: absolute; transition: fill 100ms, fill-opacity 100ms, stroke 100ms, stroke-opacity 100ms, stroke-width 100ms; visibility: inherit;" clip-path="view-box inset(0px 0px 0px 0px)" stroke="#00ff00" stroke-opacity="1" stroke-width="2px" pointer-events="visibleStroke" fill-opacity="0" tabindex="0" cx="200" cy="200" rx="100" ry="100" stroke-linecap="butt" display="inline"></ellipse></g><g></g><g></g><g><ellipse id="jxgbox2_jxgBoard40650P1" style="position: absolute; transition: fill 100ms, fill-opacity 100ms, stroke 100ms, stroke-opacity 100ms, stroke-width 100ms; visibility: hidden;" clip-path="view-box inset(0px 0px 0px 0px)" stroke="#D55E00" stroke-opacity="1" stroke-width="2px" fill="#D55E00" pointer-events="visiblePainted" fill-opacity="1" tabindex="null" cx="200" cy="200" rx="4" ry="4" display="none"></ellipse><ellipse id="jxgbox2_jxgBoard40650P2" style="position: absolute; transition: fill 100ms, fill-opacity 100ms, stroke 100ms, stroke-opacity 100ms, stroke-width 100ms; visibility: hidden;" clip-path="view-box inset(0px 0px 0px 0px)" stroke="#D55E00" stroke-opacity="1" stroke-width="2px" fill="#D55E00" pointer-events="visiblePainted" fill-opacity="1" tabindex="null" cx="300" cy="200" rx="4" ry="4" display="none"></ellipse><text id="jxgbox2_jxgBoard40650L3_ticks_15Label1" style="position: absolute; white-space: nowrap; font-family: Arial, Helvetica, Geneva, sans-serif; font-size: 12px; transition: color 100ms, opacity 100ms; visibility: inherit;" tabindex="null" clip-path="view-box inset(0px 0px 0px 0px)" fill="#000000" pointer-events="visiblePainted" fill-opacity="1" class="JXGtext" x="250px" text-anchor="middle" y="203px" dy="1.6ex" dominant-baseline="auto" display="inline">0.5</text><text id="jxgbox2_jxgBoard40650L3_ticks_110Label2" style="position: absolute; white-space: nowrap; font-family: Arial, Helvetica, Geneva, sans-serif; font-size: 12px; transition: color 100ms, opacity 100ms; visibility: inherit;" tabindex="null" clip-path="view-box inset(0px 0px 0px 0px)" fill="#000000" pointer-events="visiblePainted" fill-opacity="1" class="JXGtext" x="300px" text-anchor="middle" y="203px" dy="1.6ex" dominant-baseline="auto" display="inline">1</text><text id="jxgbox2_jxgBoard40650L3_ticks_115Label3" style="position: absolute; white-space: nowrap; font-family: Arial, Helvetica, Geneva, sans-serif; font-size: 12px; transition: color 100ms, opacity 100ms; visibility: inherit;" tabindex="null" clip-path="view-box inset(0px 0px 0px 0px)" fill="#000000" pointer-events="visiblePainted" fill-opacity="1" class="JXGtext" x="350px" text-anchor="middle" y="203px" dy="1.6ex" dominant-baseline="auto" display="inline">1.5</text><text id="jxgbox2_jxgBoard40650L3_ticks_123Label4" style="position: absolute; white-space: nowrap; font-family: Arial, Helvetica, Geneva, sans-serif; font-size: 12px; transition: color 100ms, opacity 100ms; visibility: inherit;" tabindex="null" clip-path="view-box inset(0px 0px 0px 0px)" fill="#000000" pointer-events="visiblePainted" fill-opacity="1" class="JXGtext" x="150px" text-anchor="middle" y="203px" dy="1.6ex" dominant-baseline="auto" display="inline">−0.5</text><text id="jxgbox2_jxgBoard40650L3_ticks_128Label5" style="position: absolute; white-space: nowrap; font-family: Arial, Helvetica, Geneva, sans-serif; font-size: 12px; transition: color 100ms, opacity 100ms; visibility: inherit;" tabindex="null" clip-path="view-box inset(0px 0px 0px 0px)" fill="#000000" pointer-events="visiblePainted" fill-opacity="1" class="JXGtext" x="100.00000000000001px" text-anchor="middle" y="203px" dy="1.6ex" dominant-baseline="auto" display="inline">−1</text><text id="jxgbox2_jxgBoard40650L3_ticks_133Label6" style="position: absolute; white-space: nowrap; font-family: Arial, Helvetica, Geneva, sans-serif; font-size: 12px; transition: color 100ms, opacity 100ms; visibility: inherit;" tabindex="null" clip-path="view-box inset(0px 0px 0px 0px)" fill="#000000" pointer-events="visiblePainted" fill-opacity="1" class="JXGtext" x="49.99999999999997px" text-anchor="middle" y="203px" dy="1.6ex" dominant-baseline="auto" display="inline">−1.5</text><ellipse id="jxgbox2_jxgBoard40650P11" style="position: absolute; transition: fill 100ms, fill-opacity 100ms, stroke 100ms, stroke-opacity 100ms, stroke-width 100ms; visibility: hidden;" clip-path="view-box inset(0px 0px 0px 0px)" stroke="#D55E00" stroke-opacity="1" stroke-width="2px" fill="#D55E00" pointer-events="visiblePainted" fill-opacity="1" tabindex="null" cx="200" cy="200" rx="4" ry="4" display="none"></ellipse><ellipse id="jxgbox2_jxgBoard40650P12" style="position: absolute; transition: fill 100ms, fill-opacity 100ms, stroke 100ms, stroke-opacity 100ms, stroke-width 100ms; visibility: hidden;" clip-path="view-box inset(0px 0px 0px 0px)" stroke="#D55E00" stroke-opacity="1" stroke-width="2px" fill="#D55E00" pointer-events="visiblePainted" fill-opacity="1" tabindex="null" cx="200" cy="100" rx="4" ry="4" display="none"></ellipse><text id="jxgbox2_jxgBoard40650L13_ticks_15Label1" style="position: absolute; white-space: nowrap; font-family: Arial, Helvetica, Geneva, sans-serif; font-size: 12px; transition: color 100ms, opacity 100ms; visibility: inherit;" tabindex="null" clip-path="view-box inset(0px 0px 0px 0px)" fill="#000000" pointer-events="visiblePainted" fill-opacity="1" class="JXGtext" x="194px" text-anchor="end" y="150px" dy="0.6ex" dominant-baseline="auto" display="inline">0.5</text><text id="jxgbox2_jxgBoard40650L13_ticks_110Label2" style="position: absolute; white-space: nowrap; font-family: Arial, Helvetica, Geneva, sans-serif; font-size: 12px; transition: color 100ms, opacity 100ms; visibility: inherit;" tabindex="null" clip-path="view-box inset(0px 0px 0px 0px)" fill="#000000" pointer-events="visiblePainted" fill-opacity="1" class="JXGtext" x="194px" text-anchor="end" y="100.00000000000001px" dy="0.6ex" dominant-baseline="auto" display="inline">1</text><text id="jxgbox2_jxgBoard40650L13_ticks_115Label3" style="position: absolute; white-space: nowrap; font-family: Arial, Helvetica, Geneva, sans-serif; font-size: 12px; transition: color 100ms, opacity 100ms; visibility: inherit;" tabindex="null" clip-path="view-box inset(0px 0px 0px 0px)" fill="#000000" pointer-events="visiblePainted" fill-opacity="1" class="JXGtext" x="194px" text-anchor="end" y="49.99999999999997px" dy="0.6ex" dominant-baseline="auto" display="inline">1.5</text><text id="jxgbox2_jxgBoard40650L13_ticks_123Label4" style="position: absolute; white-space: nowrap; font-family: Arial, Helvetica, Geneva, sans-serif; font-size: 12px; transition: color 100ms, opacity 100ms; visibility: inherit;" tabindex="null" clip-path="view-box inset(0px 0px 0px 0px)" fill="#000000" pointer-events="visiblePainted" fill-opacity="1" class="JXGtext" x="194px" text-anchor="end" y="250px" dy="0.6ex" dominant-baseline="auto" display="inline">−0.5</text><text id="jxgbox2_jxgBoard40650L13_ticks_128Label5" style="position: absolute; white-space: nowrap; font-family: Arial, Helvetica, Geneva, sans-serif; font-size: 12px; transition: color 100ms, opacity 100ms; visibility: inherit;" tabindex="null" clip-path="view-box inset(0px 0px 0px 0px)" fill="#000000" pointer-events="visiblePainted" fill-opacity="1" class="JXGtext" x="194px" text-anchor="end" y="300px" dy="0.6ex" dominant-baseline="auto" display="inline">−1</text><text id="jxgbox2_jxgBoard40650L13_ticks_133Label6" style="position: absolute; white-space: nowrap; font-family: Arial, Helvetica, Geneva, sans-serif; font-size: 12px; transition: color 100ms, opacity 100ms; visibility: inherit;" tabindex="null" clip-path="view-box inset(0px 0px 0px 0px)" fill="#000000" pointer-events="visiblePainted" fill-opacity="1" class="JXGtext" x="194px" text-anchor="end" y="350px" dy="0.6ex" dominant-baseline="auto" display="inline">−1.5</text><ellipse id="jxgbox2_jxgBoard40650P21" style="position: absolute; transition: fill 100ms, fill-opacity 100ms, stroke 100ms, stroke-opacity 100ms, stroke-width 100ms, width 100ms, height 100ms, rx 100ms, ry 100ms; visibility: inherit;" clip-path="view-box inset(0px 0px 0px 0px)" stroke="#D55E00" stroke-opacity="1" stroke-width="2px" fill="#D55E00" pointer-events="visiblePainted" fill-opacity="1" tabindex="0" cx="200" cy="200" rx="4" ry="4" display="inline"></ellipse></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><foreignObject display="none" x="0" y="0" width="100%" height="100%" id="jxgbox2_foreignObj"></foreignObject></svg> </div> <div class="nb-cell markdown" name="md24"> Now that we have pure real arithmetic, let's go back to our definition of an equilateral triangle: </div> <div class="nb-cell program" name="p10"> seg_length(seg(point(X1, Y1), point(X2, Y2)), Len) :- { Len == sqrt((X2 - X1)**2 + (Y2 - Y1)**2) }. triangle_cond(triangle(A, B, C), seg(A, B), seg(B, C), seg(C, A)). triangle_cond(equilateral_triangle(A, B, C), Seg1, Seg2, Seg3) :- triangle_cond(triangle(A, B, C), Seg1, Seg2, Seg3), seg_length(Seg1, Len), seg_length(Seg2, Len), seg_length(Seg3, Len). triangle_dcg(Triangle) --> { triangle_cond(Triangle, Seg1, Seg2, Seg3) }, term(Seg1), term(Seg2), term(Seg3). </div> <div class="nb-cell query" name="q14"> phrase(triangle_dcg(equilateral_triangle(A, B, C)), L, R). </div> <div class="nb-cell markdown" name="md25"> Success! Can we now _draw_ an equilateral triangle using from a fully ground triangle ? Here is an example of an equilateral triangle A, B, C of with side length of 2: * point A (0, 0) * point B (2, 0) * point C (1, sqrt(3)) </div> <div class="nb-cell html" name="htm3"> <svg style="overflow: hidden; display: block;" width="400" height="400"><defs><filter id="jxgbox3_f1" width="300%" height="300%" filterUnits="userSpaceOnUse"><feOffset in="SourceGraphic" result="offOut" dx="5" dy="5"></feOffset><feColorMatrix in="offOut" result="colorOut" type="matrix" values="0.1 0 0 0 0 0 0.1 0 0 0 0 0 0.1 0 0 0 0 0 1 0"></feColorMatrix><feGaussianBlur in="colorOut" result="blurOut" stdDeviation="3"></feGaussianBlur><feBlend in="SourceGraphic" in2="blurOut" mode="normal"></feBlend></filter><marker id="jxgbox3_jxgBoard28454L3TriangleEnd1" style="position: absolute;" stroke="#666666" stroke-opacity="1" fill="#666666" fill-opacity="1" stroke-width="0" orient="auto" markerUnits="strokeWidth" refY="5" refX="0.05" viewBox="0 0 10 10" markerHeight="8" markerWidth="8" display="inherit"><path d="M 0,0 L 10,5 L 0,10 z"></path></marker><marker id="jxgbox3_jxgBoard28454L15TriangleEnd1" style="position: absolute;" stroke="#666666" stroke-opacity="1" fill="#666666" fill-opacity="1" stroke-width="0" orient="auto" markerUnits="strokeWidth" refY="5" refX="0.05" viewBox="0 0 10 10" markerHeight="8" markerWidth="8" display="inherit"><path d="M 0,0 L 10,5 L 0,10 z"></path></marker></defs><g><text id="jxgbox3_licenseText" style="font-family:Arial,Helvetica,sans-serif; font-size:12px; fill:#356AA0; opacity:0.3;" x="20px" y="14px">JSXGraph v1.8.0 Copyright (C) see https://jsxgraph.org</text></g><g></g><g><line id="jxgbox3_jxgBoard28454L3" style="position: absolute; transition: fill 100ms, fill-opacity 100ms, stroke 100ms, stroke-opacity 100ms, stroke-width 100ms; visibility: inherit;" clip-path="view-box inset(0px 0px 0px 0px)" stroke="#666666" stroke-opacity="1" stroke-width="1px" pointer-events="visibleStroke" fill-opacity="0" tabindex="null" marker-end="url(#jxgbox3_jxgBoard28454L3TriangleEnd1)" x1="4" y1="320" x2="388" y2="320" stroke-linecap="butt" display="inline"></line><path id="jxgbox3_jxgBoard28454L3_ticks_1" style="position: absolute; visibility: inherit;" stroke-linecap="round" stroke-linejoin="round" fill-rule="evenodd" stroke="#666666" fill="none" stroke-opacity="0.25" stroke-width="1" d=" M 88 320 L 88 320 L 88 325 M 96 320 L 96 320 L 96 325 M 104 320 L 104 320 L 104 325 M 112 320 L 112 320 L 112 325 M 120 0 L 120 400 M 128 320 L 128 320 L 128 325 M 136 320 L 136 320 L 136 325 M 144 320 L 144 320 L 144 325 M 152 320 L 152 320 L 152 325 M 160 0 L 160 400 M 168 320 L 168 320 L 168 325 M 176 320 L 176 320 L 176 325 M 184 320 L 184 320 L 184 325 M 192 320 L 192 320 L 192 325 M 200 0 L 200 400 M 208.00000000000003 320 L 208.00000000000003 320 L 208.00000000000003 325 M 216.00000000000003 320 L 216.00000000000003 320 L 216.00000000000003 325 M 224.00000000000003 320 L 224.00000000000003 320 L 224.00000000000003 325 M 232.00000000000006 320 L 232.00000000000006 320 L 232.00000000000006 325 M 240.00000000000003 0 L 240.00000000000003 400 M 248.00000000000006 320 L 248.00000000000006 320 L 248.00000000000006 325 M 256.00000000000006 320 L 256.00000000000006 320 L 256.00000000000006 325 M 264.00000000000006 320 L 264.00000000000006 320 L 264.00000000000006 325 M 272.00000000000006 320 L 272.00000000000006 320 L 272.00000000000006 325 M 280.00000000000006 0 L 280.00000000000006 400 M 288.0000000000001 320 L 288.0000000000001 320 L 288.0000000000001 325 M 296.0000000000001 320 L 296.0000000000001 320 L 296.0000000000001 325 M 304.0000000000001 320 L 304.0000000000001 320 L 304.0000000000001 325 M 312.0000000000001 320 L 312.0000000000001 320 L 312.0000000000001 325 M 320.0000000000001 0 L 320.0000000000001 400 M 328.0000000000001 320 L 328.0000000000001 320 L 328.0000000000001 325 M 336.0000000000001 320 L 336.0000000000001 320 L 336.0000000000001 325 M 344.0000000000001 320 L 344.0000000000001 320 L 344.0000000000001 325 M 352.0000000000001 320 L 352.0000000000001 320 L 352.0000000000001 325 M 360.0000000000001 0 L 360.0000000000001 400 M 368.00000000000017 320 L 368.00000000000017 320 L 368.00000000000017 325 M 376.00000000000017 320 L 376.00000000000017 320 L 376.00000000000017 325 M 384.00000000000017 320 L 384.00000000000017 320 L 384.00000000000017 325 M 72 320 L 72 320 L 72 325 M 64 320 L 64 320 L 64 325 M 56 320 L 56 320 L 56 325 M 48 320 L 48 320 L 48 325 M 40 0 L 40 400 M 32 320 L 32 320 L 32 325 M 24 320 L 24 320 L 24 325 M 16.000000000000007 320 L 16.000000000000007 320 L 16.000000000000007 325 M 8 320 L 8 320 L 8 325" clip-path="view-box inset(0px 0px 0px 0px)" display="inline"></path><line id="jxgbox3_jxgBoard28454L15" style="position: absolute; transition: fill 100ms, fill-opacity 100ms, stroke 100ms, stroke-opacity 100ms, stroke-width 100ms; visibility: inherit;" clip-path="view-box inset(0px 0px 0px 0px)" stroke="#666666" stroke-opacity="1" stroke-width="1px" pointer-events="visibleStroke" fill-opacity="0" tabindex="null" marker-end="url(#jxgbox3_jxgBoard28454L15TriangleEnd1)" x1="80" y1="396" x2="80" y2="12" stroke-linecap="butt" display="inline"></line><path id="jxgbox3_jxgBoard28454L15_ticks_1" style="position: absolute; visibility: inherit;" stroke-linecap="round" stroke-linejoin="round" fill-rule="evenodd" stroke="#666666" fill="none" stroke-opacity="0.25" stroke-width="1" d=" M 75 312 L 80 312 L 80 312 M 75 304 L 80 304 L 80 304 M 75 296 L 80 296 L 80 296 M 75 288 L 80 288 L 80 288 M 0 280 L 400 280 M 75 272 L 80 272 L 80 272 M 75 264 L 80 264 L 80 264 M 75 256 L 80 256 L 80 256 M 75 248 L 80 248 L 80 248 M 0 240 L 400 240 M 75 232 L 80 232 L 80 232 M 75 224 L 80 224 L 80 224 M 75 216 L 80 216 L 80 216 M 75 208 L 80 208 L 80 208 M 0 200 L 400 200 M 75 191.99999999999997 L 80 191.99999999999997 L 80 191.99999999999997 M 75 183.99999999999997 L 80 183.99999999999997 L 80 183.99999999999997 M 75 175.99999999999997 L 80 175.99999999999997 L 80 175.99999999999997 M 75 167.99999999999994 L 80 167.99999999999994 L 80 167.99999999999994 M 0 159.99999999999997 L 400 159.99999999999997 M 75 151.99999999999994 L 80 151.99999999999994 L 80 151.99999999999994 M 75 143.99999999999994 L 80 143.99999999999994 L 80 143.99999999999994 M 75 135.99999999999994 L 80 135.99999999999994 L 80 135.99999999999994 M 75 127.99999999999994 L 80 127.99999999999994 L 80 127.99999999999994 M 0 119.99999999999994 L 400 119.99999999999994 M 75 111.99999999999991 L 80 111.99999999999991 L 80 111.99999999999991 M 75 103.99999999999991 L 80 103.99999999999991 L 80 103.99999999999991 M 75 95.99999999999991 L 80 95.99999999999991 L 80 95.99999999999991 M 75 87.99999999999989 L 80 87.99999999999989 L 80 87.99999999999989 M 0 79.99999999999989 L 400 79.99999999999989 M 75 71.99999999999989 L 80 71.99999999999989 L 80 71.99999999999989 M 75 63.999999999999886 L 80 63.999999999999886 L 80 63.999999999999886 M 75 55.999999999999886 L 80 55.999999999999886 L 80 55.999999999999886 M 75 47.999999999999886 L 80 47.999999999999886 L 80 47.999999999999886 M 0 39.999999999999886 L 400 39.999999999999886 M 75 31.99999999999983 L 80 31.99999999999983 L 80 31.99999999999983 M 75 23.99999999999983 L 80 23.99999999999983 L 80 23.99999999999983 M 75 15.99999999999983 L 80 15.99999999999983 L 80 15.99999999999983 M 75 328 L 80 328 L 80 328 M 75 336 L 80 336 L 80 336 M 75 344 L 80 344 L 80 344 M 75 352 L 80 352 L 80 352 M 0 360 L 400 360 M 75 368 L 80 368 L 80 368 M 75 376 L 80 376 L 80 376 M 75 384 L 80 384 L 80 384 M 75 392 L 80 392 L 80 392" clip-path="view-box inset(0px 0px 0px 0px)" display="inline"></path></g><g></g><g></g><g></g><g></g><g><line id="jxgbox3_jxgBoard28454L31" style="position: absolute; transition: fill 100ms, fill-opacity 100ms, stroke 100ms, stroke-opacity 100ms, stroke-width 100ms; visibility: inherit;" clip-path="view-box inset(0px 0px 0px 0px)" stroke="#000000" stroke-opacity="1" stroke-width="2px" pointer-events="visibleStroke" fill-opacity="0" tabindex="0" x1="80" y1="320" x2="240" y2="320" stroke-linecap="butt" display="inline"></line><line id="jxgbox3_jxgBoard28454L32" style="position: absolute; transition: fill 100ms, fill-opacity 100ms, stroke 100ms, stroke-opacity 100ms, stroke-width 100ms; visibility: inherit;" clip-path="view-box inset(0px 0px 0px 0px)" stroke="#000000" stroke-opacity="1" stroke-width="2px" pointer-events="visibleStroke" fill-opacity="0" tabindex="0" x1="240" y1="320" x2="160" y2="181.43592" stroke-linecap="butt" display="inline"></line><line id="jxgbox3_jxgBoard28454L33" style="position: absolute; transition: fill 100ms, fill-opacity 100ms, stroke 100ms, stroke-opacity 100ms, stroke-width 100ms; visibility: inherit;" clip-path="view-box inset(0px 0px 0px 0px)" stroke="#000000" stroke-opacity="1" stroke-width="2px" pointer-events="visibleStroke" fill-opacity="0" tabindex="0" x1="160" y1="181.43592" x2="80" y2="320" stroke-linecap="butt" display="inline"></line></g><g></g><g><ellipse id="jxgbox3_jxgBoard28454P1" style="position: absolute; transition: fill 100ms, fill-opacity 100ms, stroke 100ms, stroke-opacity 100ms, stroke-width 100ms; visibility: hidden;" clip-path="view-box inset(0px 0px 0px 0px)" stroke="#D55E00" stroke-opacity="1" stroke-width="2px" fill="#D55E00" pointer-events="visiblePainted" fill-opacity="1" tabindex="null" cx="80" cy="320" rx="4" ry="4" display="none"></ellipse><ellipse id="jxgbox3_jxgBoard28454P2" style="position: absolute; transition: fill 100ms, fill-opacity 100ms, stroke 100ms, stroke-opacity 100ms, stroke-width 100ms; visibility: hidden;" clip-path="view-box inset(0px 0px 0px 0px)" stroke="#D55E00" stroke-opacity="1" stroke-width="2px" fill="#D55E00" pointer-events="visiblePainted" fill-opacity="1" tabindex="null" cx="160" cy="320" rx="4" ry="4" display="none"></ellipse><text id="jxgbox3_jxgBoard28454L3_ticks_15Label1" style="position: absolute; white-space: nowrap; font-family: Arial, Helvetica, Geneva, sans-serif; font-size: 12px; transition: color 100ms, opacity 100ms; visibility: inherit;" tabindex="null" clip-path="view-box inset(0px 0px 0px 0px)" fill="#000000" pointer-events="visiblePainted" fill-opacity="1" class="JXGtext" x="120px" text-anchor="middle" y="323px" dy="1.6ex" dominant-baseline="auto" display="inline">0.5</text><text id="jxgbox3_jxgBoard28454L3_ticks_110Label2" style="position: absolute; white-space: nowrap; font-family: Arial, Helvetica, Geneva, sans-serif; font-size: 12px; transition: color 100ms, opacity 100ms; visibility: inherit;" tabindex="null" clip-path="view-box inset(0px 0px 0px 0px)" fill="#000000" pointer-events="visiblePainted" fill-opacity="1" class="JXGtext" x="160px" text-anchor="middle" y="323px" dy="1.6ex" dominant-baseline="auto" display="inline">1</text><text id="jxgbox3_jxgBoard28454L3_ticks_115Label3" style="position: absolute; white-space: nowrap; font-family: Arial, Helvetica, Geneva, sans-serif; font-size: 12px; transition: color 100ms, opacity 100ms; visibility: inherit;" tabindex="null" clip-path="view-box inset(0px 0px 0px 0px)" fill="#000000" pointer-events="visiblePainted" fill-opacity="1" class="JXGtext" x="200px" text-anchor="middle" y="323px" dy="1.6ex" dominant-baseline="auto" display="inline">1.5</text><text id="jxgbox3_jxgBoard28454L3_ticks_120Label4" style="position: absolute; white-space: nowrap; font-family: Arial, Helvetica, Geneva, sans-serif; font-size: 12px; transition: color 100ms, opacity 100ms; visibility: inherit;" tabindex="null" clip-path="view-box inset(0px 0px 0px 0px)" fill="#000000" pointer-events="visiblePainted" fill-opacity="1" class="JXGtext" x="240.00000000000003px" text-anchor="middle" y="323px" dy="1.6ex" dominant-baseline="auto" display="inline">2</text><text id="jxgbox3_jxgBoard28454L3_ticks_125Label5" style="position: absolute; white-space: nowrap; font-family: Arial, Helvetica, Geneva, sans-serif; font-size: 12px; transition: color 100ms, opacity 100ms; visibility: inherit;" tabindex="null" clip-path="view-box inset(0px 0px 0px 0px)" fill="#000000" pointer-events="visiblePainted" fill-opacity="1" class="JXGtext" x="280.00000000000006px" text-anchor="middle" y="323px" dy="1.6ex" dominant-baseline="auto" display="inline">2.5</text><text id="jxgbox3_jxgBoard28454L3_ticks_130Label6" style="position: absolute; white-space: nowrap; font-family: Arial, Helvetica, Geneva, sans-serif; font-size: 12px; transition: color 100ms, opacity 100ms; visibility: inherit;" tabindex="null" clip-path="view-box inset(0px 0px 0px 0px)" fill="#000000" pointer-events="visiblePainted" fill-opacity="1" class="JXGtext" x="320.0000000000001px" text-anchor="middle" y="323px" dy="1.6ex" dominant-baseline="auto" display="inline">3</text><text id="jxgbox3_jxgBoard28454L3_ticks_135Label7" style="position: absolute; white-space: nowrap; font-family: Arial, Helvetica, Geneva, sans-serif; font-size: 12px; transition: color 100ms, opacity 100ms; visibility: inherit;" tabindex="null" clip-path="view-box inset(0px 0px 0px 0px)" fill="#000000" pointer-events="visiblePainted" fill-opacity="1" class="JXGtext" x="360.0000000000001px" text-anchor="middle" y="323px" dy="1.6ex" dominant-baseline="auto" display="inline">3.5</text><text id="jxgbox3_jxgBoard28454L3_ticks_143Label8" style="position: absolute; white-space: nowrap; font-family: Arial, Helvetica, Geneva, sans-serif; font-size: 12px; transition: color 100ms, opacity 100ms; visibility: inherit;" tabindex="null" clip-path="view-box inset(0px 0px 0px 0px)" fill="#000000" pointer-events="visiblePainted" fill-opacity="1" class="JXGtext" x="40px" text-anchor="middle" y="323px" dy="1.6ex" dominant-baseline="auto" display="inline">−0.5</text><ellipse id="jxgbox3_jxgBoard28454P13" style="position: absolute; transition: fill 100ms, fill-opacity 100ms, stroke 100ms, stroke-opacity 100ms, stroke-width 100ms; visibility: hidden;" clip-path="view-box inset(0px 0px 0px 0px)" stroke="#D55E00" stroke-opacity="1" stroke-width="2px" fill="#D55E00" pointer-events="visiblePainted" fill-opacity="1" tabindex="null" cx="80" cy="320" rx="4" ry="4" display="none"></ellipse><ellipse id="jxgbox3_jxgBoard28454P14" style="position: absolute; transition: fill 100ms, fill-opacity 100ms, stroke 100ms, stroke-opacity 100ms, stroke-width 100ms; visibility: hidden;" clip-path="view-box inset(0px 0px 0px 0px)" stroke="#D55E00" stroke-opacity="1" stroke-width="2px" fill="#D55E00" pointer-events="visiblePainted" fill-opacity="1" tabindex="null" cx="80" cy="240" rx="4" ry="4" display="none"></ellipse><text id="jxgbox3_jxgBoard28454L15_ticks_15Label1" style="position: absolute; white-space: nowrap; font-family: Arial, Helvetica, Geneva, sans-serif; font-size: 12px; transition: color 100ms, opacity 100ms; visibility: inherit;" tabindex="null" clip-path="view-box inset(0px 0px 0px 0px)" fill="#000000" pointer-events="visiblePainted" fill-opacity="1" class="JXGtext" x="74px" text-anchor="end" y="280px" dy="0.6ex" dominant-baseline="auto" display="inline">0.5</text><text id="jxgbox3_jxgBoard28454L15_ticks_110Label2" style="position: absolute; white-space: nowrap; font-family: Arial, Helvetica, Geneva, sans-serif; font-size: 12px; transition: color 100ms, opacity 100ms; visibility: inherit;" tabindex="null" clip-path="view-box inset(0px 0px 0px 0px)" fill="#000000" pointer-events="visiblePainted" fill-opacity="1" class="JXGtext" x="74px" text-anchor="end" y="240px" dy="0.6ex" dominant-baseline="auto" display="inline">1</text><text id="jxgbox3_jxgBoard28454L15_ticks_115Label3" style="position: absolute; white-space: nowrap; font-family: Arial, Helvetica, Geneva, sans-serif; font-size: 12px; transition: color 100ms, opacity 100ms; visibility: inherit;" tabindex="null" clip-path="view-box inset(0px 0px 0px 0px)" fill="#000000" pointer-events="visiblePainted" fill-opacity="1" class="JXGtext" x="74px" text-anchor="end" y="200px" dy="0.6ex" dominant-baseline="auto" display="inline">1.5</text><text id="jxgbox3_jxgBoard28454L15_ticks_120Label4" style="position: absolute; white-space: nowrap; font-family: Arial, Helvetica, Geneva, sans-serif; font-size: 12px; transition: color 100ms, opacity 100ms; visibility: inherit;" tabindex="null" clip-path="view-box inset(0px 0px 0px 0px)" fill="#000000" pointer-events="visiblePainted" fill-opacity="1" class="JXGtext" x="74px" text-anchor="end" y="159.99999999999997px" dy="0.6ex" dominant-baseline="auto" display="inline">2</text><text id="jxgbox3_jxgBoard28454L15_ticks_125Label5" style="position: absolute; white-space: nowrap; font-family: Arial, Helvetica, Geneva, sans-serif; font-size: 12px; transition: color 100ms, opacity 100ms; visibility: inherit;" tabindex="null" clip-path="view-box inset(0px 0px 0px 0px)" fill="#000000" pointer-events="visiblePainted" fill-opacity="1" class="JXGtext" x="74px" text-anchor="end" y="119.99999999999994px" dy="0.6ex" dominant-baseline="auto" display="inline">2.5</text><text id="jxgbox3_jxgBoard28454L15_ticks_130Label6" style="position: absolute; white-space: nowrap; font-family: Arial, Helvetica, Geneva, sans-serif; font-size: 12px; transition: color 100ms, opacity 100ms; visibility: inherit;" tabindex="null" clip-path="view-box inset(0px 0px 0px 0px)" fill="#000000" pointer-events="visiblePainted" fill-opacity="1" class="JXGtext" x="74px" text-anchor="end" y="79.99999999999989px" dy="0.6ex" dominant-baseline="auto" display="inline">3</text><text id="jxgbox3_jxgBoard28454L15_ticks_135Label7" style="position: absolute; white-space: nowrap; font-family: Arial, Helvetica, Geneva, sans-serif; font-size: 12px; transition: color 100ms, opacity 100ms; visibility: inherit;" tabindex="null" clip-path="view-box inset(0px 0px 0px 0px)" fill="#000000" pointer-events="visiblePainted" fill-opacity="1" class="JXGtext" x="74px" text-anchor="end" y="39.999999999999886px" dy="0.6ex" dominant-baseline="auto" display="inline">3.5</text><text id="jxgbox3_jxgBoard28454L15_ticks_143Label8" style="position: absolute; white-space: nowrap; font-family: Arial, Helvetica, Geneva, sans-serif; font-size: 12px; transition: color 100ms, opacity 100ms; visibility: inherit;" tabindex="null" clip-path="view-box inset(0px 0px 0px 0px)" fill="#000000" pointer-events="visiblePainted" fill-opacity="1" class="JXGtext" x="74px" text-anchor="end" y="360px" dy="0.6ex" dominant-baseline="auto" display="inline">−0.5</text><ellipse id="jxgbox3_jxgBoard28454P25" style="position: absolute; transition: fill 100ms, fill-opacity 100ms, stroke 100ms, stroke-opacity 100ms, stroke-width 100ms, width 100ms, height 100ms, rx 100ms, ry 100ms; visibility: inherit;" clip-path="view-box inset(0px 0px 0px 0px)" stroke="#D55E00" stroke-opacity="1" stroke-width="2px" fill="#D55E00" pointer-events="visiblePainted" fill-opacity="1" tabindex="0" cx="80" cy="320" rx="4" ry="4" display="inline"></ellipse><ellipse id="jxgbox3_jxgBoard28454P27" style="position: absolute; transition: fill 100ms, fill-opacity 100ms, stroke 100ms, stroke-opacity 100ms, stroke-width 100ms, width 100ms, height 100ms, rx 100ms, ry 100ms; visibility: inherit;" clip-path="view-box inset(0px 0px 0px 0px)" stroke="#D55E00" stroke-opacity="1" stroke-width="2px" fill="#D55E00" pointer-events="visiblePainted" fill-opacity="1" tabindex="0" cx="240" cy="320" rx="4" ry="4" display="inline"></ellipse><ellipse id="jxgbox3_jxgBoard28454P29" style="position: absolute; transition: fill 100ms, fill-opacity 100ms, stroke 100ms, stroke-opacity 100ms, stroke-width 100ms, width 100ms, height 100ms, rx 100ms, ry 100ms; visibility: inherit;" clip-path="view-box inset(0px 0px 0px 0px)" stroke="#D55E00" stroke-opacity="1" stroke-width="2px" fill="#D55E00" pointer-events="visiblePainted" fill-opacity="1" tabindex="0" cx="160" cy="181.43592" rx="4" ry="4" display="inline"></ellipse></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><foreignObject display="none" x="0" y="0" width="100%" height="100%" id="jxgbox3_foreignObj"></foreignObject></svg> </div> <div class="nb-cell markdown" name="md26"> So this *should* work ? </div> <div class="nb-cell query" name="q15"> {Y == sqrt(3)}, phrase(triangle_dcg(equilateral_triangle(point(0, 0), point(2, 0), point(1, Y))), L, R). </div> <div class="nb-cell query" name="q16"> {Y == sqrt(3)}, phrase(triangle_dcg(equilateral_triangle(A, B, C)), [seg(point(0, 0), point(2, 0)), seg(point(2, 0), point(1, Y)), seg(point(1, Y), point(0, 0))]). </div> <div class="nb-cell markdown" name="md27"> > **NOTE** > We precompute `sqrt(3)` because `clpBNR` does not support expression for attributed variables, only numbers. Nice! # Conclusion In this article, I have introduced a way to write pure prolog grammar to relate geometrical figures with a list of their graphical primitives by combining the DCG notation and constraint programming. Stay tune for the next blog post of this subject! </div> </div>