--- jupyter: description: 'Geometry checks in a planex2d sketch: Diagnose lists open ends, ends that nearly meet, crossings, overlaps and degenerate elements with their ids and places, and show_diagnostics marks them in the plot. A traced plate with four typical faults, found and fixed.' --- # Geometry checks A face can only be made of an outline that closes, does not cross itself and has no edge twice. When `Face()` fails on a drawing, `Diagnose` shows why. It lists every fault of the drawn geometry with the ids of the elements and the place where it is, and `show_diagnostics=True` marks the faults in the plot. Both work on constraint sketches and quick sketches alike. ```python import math import planex as px ``` ```python nbsphinx="hidden" from docs_figures import Row ``` ## Five kinds of faults `Diagnose` returns a dict with five lists, one per kind of fault: - `open_ends`: an end of a line, arc or open spline that no other end meets; - `missing_coincident`: two ends that nearly meet, closer than `gap`, but are not one point; - `crossings`: two elements that meet where not both of them end, such as a line crossing an outline, a line ending on another one, or a circle touching a line; - `overlaps`: two elements that share a stretch; - `degenerate`: a line too short to have a direction, or an arc or circle of almost no radius. Each entry is a dict with the ids of the elements and the place `at`. Construction elements, points and the axes are not checked, since they never become part of a face. ## A traced plate The example is a plate of 60 × 40 with a bore of diameter 16, traced from a drawing with four mistakes that are easy to make. The left side ends 0.02 above the start of the bottom, the center line is drawn as an ordinary line, the right side is drawn a second time, and a stray line sticks out of the top: ```python sk = px.Sketch() sk.Line(60, id="bottom").Line((60, 40), id="right").Line((0, 40), id="top").Line((0, 0.02), id="left") sk.Move((30, -5)).Line((30, 45), id="center_line") sk.Move((60, 10)).Line((60, 30), id="right_again") sk.Move((45, 40)).Line((45, 52), id="stray") sk.Circle((30, 20), 8, id="bore") try: sk.Face() except ValueError as err: print(err) ``` `Face()` fails, and its message alone does not say which edges are at fault. `Diagnose` does: ```python for kind, found in sk.Diagnose().items(): for fault in found: print(kind, fault["ids"], "at", fault["at"]) ``` The center line crosses the bottom, the top and the bore, the stray line ends on the top, and the doubled side overlaps the right side. The ends of these three lines are open ends, and so are the two ends at the gap, which also form a missing coincident. In the plot, each kind has its own color (Figure 1a). ## Fixing the faults Each fault has its own fix. The gap is closed by a coincident constraint, the center line becomes a construction line, and the doubled side and the stray line are removed. After that, `Diagnose` finds nothing, and the face has the area of the plate less the bore (Figure 1b): ```python traced = sk.Copy() sk.Constrain("left.end", "bottom.start", px.Coincident) sk("center_line").construction = True sk.Remove("right_again") sk.Remove("stray") print(sk.Diagnose()) print("area", sk.Face().mass, "exact", 60 * 40 - math.pi * 8**2) ``` ```python nbsphinx="hide-input" Row((traced, "The traced plate, its faults marked", {"plot": {"show_diagnostics": True}}), (sk, "Fixed", {"plot": {"show_diagnostics": True}}), caption="A traced plate before and after its faults are fixed") ``` ## How close counts as nearly Two ends count as a missing coincident when they are closer than `gap`, and as two open ends when they are farther apart. By default, `gap=px.Auto` is a thousandth of the larger side of the drawing's bounding box, here 0.06, so the gap of 0.02 is found. With a smaller `gap`, the two ends are open ends instead: ```python for gap in (px.Auto, 0.01): found = traced.Diagnose(gap=gap) print(gap, "->", len(found["missing_coincident"]), "missing coincident,", len(found["open_ends"]), "open ends") ``` Ends closer than the rounding are always one point. For a drawing read from a file, where every joint misses by about the same small amount, `AutoConstrain` closes them all at once. See [From a drawing to a parametric sketch](from-drawing.md) for details.