--- jupyter: description: 'What is new in planex2d v0.3: views and sections of solids in drawings, ASME drawings, the outline of a solid''s shadow, the quintic spline style, sketches from .CATPart files, animated and spreadsheet-driven constraint sketches, control-point splines, and what changed and was fixed.' --- # What's new This page describes planex2d v0.3 in full, v0.3.0 together with the fixes of v0.3.1, with what it adds, what changes for code written for v0.2, and what was fixed. The entries of v0.3.1 say so. The earlier releases are listed in [CHANGELOG.md](https://github.com/planex2d/planex2d/blob/master/CHANGELOG.md) in the repository. ## v0.3 v0.3 makes the core more dependable and finishes what v0.2 began, with views and sections of solids in drawings, the outline of a solid's shadow, a quintic default spline and more control in constraint sketches. ### Drawings of solids - **Views.** `px.Drawing(part, px.Front)` draws an OpenCASCADE solid as seen along a plane, one of `px.Front`, `px.Top`, `px.Left`, `px.Right`, `px.Rear` and `px.Bottom` or any `px.Plane`. Visible edges and the fold lines of curved faces are drawn wide, hidden edges narrow and dashed, and `hidden=False` leaves the hidden edges out. Where two faces meet tangentially, their smooth edge is drawn narrow and stops short of the outline. - **A pictorial.** `px.Drawing(part, px.Isometric)` is a shaded pictorial of the solid. - **Names and places.** Each view names itself, "Front view", "Top view" and so on to "Isometric view". `view_name=` gives it another name, and `view_name=""` removes the name. A view takes a sketch's dimensions with `dw.Add(sk)` and its place on a `px.Sheet` in first-angle projection like any drawing, and its edges are exact in DXF. - **Sections.** `px.Drawing(part, plane, section=True, view_name="Section view B-B")` cuts the solid at the plane, hatches the cut faces and leaves the hidden edges out. `dw.Section(section)` marks the cut on the view it is taken from, with the cutting line, wide at its ends, and an arrow with the section's letter at each end. - **How the lines are found.** The edges come from OpenCASCADE's exact hidden-line algorithm. The fold lines it traces only approximately are refined onto the exact surface, and the visibility of every line is checked by rays. ### ASME drawings `standard="ASME"` on `px.Sheet` and `px.Drawing` lays out the views in third-angle projection, with its symbol, and writes a decimal point in the dimensions, tables, title block and DXF, as in `px.Sheet(front, top, side, standard="ASME")`. `px.config.drawing_standard` sets it for a whole session, and its default is `"DIN"`. In this version, the dimension text along the line, the sheet sizes, the title block and the millimeters stay as they are with the default. ### The outline of a solid's shadow `silhouette=True` on `px.Project`, on the `Project` of a face, shape or wire and on `sk.Project` gives the outline of the shadow a solid casts on the plane instead of its edges. The outline includes the fold lines of curved faces where they bound it, such as the two lines of a cylinder seen side-on or the circle of a sphere, and the holes one sees through. `px.Shape(...)` of the outline is the region, as in `px.Shape(px.Project(shaft, plane=px.XZ, silhouette=True))`, and a sketch takes it as construction geometry, with the fold lines named `Projection.N.Fold.K`. ### Curves `tol=` on `px.Curve` and `sk.Curve` for a Python function sets how closely the fitted cubic passes the curve, relative to its size, 1e-10 by default as before. A curve with fine detail that 4096 samples do not fit to 1e-10 takes a looser target without a warning, as in `px.Curve(f, (0, 1), tol=1e-6)`. ### Splines - **The quintic style.** `style="quintic"` gives the quintic through the points with the least change of curvature, smooth to its fourth derivative at the points, as in `px.Spline(points, style="quintic")`. It is the default now (see Changed below). - **Tensions and curvature radii.** At points with a tangent, `px.Spline` takes `tensions=`, as `{index: tension}`, which scales the tangent's own length in every style, and `curvature_radii=`, as `{index: radius}`, in the quintic style. A radius is positive where the spline bends to the left of the tangent, and `math.inf` makes it run straight. A tangent is an arrow that the quintic runs along. - **Curvature radii in a sketch.** `sk.Spline` takes them too, and a curvature radius becomes a dimension, `px.CurvatureRadius` (`'CurvatureRadius.1'`), which `sk.Update` changes. `sk.Constrain("Spline.1.p1", px.CurvatureRadius, -30)` adds one, and a mirror's copy bends the other way. Sketch plots and drawings do not draw it yet. `px.Curvature` where a quintic meets another element gives the spline its curvature there when the constraints leave it free. See [Splines](basics/splines.md) for the styles. ### Files `px.FromCATPart(path, scale=px.Auto)` reads the sketches of a .CATPart file as planex2d sketches on their planes, with their constraints and dimensions, the part's parameters and formulas, and its splines in the quintic style with their tangents and curvature radii, as in `part = px.FromCATPart("bracket.CATPart")` followed by `part["parameters"].Update(w=0.06)`. What planex2d has no counterpart for is named in a warning. See [CATIA V5](../integrations/catia.md) for what is read. ### Constraint sketches - **One coordinate fixed.** `px.FixH` and `px.FixV` hold one coordinate of a point, as `px.Fix` holds both, as constraints and as drawing flags. `sk.Line(0, 5, px.FixH)` is a vertical line that keeps its h and may slide and stretch vertically. - **Control-point splines.** `sk.BSpline(poles, knots=None, degree=3, periodic=False)` draws a spline by its control points, in a `px.Sketch` and in a `px.QuickSketch`. It is an element of its own kind, `BSpline`, with the ids `'BSpline.1'` and so on. Its poles are point elements and the legs of its control polygon construction lines, both numbered from the first pole (`'s.p0'`, `'s.leg.0'`), so constraints on them shape the curve and move only the poles they need. A leg held horizontal makes the tangent at that end horizontal. `px.Tangent` joins the spline to the element before it, and `periodic=True` closes it. Copies get their own legs, and `Remove` takes the poles and legs along. - **Animations.** `sk.Animate(rows)` solves the sketch for each row of dimensions and plays the frames as a matplotlib animation, such as a crank through a full turn with `sk.Animate({"crank": np.linspace(0, 360, 73)})`. `steps=` adds frames between the rows, with the rows' designs kept, and `fps=` and `label=` set the speed and the caption. A row without a solution shows the last good geometry grayed out. The animation shows in Jupyter, and `anim.save("x.gif")` writes it to a file. - **Spreadsheets.** `sk.AddToSpreadsheet(path, name=, values=)` appends the current solution as a row of a CSV or .xlsx file, every dimension in its own column after a header row. `sk.ReadSpreadsheet(path)` reads the rows back as driving dimensions for `sk.Update(row)` or `sk.Animate(rows)`. CSV files use Python's csv module, .xlsx files openpyxl. - **Extending.** `sk.Extend(a, b)` lengthens a line along its line or an arc along its circle, to where it first meets `b`, as in `sk.Extend("Line.3", "Arc.2")`, or by a length, as in `sk.Extend("Line.3.end", 5)`. In a `px.Sketch`, a constraint that keeps the end where it is stops it, and with `auto_constraint` the new end stays on the target. - **What a redundant constraint follows from.** `sk.dependencies` lists each redundant constraint with the constraints it follows from, such as `{"Horizontal.1": ["Fix.1", "Fix.2"]}`. That is a small set that makes it redundant, not always the smallest. - **Angles to an axis.** An angle measured from `px.H` or `px.V`, as in `sk.Constrain(px.H, "arm", px.Angle, 30)`, is drafted in sketch plots, drawings and DXF with a thin auxiliary line along the axis from the line's start, and the angle's arc and value between it and the line. `dw.Place` places it as an angle between two lines. Before, it kept its value label. ### Selecting and measuring - **Selections along a chain.** Every selection call and `sk.Wire(id)` take `propagate=`. With `px.Coincident`, `px.Tangent` or `px.Curvature`, the selection grows by the edges that continue the chosen ones across their ends, meeting there, tangentially or with equal curvature as well, up to where two or more edges would continue it. `px.Minimal`, the default, takes the chosen edges alone. `face.edges(near=(2, 0), propagate=px.Tangent)` selects a side together with the fillets and sides tangent to it. - **A place along an edge.** `Measure(at=...)` on edges, wires, selections of edges, and sketch elements or chains gives the place at an arc length (a number) or nearest a given point, on the exact curves. It returns the point, the tangent, the normal, the curvature (positive for a left turn), the arc length, the distance from the point given and the edge the place lies on, as in `wire.Measure(at=12.5)["point_hv"]`. - **Hull and enclosing shapes.** `px.measurements.ConvexHull()` gives the convex hull as a face, exact with arcs. `EnclosingCircle()` and `EnclosingRectangle()` give the smallest circle and the rectangle of least area around the edges, at any angle, with their center and radius or their length, width and angle, as in `part.Measure(px.measurements.EnclosingRectangle())["length"]`. - **Reversed wires.** `wire.Reverse()` and `wire.Reversed()` run a wire the other way. ### Changed - `px.config.spline_style` is `"quintic"` by default, so `px.Spline`, `sk.Spline` and the splines of a `px.QuickSketch` without `style=` are quintics. Existing code draws a slightly different curve through the same points, of degree 5 in DXF and STEP. `style="averaged"` gives the cubic of before, and `px.config.spline_style = "averaged"` sets it for the whole session. - `sk.Plot(show_parent=solid)` draws the solid as the isometric view of `px.Drawing` does, seen along the sketch's normal, with gray shaded faces, wide visible edges and thin smooth edges, and without hidden edges. Before, every edge was projected flat over a lightly filled shadow. - `px.BSpline(poles)` takes clamped uniform knots and degree 3 when none are given. Each end knot is then repeated degree + 1 times with the inner knots evenly between, and a periodic curve gets one period of even knots. - `wire.Offset(d)` returns a wire, as a sketch's `Offset` does, instead of the band as a shape. For an open wire, it is the boundary of the band, on both sides by default, and `both_sides=False` gives the run on one side, left for d > 0 and right for d < 0. For a closed wire, it is the loop outside for d > 0 or inside for d < 0, and `both_sides=True` gives both loops. `.Face()` or `px.Shape([...])` makes the region, so `wire.Offset(0.5).Face()` gives the band of before. - `MinThickness` and `MaxThickness` measure walls. A disc counts only where the sides it touches face each other, with its touching points more than 90° apart. At a convex corner or a bend, the disc grows along the medial axis to the wall it leads to, so a rectangle's corners measure its wall and an outline drawn as lines measures the same as its curves, at any resolution. Only a knife edge sharper than 90° stays thin, with a warning. A NACA 2412 of chord 100 now has a `MinThickness(ignore_corners=30)` of about 4.0, where its leading edge's sides first face each other, instead of 3.13. - A write call's path whose file name has no extension gets the format's own, so `face.WriteStep("face")` writes face.step, and the same holds for `WriteBrep`, `WriteDXF`, `WriteSVG`, `WriteGeo`, `WriteKiCad` and a drawing's or sheet's `WriteSVG`, `WritePDF` and `WriteDXF`. Before, the file had no extension. A path with any extension is written as given. - The redundancy warning names what the constraint follows from, as in `Horizontal.1 ("l", px.Horizontal) follows from Fix.1, Fix.2; remove one of them`. Before, it listed every other redundant constraint. A new constraint that makes older ones redundant names them the same way. - `sk.Update` without a solution names what contradicts the new values, as in `Update(dc = 10): no solution, nothing changed; dc contradicts da, db`. - Sketch plots tell a repeated constraint from a contradicting one. A constraint that repeats others, while the sketch still solves, is drawn light pink, and one that contradicts others, with the elements of an over-constrained sketch, red (`sk.Plot(show_constraints=True)`, `px.ToSVG(sk, show_constraints=True)`). Before, both were drawn in one deep pink. Changed in v0.3.1. ### Fixed - Two sketches with equal ids in one drawing, such as two `Circle.1` or two `Distance.1`, are kept apart. Each circle gets its center lines, and a shared id is named by its full id, as in `dw.Place("Sketch.2.Distance.1", 10)`, or by a selected element, as in `dw.Center(sk("Circle.1"), off=True)`. A plain shared id raises an error that names both. Before, only the last sketch's circle got its center lines, and a plain id placed both dimensions. - `Drawing.Add` of a sketch on another plane, such as a face of the solid in a view or a sketch on a parallel plane, puts its dimensions and center lines in the view, turned with its axes and mirrored when the view sees it from behind. Before, its dimensions raised an IndexError or a ValueError. - A shaded isometric shows the nearest face at every point. Before, long triangles of a farther face could be painted over a nearer one, as small mis-shaded slivers on a gear's tooth flanks. Drawings with an isometric are also written about four times faster. - A drawing draws a slot's width (`slot.w`) across the slot and marks the center of an arc dimensioned by its diameter. Before, it left the width out and marked only arcs dimensioned by their radius. - Angles in drawings and in `sk.Plot` stay near their vertex. An angle whose edges start at the vertex is drawn at most three dimension-line spacings out, 24 mm on paper, and the angles at one vertex stack a spacing apart, the smaller one inside. Before, long edges or a long name drew the arc out to the end of the shorter edge. - `sk.Animate` gives every frame the grid of the view that all frames share. Before, each frame took its own grid, and the tick labels changed between frames and ran together. - Sketch plots draw splines, curves and offset curves within 2e-4 of the sketch's size, so a B-spline of one span no longer looks faceted. Before, they drew 16 pieces between two of their points. - `sk.Plot(show_parent=solid)` fills the solid's shadow, so a curved face seen side-on, such as a cylinder on top of a housing, has its outline and its fill. - The OpenCASCADE bindings compile with OpenCASCADE 8. - A piece of a live offset in a `px.Sketch` can be trimmed, split, removed, filleted or chamfered alone, and what is left keeps following its source. Its ties and points still refuse these edits, since they hold the offset together. This includes the offset of a spline, whose pieces stay on its exact offset curve, also across a closed spline's seam. Before, `Trim` and `Split` refused such a piece, and changing the offset's distance after removing one raised an IndexError. - `MinThickness` finds a thin wall between its samples. Every sample thinner than its neighbors, within 1.5 times the thinnest, is refined along its edge, not only the thinnest one. On spline faces, a thin neck whose samples fell beside it was missed before. - A pattern's new count, angle or spacing leaves its original where nothing holds it, and the copies spread around it. Before, the original moved so that the copies moved less. A constraint on a copy still moves the original as far as it needs. - After a dimension change, the free geometry of a sketch stays where it was drawn. Before, the solver's last phase, which takes back what the steps moved along free directions, failed after large changes, so that a slot widened from 6.6 to 9 turned slightly. - `Simplified`, and the cleanup of an export, keeps the identified sides of a periodic cell matched. A run of edges shorter than `remove_edges_smaller` goes to one point, the mean of its vertices, whichever way the wire runs. Before, the two sides of a cell could end at different points, and `px.OCCGeometry` refused the identification. - `Relate` of a face and a face of the same outline with a hole puts the hole in the first face's interior and in the second's exterior. Before, the matrix left the hole out, and `Equals`, `Covers` and the other named relations followed it, so a square equaled the same square with a hole. - `px.measurements.EnclosingCircle` holds an outline with arcs or splines whole, and `EnclosingRectangle` is the least rectangle around it. Before, the circle could leave a spline's outline out by about 1e-5 of its size, and the rectangle could come out larger than the least. - A spline whose approximation has a piece with nearly equal end tangents is built, or refused with the wire's message if it crosses itself. Before, a closed spline with tangents through points on one line raised "Arc: a value is not finite (-nan)". - `sk.Update` changes a `px.CurvatureRadius` to the other sign or from straight (`math.inf`). Before, it found no solution. - A coordinate as small as 5e-324, the least subnormal number, no longer makes the build of a wire or an offset run out of memory. - `Trim` and `Split` leave no constraint redundant. Where a cut ties a cut point to a cutter's end, the coincidence that put that point on the element cut, the user's or an earlier cut's, repeats the tie and is removed, and the sketch's degrees of freedom stay as they were. Before, that coincidence stayed and was drawn as over-constrained. Fixed in v0.3.1. - `Mirrored` leaves no constraint redundant. A point on the mirror axis joins its copy, and the coincidence that put the point on the axis is removed. Before, it stayed and was drawn as over-constrained. Fixed in v0.3.1. - `Drawing.Center(id)` marks the center of a construction circle as asked, with lines across it or, with `cross=True`, a short cross, as in `px.Drawing(hook).Center("throat").Center("back", cross=True)`. Before, it drew nothing for one. Fixed in v0.3.1. - `px.FromCATPart` reads a sketch with a circle, an arc or a spline point made coincident with the sketch's origin. The point lies on planex2d's `origin`, held there by a coincidence. Before, such a file raised a KeyError. Fixed in v0.3.1. - `px.FromCATPart` keeps the sketch's coincidences when its dimensions change. A line or an arc that starts or ends on the sketch's origin, and two points made coincident in the .CATPart file, such as a line's end on a circle's center, stay together. Before, they were drawn at the same place but came apart on `sk.Update`. Fixed in v0.3.1. - `px.FromCATPart` reads a spline made tangent to a curve, where the .CATPart file gives it no tangent arrow, with the tangent its points give it, so the tangency fixes where the spline meets the curve and the sketch reads as constrained as it was saved. Before, the spline got a tangent of its own there, and its end could slide along the curve. Fixed in v0.3.1. See [In the works](roadmap.md) for the features being explored.