v7.0.0
π These release notes appear cropped on the Releases page. You'll find them in full on their dedicated page. π
Full Changelog: v6.6.1...v7.0.0 β big thanks to @PeterC-DLS for #1311, #1324 and #1356 π§βπ€βπ§
tldr; This release includes numerous breaking changes and new features to the component library, @h5web/lib
. Highlights include: a simpler way to control the aspect ratio of the visualization; a more powerful SelectionTool
component with support for validating and transforming selections; a more flexible API to render SVG shapes on top of the canvas.
@h5web/app
- βοΈ Select-to-zoom interactions now zoom only on areas of at least 20x20px. This avoids inadvertently zooming on very small areas. Visual clues are provided to help users determine when selections being drawn become big enough to zoom on. #1345
- π Fix error when switching from
< NX Heatmap >
to< NX Line >
with an axis dataset longer than the signal dataset by one value (i.e. pixel boundaries) #1350 #1352 - π When drawing a zoom selection, the stroke of the rectangle is no longer partially cropped by the canvas. #1346
@h5web/lib
Open the sections below to see the changes.
β οΈ Breaking changes
[HeatmapVis, RgbVis]
Remove proplayout
, now replaced with propaspect
(cf. New features). #1284layout="cover"
π replace withaspect="equal"
(default)layout="fill"
π replace withaspect="auto"
layout="contain"
π remove; equivalent behaviour can be achieved by constraining the size of the visualization's container. #1283
[VisCanvas]
Remove propvisRatio
, now replaced with propaspect
(cf. New features). #1284visRatio={undefined}
π replace withaspect="auto"
(default)visRatio={cols / rows}
(i.e. orthonormal basis) π replace withaspect="equal"
(the correctvisRatio
is computed internally)visRatio={(cols / rows) / aspect}
π replace withaspect={aspect}
[VisCanvas]
Remove propcanvasRatio
, which allowed restricting the canvas to a specific ratio. The canvas now always fills the available space (i.e. parent container minus axes). Migration: remove prop and constrain size of visualization container. #1283[DefaultInteractions, SelectToZoom]
Remove propkeepRatio
. Interaction components now adjust their behaviours automatically when used inside a canvas with an aspect ratio (cf. New features). #1287[SelectionTool, AxialSelectionTool]
Callback propsonSelectionChange
andonSelectionEnd
can now receiveundefined
when the user cancels the selection with Escape or releases the modifier key (if any). You may use the newonValidSelection
callback instead ofonSelectionEnd
if you only care about successful selections. #1360[SelectionTool, AxialSelectionTool]
The shape of selection objects passed to callback props and to the render prop has changed - cf. the new interface vs. the old interface- Remove
SelectionRect
andSelectionLine
. Use the newDataToHtml
,SvgElement
,SvgRect
andSvgLine
components instead. See code example below. - Rename
useAxisSystemContext()
touseVisCanvasContext()
#1291 - Utility
getAxisValues
now returnsNumArray
(i.e.TypedArray | number[]
) instead ofnumber[]
. #1357 useCameraState
The callback parameter no longer receives acontext
argument, and the second parameter is removed (i.e. the array dependencies Γ lauseCallback
), which means that ESLint rulereact-hooks/exhaustive-deps
no longer has to be configured in consumer projects. #1361dataToHtml, getVisibleDomains
must now be retrieved from context and no longer require acontext
argument. #1342- Rename
getWorldFOV
togetFovBox
and move to context; it no longer requires acontext
argument and returns aBox
instance (cf. New features). #1361 - Hook
useCanvasEvents
and context utilitiesdataToHtml, dataToWorld, worldToData
now work solely withVector3
instances; they no longer accept/returnVector2
instances. More generally, computations in all three coordinate spaces (HTML, world, data) are now performed withVector3
only to avoid cluttering the codebase withVector3 <-> Vector2
conversions (cf. notably, the newBox
class, as well as theRect
type used for selection rectangles). We recommend you do the same when implementing your own interactivity features. #1300 #1341 - Callbacks passed to
useCanvasEvents
no longer receive points in camera space (i.e. propertycameraPt
).
π New features
VisCanvas
now renders ansvg
element that overlays the canvas. Use the newSvgElement
component to append any SVG shape to it from anywhere inside theVisCanvas
component tree. See code example and screen recording below. #1322- New
SvgRect
andSvgLine
components to simplify rendering SVGrect
andline
elements from a tuple ofVector3
points.SvgRect
also provides a way to simulate rendering the stroke outside or inside of the rectangle (instead of centred on the boundary). These components are for convenience only; don't hesitate to renderrect
,line
and any other SVG element directly. #1332 #1346 - New
DataToHtml
component to convert points from data space to HTML space directly in JSX. Useful to render SVG shapes on the canvas and update them automatically when the user zooms or pans. See code example below. - New
Box
utility class based on Three'sBox 3
, but with a more convenient API and some useful additional methods. Particularly handy when implementing interactivity features (zoom selection, SVG ROIs, etc.) #1323 #1359 [LineVis]
Allow passing abscissa array longer than data array (i.e. the abscissa array gets cropped) #1352[VisCanvas]
Add propaspect
, which works similarly to matplotlib'sAxes.set_aspect
#1284:aspect="auto"
: the visualization is to fill the entire canvas (default; i.e. the internalvisRatio
is set toundefined
);aspect="equal"
: one ordinate unit is to be displayed with the same number of pixels on screen as one abscissa unit (equivalent toaspect={1}
; i.e. orthonormal basis);aspect={2}
: one ordinate unit is to be displayed with twice the number of pixels on screen as one abscissa unit.
[VisCanvas]
Addnice
property toabscissaConfig
andordinateConfig
props. This passes through to D3 to allow it to extend the domains to nice values when appropriate. #1324[SelectToZoom, AxialSelectToZoom]
Enforce minimum selection size of 20x20px, with help of visual clues, to avoid inadvertently zooming when users mean to click. #1345[XAxisZoom, YAxisZoom, AxialSelectToZoom]
now disable themselves when used inside a canvas with an aspect ratio (equal or custom). #1287[SelectToZoom]
now configures its own behaviour automatically according to the canvas' aspect ratio (or lack thereof). #1287[SelectionTool, AxialSelectionTool]
Add propsvalidate
andonValidSelection
to support validating selections. See code examples and screen recordings below. Callback propsonSelectionChange
andonSelectionEnd
, as well as thechildren
render prop, all receive a boolean flag conveying whether the selection they are invoked with is valid. #1345[SelectionTool, AxialSelectionTool]
The selection objects passed to the render prop and to the callback props now include the selection rectangle in HTML space (in addition to world and data space). This is likely what you want to use in the render prop to render the selection rectangle in SVG. See code examples below. #1343[SelectionTool]
Add proptransform
to allow transforming the selection while it is being drawn. The render prop andonSelectionChange
both receive the transformed selection object as first parameter and the raw, untransformed selection object as second parameter. This feature is used internally byAxialSelectionTool
to transform the user's selection into a selection that spans the entire width or height of the canvas. For another example, check out story Rect With Transform. #1301 #1340 #1349[Histogram]
Add propshowLeftAxis
, which defaults totrue
, to allow hiding the left axis (withshowLeftAxis={false}
) #1295[DataCurve, ScatterPoints]
Accept typed arrays for abscissas and ordinates #1357getValueToIndexScale, getAxisDomain, getAxisValues
Accept typed arrays as argument #1357useCameraState
Allow passing equality function as second parameter to skip unnecessary re-renders. #1361
βοΈ New context properties
canvasSize
β the size of the canvas (equivalent touseThree((state) => state.size)
) #1288canvasRatio
β the aspect ratio of the canvas (i.e.width / height
) #1288canvasBox
β aBox
instance that spans the entire canvas; useful for HTML-space computations, like clipping/clamping other boxes/points to the bounds of the canvas. #1361visRatio
β the ratio of the visualization, computed internally byVisCanvas
from the axis domains andaspect
prop. Defined when the canvas has an equal/custom aspect ratio;undefined
otherwise. #1287worldToHtml, htmlToWorld, dataToHtml, htmlToData
β space-conversion utilities;dataToHtml
was previously available as a direct import #1302 #1342getVisibleDomains, getFovBox
β camera-based utilities previous available as direct imports #1361
π Bug fixes
[SelectionTool, AxialSelectionTool]
:- Fix
onSelectionStart
callback called before user actually starts moving the mouse. #1318 - Fix
onSelectionChange
callback not called on first pointer move. #1339 - Fix
onSelectionEnd
callback not called when cancelling selection with Escape. #1360 - Invoke callbacks asynchronously, after render prop, to avoid bugs such as a potential persisted selection being displayed on top of the active selection for one render. #1318
- Wait for next available animation frame before re-rendering
SelectionTool
to avoid overloading the main thread whenonPointerMove
is triggered many times in a row. #1298
- Fix
Examples
Drawing SVG shapes from data coordinates
Example taken from story Building Blocks / SvgElement / Default:
See the code
<DataToHtml points={[new Vector3(2, 8), new Vector3(4, 6), new Vector3(3, 2), new Vector3(6, 4), new Vector3(6, 6), new Vector3(7, 7)]}>
{(pt1, pt2, pt3, pt4, pt5, pt6) => (
<SvgElement>
<SvgRect className={styles.rect} coords={[pt1, pt2]} />
<SvgLine
coords={[pt3, pt4]}
stroke="darkblue"
strokeWidth={5}
strokeDasharray={15}
strokeLinecap="round"
/>
<SvgRect
coords={[pt5, pt6]}
fill="none"
stroke="teal"
strokeWidth={10}
strokePosition="outside"
/>
</SvgElement>
)}
</DataToHtml>
You can make your SVG elements interactive by simply registering event handlers on them (e.g. onPointerDown={handlePointerDown}
) and by applying styles to them like :hover
(i.e. via className
or style
). Just make sure to apply at least pointer-events: auto
to the elements so they can respond to pointer events (the SVG overlay has pointer-events: none
to let events through to the canvas). Note that pointer events intercepted by SVG elements do not currently "bubble up" to the canvas, since the SVG overlay is not a child of the canvas. This limitation should be resolved in a future release of H5Web.
Internally, DataToHtml
retrieves space conversion utility dataToHtml
from context and uses useCameraState
to keep the HTML points up to date as the user interacts with the canvas. It then invokes the children
render prop with those points on every render. For advanced use cases, especially when optimising for performance, don't hesitate to perform the conversions and invoke useCameraState
yourself, perhaps with an equality function.
Validating selections
Example taken from story Building Blocks / SelectionTool / LineWithLengthValidation:
See the code
<SelectionTool validate={({ html: [start, end] }) => start.distanceTo(end) >= 100}>
{({ html: htmlSelection }, _, isValid) => (
<SvgElement>
<SvgLine
coords={htmlSelection}
stroke={isValid ? 'teal' : 'orangered'}
strokeWidth={3}
strokeLinecap="round"
/>
</SvgElement>
)}
</SelectionTool>
Example taken from story Building Blocks / AxialSelectionTool / WithValidation:
See the code
<AxialSelectionTool
axis="x"
validate={({ html }) => Box.fromPoints(...html).hasMinSize(200)} // note the use of the new `Box` utility class
onValidSelection={({ data: dataSelection }) => { /* save `dataSelection` somehow */ }}
>
{({ html: htmlSelection }, _, isValid) => (
<SvgElement>
<SvgRect
coords={htmlSelection}
fill={isValid ? 'teal' : 'orangered'}
fillOpacity={0.5}
/>
</SvgElement>
)}
</AxialSelectionTool>