A block whose content is only 3D solids leaves a marker wire carrying
its insertion snap, and that point counted as drawing extent: a
georeferenced block inserted at the origin made ZOOM E frame the
origin and the model together, 860 km wide. Extents now skip those
markers; the solids count through their meshes. The marker's snap also
took the insertion point straight from the OCS, so a mirrored insert's
snap sat on the far side of the origin.
ZOOM All fitted the limits alone. In a 3D view it is now Zoom
Extents, and in plan it frames the limits together with the extents.
Instanced solids summed their rotated point and translation in f32
before meeting the eye, which cost up to half a metre at survey
coordinates; the large terms now cancel first.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Options > Selection gains "Selection highlight opacity" and "Selection
highlight extra width". Below full opacity the object's own colour
shows through the highlight. The selection overlay draws with its own
copy of the frame uniforms: transparency on, and every line widened by
the extra width. Drawings opened at startup now get the selection
colour settings too; they used to keep the defaults until a setting
changed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A solid's edges and silhouettes were drawn in its face colour, and the
edge shader read that from the material instead of the edge vertices.
Colour 7 is white, so in Wireframe 2D a solid vanished white-on-white in
a layout viewport (and on a light model background), while wires and
block solids already turned black there. Solids now keep a separate
edge colour, colour 7 turned against the background they are drawn on,
and the edge shader uses the edge vertices' colour again, which also
brings back visual-style edge colours. Shaded faces keep their colour
so they still read as lit. (#1446)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- Gradients: the middle colour sits (horizon - 0.5) viewport widths from the
centre and blends over height viewport heights each side; two colours
blend over a viewport height each side.
- Image backgrounds: offset in device pixels (right, down), negative scale
mirrors, fitted with aspect ratio covers the viewport; the dialog keeps an
image's offset and scale.
- Layouts: a model-space view restored on a sheet asks for the viewport,
enters it and gives it the view's camera, background and visual style;
inside a viewport the view goes there; a paper-space view's background is
kept on the sheet viewport but not drawn; floating viewports draw their
background.
- Background objects no view or viewport uses any more are erased; a view
without a background restored where the viewport already is leaves the
viewport's background.
- The viewport's named visual style (Realistic, ...) shows by name and the
render-mode picker records its style on the viewport record.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
An image background not fitted to the screen keeps its own pixel size
(times its scale), centred on the viewport and moved by its offset, as
the reference draws it; tiling repeats it from there.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Measured against the reference: a two-colour gradient reaches its end
colours a viewport beyond the edges, and the bands blend linearly.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The rotation is stored in degrees and turns the bottom-to-top direction
counter-clockwise, measured in pixels; a two-colour gradient blends from
the bottom colour to the top one across the whole viewport.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Circle and ellipse shaders scaled the pattern length by the linetype
scale (PSLTSCALE in a viewport, annotation scale in model) but not the
dash and gap elements, so a dashed arc broke into dots.
Fixes#1644
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Turning a 141k-entity layer off/on took 1.5-5 s; it now takes ~0.6-0.9 s.
- Off/freeze toggles go through invalidate_layer_visibility: entities on
the layer keep their tessellation memo (visibility gates them before
tessellation); only block users re-expand. No mesh recolor.
- The resident patch reuses a surviving memo entry instead of
re-tessellating, guarded by the same display-context hash as the full
build.
- The patch looked up `layout.order` linearly per re-shown entity, which
made turning a big layer back on quadratic.
- recolor_meshes_for_handles resolved materials for handles with no mesh.
- Wire arena: a hidden slab keeps its instances and sets a `hidden` flag
in its consts (collapsed in the vertex shader); an unchanged re-show only
clears the flag. Const writes are batched into merged spans.
- The `layer_visible` automation action indexed the document's layer
table instead of the sortable panel rows the Layer* messages use.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Each band segment was a rectangle ending square at its vertex, so the
outer corner of every joint showed a notch. Every segment end now carries
the tangent of half its turn, and the shader slides that end's corners
onto the joint's bisector so neighbours meet in a miter. Turns sharper
than the limit keep butt ends. Constant-width runs inside a polyline split
around its arcs no longer count as tapered, which had kept them unmitred.
Fixes#1565
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- Lighting follows the view: the default source is a key light from the
upper left and a fill light from the lower right over an ambient term,
the other source one light from above; POINTCLOUDLIGHTING 1 lights the
front faces, 2 both.
- A selected or hovered point cloud shows its extents box.
- A move-gizmo arrow can be grabbed along its shaft; the grab offset is
taken on the press, so click-and-place and typed distances start from
the insertion point.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Points carry their world normal: POINTCLOUDLIGHTING lights them in the
shaded visual styles (1 lit from both sides, 2 with back faces dark),
from the view (POINTCLOUDLIGHTSOURCE 0) or the default lights (1).
The 2D wireframe style draws no cloud points: a found cloud shows its
extents and "Point clouds are not displayed in 2D visual style.",
or nothing with POINTCLOUD2DVSDISPLAY 1. While the view moves only
POINTCLOUDRTDENSITY per cent of the points are drawn; the points are
stored in an order where every leading part samples the whole cloud.
The colour map dialog lists its schemes by name.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Scans (.rcs) and projects (.rcp) are read and their points drawn as
screen-sized dots, coloured by the cloud's stylization. The cloud's
placement carries its scale and rotation; its extents and crops stay in
the scan's coordinates. A missing scan shows the extents with the saved
path. -POINTCLOUDATTACH / POINTCLOUDATTACH, POINTCLOUDCROP and the
POINTCLOUD* settings follow the reference's prompts and messages, the
Properties list is the reference's, and a locked cloud is left alone by
edits.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Only the wire shaders read transparency_enable; hatch (solid, gradient
and pattern), text and 3D/fill-triangle fragments always drew the
entity's alpha, so turning Show Transparency off left them see-through.
Fixes#1529
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A transparent pixel wrote depth, hiding lines and objects under the image
that were drawn after it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
An image's transparency flag decides whether transparent pixels show what
is behind the image or draw in their stored colour: the image pass and PDF
plots now follow it (it was ignored, so every PNG alpha showed through).
TRANSPARENCY selects images and asks for the mode, defaulting to the first
image's current one.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- Repurpose CircleInstance center_high.w and params.w for start_width and end_width (preserving exact 128-byte stride)
- Route wide polylines, tapered polylines with bulges, and donuts through split_mixed_polyline into GPU analytical arc instances
- Compute physical annular bands, width tapers, and round end caps in circle.wgsl using world-space distances to prevent 3D perspective foreshortening distortion
- Expand bounding quad margins dynamically with grazing-angle compensation to eliminate clipping
- Relax pick_tris check in wire classification and partition routines so wide entities retain CPU picking while drawing analytically
- Add unit and hardware GPU integration tests, including tilted 3D perspective rendering verification
Keep adaptive sampling in both text shaders with compact sampling loops. Validate shader derivatives and existing text rendering, including GPU pipeline creation.
Closes#964
The layout-switch OOM names its buffer: `Buffer with 'block_wire.vertices'
label is invalid`, right after `uncaptured wgpu error #1 … Out of Memory`.
That buffer is the largest thing this renderer allocates, and five sixths
of it is duplication.
A segment reaches the shader once per corner of its quad, and the packed
path satisfies that by writing the whole 60-byte record six times —
360 bytes a segment — with `vs_main` recovering the corner from
`vertex_index % 6`. Six identical copies, on a 2 GB card.
`block_wire_storage.wgsl` puts the segments in a read-only storage buffer
indexed by `vertex_index / 6` instead, so each record is stored once. The
geometry vertex buffer disappears; the placements move to slot 0 and the
segments arrive through the definition's bind group, which becomes
per-chunk because each chunk binds its own slice.
This is the pattern `wire_indexed.wgsl` already uses for per-wire
constants, down to the capability gate: `supports_wire_storage` asks for
exactly one vertex-stage storage binding, which is exactly what this
needs. Where it is false — WebGL, the forced compatibility renderer — the
packed shader and its vertex buffer are built as before.
`Segment` is declared with scalar members rather than `vec3<f32>`, which
would align to 16 in storage and reintroduce the padding this removes. A
test validates the shader through naga and asserts the struct's stride
equals `size_of::<BlockWireVertex>()`, because a layout mismatch would
otherwise surface only as garbage geometry on a real device.
`block-wire-geometry` reports the bytes uploaded and what the packed path
would have taken, so the saving is measured rather than assumed.
The cache value becomes `Arc<Vec<BlockGeometryChunk>>` while keeping the
translation-aware key from #1006 — both modes still have to invalidate
when the base instance moves, because the records hold its world
coordinates either way. `const_bgl` and the mode are decided together
when the pipeline is built, so they travel as one `BlockWireTarget`
rather than pushing `from_wires` past the argument limit.
Co-Authored-By: Claude Opus 5 <[email protected]>
Keep all solid geometry GPU-resident during navigation. Use vertex-rate instancing, compact untextured vertices, vertex-cache ordering, and cached silhouette sources without culling geometry.
Reuse full VPort records during camera and tile synchronization so
navigation cannot replace file-defined display state with defaults.
Honor image, ground, IBL, and sun-backed sky parameters in
background rendering.
Share source buffers across block placements and render nested content with GPU instance transforms.\n\nChunk silhouettes and compact mesh material data so large drawings stay under device buffer limits and remain responsive.
Route model, paper, viewport and nested block content through one traversal while preserving leaf renderers. Carry decoded material state into mesh shading and harden solid geometry conversion.
Preload one language-aware font resource across the interface, drawing text, and navigation labels.\n\nRender the navigation cube, ring, outlines, and labels through a 4x MSAA target.
Keep storage fast paths on capable adapters and use packed or texture fallbacks only when device limits require them.\n\nCompact mesh-edge varyings to avoid WebGL shader rejection.