From 8b077abfd4532078d0e15cfa4abcab961e089273 Mon Sep 17 00:00:00 2001 From: tpoisseau <22891227+tpoisseau@users.noreply.github.com> Date: Wed, 26 Aug 2026 16:03:59 +0200 Subject: [PATCH 1/3] feat(processings): integrate operatorUI ChartSVG Experimental: true --- src/component/1d/SVGContent1D.tsx | 6 ++ src/component/1d/plugin_svg_chart.tsx | 60 ++++++++++++++++++ src/component/context/ScaleContext.tsx | 2 +- .../core_slots/core_operator_chart_svg.tsx | 61 +++++++++++++++++++ 4 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 src/component/1d/plugin_svg_chart.tsx create mode 100644 src/component/utility/core_slots/core_operator_chart_svg.tsx diff --git a/src/component/1d/SVGContent1D.tsx b/src/component/1d/SVGContent1D.tsx index 84a127cf1..c6293e8f3 100644 --- a/src/component/1d/SVGContent1D.tsx +++ b/src/component/1d/SVGContent1D.tsx @@ -1,6 +1,7 @@ import { ClipPathContainer } from '../1d-2d/components/ClipPathContainer.js'; import SpectrumInfoBlock from '../1d-2d/components/SpectrumInfoBlock.js'; import { ShareDataProvider } from '../context/ShareDataContext.js'; +import useCheckExperimentalFeature from '../hooks/useCheckExperimentalFeature.ts'; import { ApodizationLine } from './ApodizationLine.js'; import ExclusionZonesAnnotations from './ExclusionZonesAnnotations.js'; @@ -18,11 +19,14 @@ import MultiAnalysisRanges from './multiAnalysis/MultiAnalysisRanges.js'; import MultiplicityTrees from './multiplicityTree/MultiplicityTrees.js'; import Peaks from './peaks/Peaks.js'; import PeaksShapes from './peaks/PeaksShapes.js'; +import { PluginSVGChart } from './plugin_svg_chart.tsx'; import Ranges from './ranges/Ranges.js'; import RangesIntegrals from './ranges/RangesIntegrals.js'; import { Signals } from './ranges/Signals.tsx'; export function SVGContent1D() { + const isExperimental = useCheckExperimentalFeature(); + return ( @@ -47,6 +51,8 @@ export function SVGContent1D() { + + {isExperimental && } diff --git a/src/component/1d/plugin_svg_chart.tsx b/src/component/1d/plugin_svg_chart.tsx new file mode 100644 index 000000000..e726279c1 --- /dev/null +++ b/src/component/1d/plugin_svg_chart.tsx @@ -0,0 +1,60 @@ +import { useMemo } from 'react'; + +import { useChartData } from '../context/ChartContext.tsx'; +import { useCore } from '../context/CoreContext.tsx'; +import { useScaleChecked } from '../context/ScaleContext.tsx'; +import { useIndicatorLineColor } from '../hooks/useIndicatorLineColor.ts'; +import { useStableSpectrum } from '../hooks/useSpectrum.ts'; +import { useVerticalAlign } from '../hooks/useVerticalAlign.ts'; +import { useLiveOperation } from '../panels/filtersPanel/processings/use_live_operation.ts'; +import { CoreOperatorChartSVG } from '../utility/core_slots/core_operator_chart_svg.tsx'; + +import { getYScale } from './utilities/scale.ts'; + +export function PluginSVGChart() { + const core = useCore(); + const spectrum = useStableSpectrum(); + const { height, width, margin } = useChartData(); + const [liveOperation] = useLiveOperation(); + const { scaleX: scaleXBuilder } = useScaleChecked(); + const indicatorLineColor = useIndicatorLineColor(); + + const scaleX = useMemo(() => scaleXBuilder(), [scaleXBuilder]); + const scaleY = useWindowYScale(); + + if (!liveOperation) return null; + if (!spectrum) return null; + + const operatorUI = core.slotOperator(liveOperation.operatorId); + if (!operatorUI) return null; + + return ( + + ); +} + +function useWindowYScale() { + const { spectraBottomMargin } = useScaleChecked(); + const { height, margin, yDomains } = useChartData(); + const verticalAlign = useVerticalAlign(); + + return getYScale({ + height, + margin, + verticalAlign, + yDomain: [0, 1], + yDomains, + spectraBottomMargin, + }); +} diff --git a/src/component/context/ScaleContext.tsx b/src/component/context/ScaleContext.tsx index 705984f6a..f4a23d4a6 100644 --- a/src/component/context/ScaleContext.tsx +++ b/src/component/context/ScaleContext.tsx @@ -20,7 +20,7 @@ export interface ScaleLinearNumberOptions { } type ScaleLinearNumberFunction = ( options?: ScaleLinearNumberOptions, -) => ScaleLinear; +) => ScaleLinear; interface ScaleState { scaleX: ScaleLinearNumberFunction | null; diff --git a/src/component/utility/core_slots/core_operator_chart_svg.tsx b/src/component/utility/core_slots/core_operator_chart_svg.tsx new file mode 100644 index 000000000..63f7ce721 --- /dev/null +++ b/src/component/utility/core_slots/core_operator_chart_svg.tsx @@ -0,0 +1,61 @@ +import type { ProcessingOperatorId } from '@zakodium/nmr-types'; +import type { + ProcessingOperatorUI, + ProcessingOperatorUIChartSVGProps, +} from '@zakodium/nmrium-core'; +import { useEffect } from 'react'; +import type { FallbackProps } from 'react-error-boundary'; +import { ErrorBoundary } from 'react-error-boundary'; + +interface CoreOperatorChartSVGProps extends ProcessingOperatorUIChartSVGProps { + operatorUI: ProcessingOperatorUI; +} + +export function CoreOperatorChartSVG(props: CoreOperatorChartSVGProps) { + const { + operatorUI, + core, + spectrum, + operation, + height, + width, + margin, + indicatorLineColor, + scaleX, + scaleY, + } = props; + + const { ChartSVG } = operatorUI; + if (!ChartSVG) return null; + + return ( + + + + ); +} + +/** + * SVG context. No friendly error rendering method. + * + * @param props + */ +function LogError(props: FallbackProps) { + const { error } = props; + + useEffect(() => { + reportError(error); + }, [error]); + + return null; +} From cfa2e24af8b828cb764d774f3e317db40fb31e19 Mon Sep 17 00:00:00 2001 From: tpoisseau <22891227+tpoisseau@users.noreply.github.com> Date: Wed, 26 Aug 2026 16:50:12 +0200 Subject: [PATCH 2/3] refactor: use `SVGPathBuilder` from `react-science` --- package.json | 2 +- src/component/1d/ApodizationLine.tsx | 4 +- src/component/1d/Line.tsx | 4 +- src/component/1d/baseline/BaselinePreview.tsx | 4 +- .../1d/database/DatabaseSpectrum.tsx | 4 +- .../1d/database/ResurrectedDatabaseRanges.tsx | 4 +- src/component/1d/matrix/Boxplot.tsx | 10 ++--- src/component/1d/matrix/Stocsy.tsx | 4 +- src/component/1d/peaks/usePeakShapesPath.ts | 4 +- .../2d/1d-tracer/HorizontalSliceChart.tsx | 4 +- src/component/2d/1d-tracer/Left1DChart.tsx | 4 +- src/component/2d/1d-tracer/Top1DChart.tsx | 4 +- .../2d/1d-tracer/VerticalSliceChart.tsx | 4 +- .../SpectrumPhaseTrace.tsx | 6 +-- src/component/2d/ft/Contours.tsx | 4 +- src/component/hooks/useIntegralPath.ts | 4 +- src/component/utility/PathBuilder.ts | 41 ------------------- 17 files changed, 35 insertions(+), 76 deletions(-) delete mode 100644 src/component/utility/PathBuilder.ts diff --git a/package.json b/package.json index 581d99556..8397f0dd2 100644 --- a/package.json +++ b/package.json @@ -111,7 +111,7 @@ "react-ocl-nmr": "^4.1.1", "react-plot": "^3.1.2", "react-rnd": "^10.5.3", - "react-science": "^23.0.1", + "react-science": "^23.1.1", "react-table": "^7.8.0", "smart-array-filter": "^5.0.0", "ts-pattern": "^5.9.0", diff --git a/src/component/1d/ApodizationLine.tsx b/src/component/1d/ApodizationLine.tsx index 59f4054d9..af7562917 100644 --- a/src/component/1d/ApodizationLine.tsx +++ b/src/component/1d/ApodizationLine.tsx @@ -6,6 +6,7 @@ import { createApodizationWindowData, default1DApodization, } from 'nmr-processing'; +import { SVGPathBuilder } from 'react-science/ui'; import { useChartData } from '../context/ChartContext.js'; import { useFilterSyncOptions } from '../context/FilterSyncOptionsContext.js'; @@ -16,7 +17,6 @@ import useSpectrum from '../hooks/useSpectrum.js'; import useTempSpectrum from '../hooks/useTempSpectrum.js'; import { useVerticalAlign } from '../hooks/useVerticalAlign.js'; import type { ApodizationOptions } from '../panels/filtersPanel/Filters/hooks/useApodization.js'; -import { PathBuilder } from '../utility/PathBuilder.js'; import { useIsInset } from './inset/InsetProvider.js'; import { getXScale, getYScale } from './utilities/scale.js'; @@ -84,7 +84,7 @@ export function ApodizationLine() { scaleX = windowScaleX; } - const pathBuilder = new PathBuilder(); + const pathBuilder = new SVGPathBuilder(); const { re, x } = tempSpectrum.data; const apodizationOptions = merge( diff --git a/src/component/1d/Line.tsx b/src/component/1d/Line.tsx index 3a2ed3389..1e9437a12 100644 --- a/src/component/1d/Line.tsx +++ b/src/component/1d/Line.tsx @@ -1,10 +1,10 @@ import type { CSSProperties } from 'react'; import { useMemo } from 'react'; +import { SVGPathBuilder } from 'react-science/ui'; import { useScaleChecked } from '../context/ScaleContext.js'; import useActiveSpectrumStyleOptions from '../hooks/useActiveSpectrumStyleOptions.js'; import useXYReduce from '../hooks/useXYReduce.js'; -import { PathBuilder } from '../utility/PathBuilder.js'; import { parseColor } from '../utility/parseColor.js'; import { useInsetOptions } from './inset/InsetProvider.js'; @@ -31,7 +31,7 @@ function Line({ data, id, display, index }: LineProps) { const _scaleX = scaleX(); const _scaleY = scaleY({ spectrumId: id }); - const pathBuilder = new PathBuilder(); + const pathBuilder = new SVGPathBuilder(); if (data?.x && data?.y && _scaleX(0)) { const pathPoints = xyReduce(data); diff --git a/src/component/1d/baseline/BaselinePreview.tsx b/src/component/1d/baseline/BaselinePreview.tsx index 775085f51..078200286 100644 --- a/src/component/1d/baseline/BaselinePreview.tsx +++ b/src/component/1d/baseline/BaselinePreview.tsx @@ -4,6 +4,7 @@ import type { Spectrum1D } from '@zakodium/nmrium-core'; import { isSpectrum1D } from '@zakodium/nmrium-core'; import { xyBaselineCalculation } from 'nmr-processing'; import { useMemo, useRef, useState } from 'react'; +import { SVGPathBuilder } from 'react-science/ui'; import { useChartData } from '../../context/ChartContext.tsx'; import { useFilterSyncOptions } from '../../context/FilterSyncOptionsContext.tsx'; @@ -18,7 +19,6 @@ import { DEFAULT_BASELINE_ALGORITHM, getBaselineValues, } from '../../panels/filtersPanel/Filters/hooks/useBaselineCorrection.tsx'; -import { PathBuilder } from '../../utility/PathBuilder.ts'; import { getMedianWindow } from './getMedianWindow.ts'; import { getMedianY } from './getMedianY.ts'; @@ -227,7 +227,7 @@ function SpectrumPreview({ spectrum, anchors }: SpectrumPreviewProps) { const _scaleX = scaleX(); const _scaleY = scaleY({ spectrumId: activeSpectrum?.id }); - const pathBuilder = new PathBuilder(); + const pathBuilder = new SVGPathBuilder(); if (!x || !y || !_scaleX(0)) return ''; diff --git a/src/component/1d/database/DatabaseSpectrum.tsx b/src/component/1d/database/DatabaseSpectrum.tsx index 4674f56b5..dcef203aa 100644 --- a/src/component/1d/database/DatabaseSpectrum.tsx +++ b/src/component/1d/database/DatabaseSpectrum.tsx @@ -2,6 +2,7 @@ import type { Spectrum1D } from '@zakodium/nmrium-core'; import { extent } from 'd3-array'; import throttle from 'lodash/throttle.js'; import { useCallback, useContext, useEffect, useRef, useState } from 'react'; +import { SVGPathBuilder } from 'react-science/ui'; import { useChartData } from '../../context/ChartContext.js'; import { useCore } from '../../context/CoreContext.js'; @@ -13,7 +14,6 @@ import { } from '../../highlight/index.js'; import { usePanelPreferences } from '../../hooks/usePanelPreferences.js'; import { spinnerContext } from '../../loader/SpinnerContext.js'; -import { PathBuilder } from '../../utility/PathBuilder.js'; import { getYScaleWithRation } from '../utilities/scale.js'; function DatabaseSpectrum() { @@ -61,7 +61,7 @@ function DatabaseSpectrum() { setLoading(false); const spectrum = data?.spectra?.[0] || null; if (spectrum) { - const pathBuilder = new PathBuilder(); + const pathBuilder = new SVGPathBuilder(); const { x, re: y } = (spectrum as Spectrum1D).data; const yDomain = extent(y) as number[]; const finalScaleX = scaleX(); diff --git a/src/component/1d/database/ResurrectedDatabaseRanges.tsx b/src/component/1d/database/ResurrectedDatabaseRanges.tsx index 5df28f1c3..e568cb227 100644 --- a/src/component/1d/database/ResurrectedDatabaseRanges.tsx +++ b/src/component/1d/database/ResurrectedDatabaseRanges.tsx @@ -1,6 +1,7 @@ import type { Spectrum1D } from '@zakodium/nmrium-core'; import { extent } from 'd3-array'; import { rangesToXY } from 'nmr-processing'; +import { SVGPathBuilder } from 'react-science/ui'; import { useChartData } from '../../context/ChartContext.js'; import { useScaleChecked } from '../../context/ScaleContext.js'; @@ -11,7 +12,6 @@ import { import { usePanelPreferences } from '../../hooks/usePanelPreferences.js'; import useSpectrum from '../../hooks/useSpectrum.js'; import { useVerticalAlign } from '../../hooks/useVerticalAlign.js'; -import { PathBuilder } from '../../utility/PathBuilder.js'; import { getYScale } from '../utilities/scale.js'; const emptyData = { info: { originFrequency: 400 } }; @@ -71,7 +71,7 @@ function ResurrectedDatabaseRanges() { const finalScaleX = scaleX(); const paths = spectra.map(({ data: { x, y }, from, to }) => { - const pathBuilder = new PathBuilder(); + const pathBuilder = new SVGPathBuilder(); pathBuilder.moveTo(finalScaleX(x[0]), scaleY(y[0])); for (let i = 1; i < x.length; i++) { pathBuilder.lineTo(finalScaleX(x[i]), scaleY(y[i])); diff --git a/src/component/1d/matrix/Boxplot.tsx b/src/component/1d/matrix/Boxplot.tsx index ac5d1e331..16be219c2 100644 --- a/src/component/1d/matrix/Boxplot.tsx +++ b/src/component/1d/matrix/Boxplot.tsx @@ -4,12 +4,12 @@ import { xFindClosestIndex } from 'ml-spectra-processing'; import { matrixToBoxPlot } from 'nmr-processing'; import type { CSSProperties } from 'react'; import { useMemo } from 'react'; +import { SVGPathBuilder } from 'react-science/ui'; import { useChartData } from '../../context/ChartContext.js'; import { useScaleChecked } from '../../context/ScaleContext.js'; import { useActiveNucleusTab } from '../../hooks/useActiveNucleusTab.js'; import { usePanelPreferences } from '../../hooks/usePanelPreferences.js'; -import { PathBuilder } from '../../utility/PathBuilder.js'; import { getYScaleWithRation } from '../utilities/scale.js'; import { sliceArrayForDomain, useMatrix } from './useMatrix.js'; @@ -64,7 +64,7 @@ function usePath( const { scaleX } = useScaleChecked(); const scaleY = useYScale(scaleRatio, yDomain); - const pathBuilder = new PathBuilder(); + const pathBuilder = new SVGPathBuilder(); const xScaler = scaleX(); @@ -83,8 +83,8 @@ function useAreaPath( const { scaleX } = useScaleChecked(); const scaleY = useYScale(scaleRatio, yDomain); - const pathBuilder = new PathBuilder(); - const pathBuilder2 = new PathBuilder(); + const pathBuilder = new SVGPathBuilder(); + const pathBuilder2 = new SVGPathBuilder(); const xScaler = scaleX(); @@ -97,7 +97,7 @@ function useAreaPath( pathBuilder2.lineTo(xScaler(pathPoints.x[i]), scaleY(pathPoints.y2[i])); } - return pathBuilder.concatPath(pathBuilder2); + return SVGPathBuilder.concat(pathBuilder, pathBuilder2).toString(); } function useBoxPlot() { diff --git a/src/component/1d/matrix/Stocsy.tsx b/src/component/1d/matrix/Stocsy.tsx index 3e30d6ac9..0e8ae3e8e 100644 --- a/src/component/1d/matrix/Stocsy.tsx +++ b/src/component/1d/matrix/Stocsy.tsx @@ -2,12 +2,12 @@ import { extent } from 'd3-array'; import { xFindClosestIndex } from 'ml-spectra-processing'; import { matrixToStocsy } from 'nmr-processing'; import { useMemo } from 'react'; +import { SVGPathBuilder } from 'react-science/ui'; import { useChartData } from '../../context/ChartContext.js'; import { useScaleChecked } from '../../context/ScaleContext.js'; import { useActiveNucleusTab } from '../../hooks/useActiveNucleusTab.js'; import { usePanelPreferences } from '../../hooks/usePanelPreferences.js'; -import { PathBuilder } from '../../utility/PathBuilder.js'; import { getYScaleWithRation } from '../utilities/scale.js'; import { @@ -162,7 +162,7 @@ function RenderStocsyAsSVG(props: StocsyProps) { return Object.keys(colorGroups).map((color) => { const points = colorGroups[color]; - const pathBuilder = new PathBuilder(); + const pathBuilder = new SVGPathBuilder(); pathBuilder.moveTo(xScaler(points[0].x), scaleY(points[0].y)); diff --git a/src/component/1d/peaks/usePeakShapesPath.ts b/src/component/1d/peaks/usePeakShapesPath.ts index 544fd5dcd..bd129c7ec 100644 --- a/src/component/1d/peaks/usePeakShapesPath.ts +++ b/src/component/1d/peaks/usePeakShapesPath.ts @@ -2,10 +2,10 @@ import type { Peak1D } from '@zakodium/nmr-types'; import type { Spectrum1D } from '@zakodium/nmrium-core'; import type { DataXY } from 'cheminfo-types'; import { peakToXY, peaksToXY } from 'nmr-processing'; +import { SVGPathBuilder } from 'react-science/ui'; import { useChartData } from '../../context/ChartContext.js'; import { useScaleChecked } from '../../context/ScaleContext.js'; -import { PathBuilder } from '../../utility/PathBuilder.js'; type PeaksShapesOptions = | { @@ -56,7 +56,7 @@ export function usePeakShapesPath(spectrum: Spectrum1D) { const _scaleX = scaleX(); const _scaleY = scaleY({ spectrumId: spectrum.id }); - const pathBuilder = new PathBuilder(); + const pathBuilder = new SVGPathBuilder(); let fill = 'transparent'; if (pathSeries) { diff --git a/src/component/2d/1d-tracer/HorizontalSliceChart.tsx b/src/component/2d/1d-tracer/HorizontalSliceChart.tsx index ff2ee9269..c953489a6 100644 --- a/src/component/2d/1d-tracer/HorizontalSliceChart.tsx +++ b/src/component/2d/1d-tracer/HorizontalSliceChart.tsx @@ -1,9 +1,9 @@ import type { Spectrum2D } from '@zakodium/nmrium-core'; import type { NmrData1D } from 'cheminfo-types'; +import { SVGPathBuilder } from 'react-science/ui'; import { useChartData } from '../../context/ChartContext.js'; import useSpectrum from '../../hooks/useSpectrum.js'; -import { PathBuilder } from '../../utility/PathBuilder.js'; import { getSliceYScale, useScale2DX } from '../utilities/scale.js'; interface BaseProps { @@ -32,7 +32,7 @@ function usePath(data: NmrData1D, options: UsePathOptions) { margin: verticalMargin, }); - const pathBuilder = new PathBuilder(); + const pathBuilder = new SVGPathBuilder(); pathBuilder.moveTo(scaleX(x[0]), scaleY(y[0])); for (let i = 1; i < x.length; i++) { pathBuilder.lineTo(scaleX(x[i]), scaleY(y[i])); diff --git a/src/component/2d/1d-tracer/Left1DChart.tsx b/src/component/2d/1d-tracer/Left1DChart.tsx index 28743654b..8eed3837b 100644 --- a/src/component/2d/1d-tracer/Left1DChart.tsx +++ b/src/component/2d/1d-tracer/Left1DChart.tsx @@ -1,10 +1,10 @@ import type { Spectrum1D } from '@zakodium/nmrium-core'; import { memo, useRef } from 'react'; +import { SVGPathBuilder } from 'react-science/ui'; import { Signals1D } from '../../1d-2d/components/Signals1D.tsx'; import { useChartData } from '../../context/ChartContext.js'; import useXYReduce from '../../hooks/useXYReduce.js'; -import { PathBuilder } from '../../utility/PathBuilder.js'; import { use1DTraceYScale, useScale2DY } from '../utilities/scale.js'; import { Ranges1D } from './Ranges1D.tsx'; @@ -28,7 +28,7 @@ function usePath(spectrum: Spectrum1D, options: UsePathOptions) { const { x, re: y } = spectrum.data; const pathPoints = xyReduce({ x, y }); - const pathBuilder = new PathBuilder(); + const pathBuilder = new SVGPathBuilder(); pathBuilder.moveTo( scaleY(pathPoints.y.at(-1) as number), diff --git a/src/component/2d/1d-tracer/Top1DChart.tsx b/src/component/2d/1d-tracer/Top1DChart.tsx index 1416ad1c3..294472aa9 100644 --- a/src/component/2d/1d-tracer/Top1DChart.tsx +++ b/src/component/2d/1d-tracer/Top1DChart.tsx @@ -1,10 +1,10 @@ import type { Spectrum1D } from '@zakodium/nmrium-core'; import { memo, useRef } from 'react'; +import { SVGPathBuilder } from 'react-science/ui'; import { Signals1D } from '../../1d-2d/components/Signals1D.tsx'; import { useChartData } from '../../context/ChartContext.js'; import useXYReduce from '../../hooks/useXYReduce.js'; -import { PathBuilder } from '../../utility/PathBuilder.js'; import { use1DTraceYScale, useScale2DX } from '../utilities/scale.js'; import { Ranges1D } from './Ranges1D.tsx'; @@ -28,7 +28,7 @@ function usePath(spectrum: Spectrum1D, options: UsePathOptions) { const { x, re: y } = spectrum.data; const pathPoints = xyReduce({ x, y }); - const pathBuilder = new PathBuilder(); + const pathBuilder = new SVGPathBuilder(); pathBuilder.moveTo(scaleX(pathPoints.x[0]), scaleY(pathPoints.y[0])); for (let i = 1; i < pathPoints.x.length; i++) { pathBuilder.lineTo(scaleX(pathPoints.x[i]), scaleY(pathPoints.y[i])); diff --git a/src/component/2d/1d-tracer/VerticalSliceChart.tsx b/src/component/2d/1d-tracer/VerticalSliceChart.tsx index ef856e77c..0fabb517b 100644 --- a/src/component/2d/1d-tracer/VerticalSliceChart.tsx +++ b/src/component/2d/1d-tracer/VerticalSliceChart.tsx @@ -1,9 +1,9 @@ import type { Spectrum2D } from '@zakodium/nmrium-core'; import type { NmrData1D } from 'cheminfo-types'; +import { SVGPathBuilder } from 'react-science/ui'; import { useChartData } from '../../context/ChartContext.js'; import useSpectrum from '../../hooks/useSpectrum.js'; -import { PathBuilder } from '../../utility/PathBuilder.js'; import { getSliceYScale, useScale2DY } from '../utilities/scale.js'; interface BaseProps { @@ -35,7 +35,7 @@ function usePath(data: NmrData1D, props: usePathOptions) { margin: horizontalMargin, }); - const pathBuilder = new PathBuilder(); + const pathBuilder = new SVGPathBuilder(); pathBuilder.moveTo(scaleY(y.at(-1) as number), scaleX(x.at(-1) as number)); diff --git a/src/component/2d/1d-tracer/phase-correction-traces/SpectrumPhaseTrace.tsx b/src/component/2d/1d-tracer/phase-correction-traces/SpectrumPhaseTrace.tsx index 5016a9be8..74deb33f0 100644 --- a/src/component/2d/1d-tracer/phase-correction-traces/SpectrumPhaseTrace.tsx +++ b/src/component/2d/1d-tracer/phase-correction-traces/SpectrumPhaseTrace.tsx @@ -1,13 +1,13 @@ import type { Spectrum1D, Spectrum2D } from '@zakodium/nmrium-core'; import { Filters1D } from 'nmr-processing'; import type { ReactNode, SVGAttributes } from 'react'; +import { SVGPathBuilder } from 'react-science/ui'; import { getSlice } from '../../../../data/data2d/Spectrum2D/index.js'; import { useChartData } from '../../../context/ChartContext.js'; import { useActiveSpectrum } from '../../../hooks/useActiveSpectrum.js'; import useSpectrum from '../../../hooks/useSpectrum.js'; import type { TraceDirection } from '../../../reducer/Reducer.js'; -import { PathBuilder } from '../../../utility/PathBuilder.js'; import { get2DXScale, get2DYScale, @@ -43,7 +43,7 @@ function usePath(x: Float64Array, y: Float64Array, direction: TraceDirection) { scaleRatio, }); - const pathBuilder = new PathBuilder(); + const pathBuilder = new SVGPathBuilder(); pathBuilder.moveTo(scaleX(x[0]), scaleY(y[0])); for (let i = 1; i < x.length; i++) { pathBuilder.lineTo(scaleX(x[i]), scaleY(y[i])); @@ -58,7 +58,7 @@ function usePath(x: Float64Array, y: Float64Array, direction: TraceDirection) { scaleRatio, }); - const pathBuilder = new PathBuilder(); + const pathBuilder = new SVGPathBuilder(); pathBuilder.moveTo(scaleY(y.at(-1) as number), scaleX(x.at(-1) as number)); diff --git a/src/component/2d/ft/Contours.tsx b/src/component/2d/ft/Contours.tsx index da6a10bde..803f04729 100644 --- a/src/component/2d/ft/Contours.tsx +++ b/src/component/2d/ft/Contours.tsx @@ -1,5 +1,6 @@ import debounce from 'lodash/debounce.js'; import { memo, useMemo, useRef } from 'react'; +import { SVGPathBuilder } from 'react-science/ui'; import type { LevelSign } from '../../../data/data2d/Spectrum2D/contours.js'; import { drawContours } from '../../../data/data2d/Spectrum2D/contours.js'; @@ -12,7 +13,6 @@ import { useToaster } from '../../context/ToasterContext.js'; import type { SpectrumFTData } from '../../hooks/use2DReducer.tsx'; import { use2DReducer } from '../../hooks/use2DReducer.tsx'; import { useActiveSpectrum } from '../../hooks/useActiveSpectrum.js'; -import { PathBuilder } from '../../utility/PathBuilder.js'; import { useScale2DX, useScale2DY } from '../utilities/scale.js'; interface SpectrumContoursProps { @@ -33,7 +33,7 @@ function usePath(contours: ReturnType['contours']) { const scaleX = useScale2DX(); const scaleY = useScale2DY(); - const pathBuilder = new PathBuilder(); + const pathBuilder = new SVGPathBuilder(); for (const element of contours) { if (element.lines) { const lines = element.lines; diff --git a/src/component/hooks/useIntegralPath.ts b/src/component/hooks/useIntegralPath.ts index c4fd05394..08623e0cd 100644 --- a/src/component/hooks/useIntegralPath.ts +++ b/src/component/hooks/useIntegralPath.ts @@ -1,10 +1,10 @@ import type { ScaleLinear } from 'd3-scale'; import { xyReduce } from 'ml-spectra-processing'; +import { SVGPathBuilder } from 'react-science/ui'; import { getIntegralYScale } from '../1d/utilities/scale.js'; import { useChartData } from '../context/ChartContext.js'; import { useScaleChecked } from '../context/ScaleContext.js'; -import { PathBuilder } from '../utility/PathBuilder.js'; import { useXScale } from './useXScale.js'; @@ -48,7 +48,7 @@ export default function useIntegralPath(options: UseIntegralPathOptions) { }, ); - const pathBuilder = new PathBuilder(); + const pathBuilder = new SVGPathBuilder(); pathBuilder.moveTo(scaleX(xySeries.x[0]), scaleY(xySeries.y[0])); for (let i = 1; i < xySeries.x.length; i++) { pathBuilder.lineTo(scaleX(xySeries.x[i]), scaleY(xySeries.y[i])); diff --git a/src/component/utility/PathBuilder.ts b/src/component/utility/PathBuilder.ts deleted file mode 100644 index 16c3b989e..000000000 --- a/src/component/utility/PathBuilder.ts +++ /dev/null @@ -1,41 +0,0 @@ -export class PathBuilder { - private path = ''; - - private appendPath(segment: string) { - this.path += `${segment} `; - } - - public moveTo(x: number, y: number) { - x = clamp(x); - y = clamp(y); - this.appendPath(`M ${x} ${y}`); - } - - public lineTo(x: number, y: number) { - x = clamp(x); - y = clamp(y); - this.appendPath(`L ${x} ${y}`); - } - - public closePath() { - if (this.path.length > 0) this.appendPath('Z'); - } - - public toString() { - return this.path.trim(); - } - - public concatPath(pathBuilder: PathBuilder) { - return (this.path + pathBuilder.path).trim(); - } -} - -/** - * Clamp values to avoid rendering issues of the SVG. - * This assumes that the viewport has pixel coordinates (so the max values are outside of the visible area) - */ -function clamp(value: number) { - if (value < -1e5) return -1e5; - if (value > 1e5) return 1e5; - return value; -} From 100c46276b004af576d2412c4dd74aebed9ddcc9 Mon Sep 17 00:00:00 2001 From: tpoisseau <22891227+tpoisseau@users.noreply.github.com> Date: Thu, 27 Aug 2026 09:43:05 +0200 Subject: [PATCH 3/3] fix: update core --- package-lock.json | 26 +++++++++++++------------- package.json | 4 ++-- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6a14f4fa4..b4ee344be 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,8 +15,8 @@ "@tanstack/react-form": "^1.33.5", "@tanstack/react-store": "^0.11.1", "@zakodium/nmr-types": "^0.5.26", - "@zakodium/nmrium-core": "^0.7.64", - "@zakodium/nmrium-core-plugins": "^0.7.86", + "@zakodium/nmrium-core": "^0.7.65", + "@zakodium/nmrium-core-plugins": "^0.7.87", "@zakodium/pdnd-esm": "^1.1.1", "@zakodium/utils": "^0.5.1", "@zip.js/zip.js": "^2.8.53", @@ -60,7 +60,7 @@ "react-ocl-nmr": "^4.1.1", "react-plot": "^3.1.2", "react-rnd": "^10.5.3", - "react-science": "^23.0.1", + "react-science": "^23.1.1", "react-table": "^7.8.0", "smart-array-filter": "^5.0.0", "ts-pattern": "^5.9.0", @@ -4006,9 +4006,9 @@ } }, "node_modules/@zakodium/nmrium-core": { - "version": "0.7.64", - "resolved": "https://registry.npmjs.org/@zakodium/nmrium-core/-/nmrium-core-0.7.64.tgz", - "integrity": "sha512-ZbaXg5s+x3wqpJxbDYz5kqGFggYJ6ftOWntn4g/YhMoWMxnsw1I5bJrXVr9bi8Lx3tv0SqAOhFXFfI6+k4wRuQ==", + "version": "0.7.65", + "resolved": "https://registry.npmjs.org/@zakodium/nmrium-core/-/nmrium-core-0.7.65.tgz", + "integrity": "sha512-mXXylnGVMbYRdz23kalRziwMK6OCkJkS3HW9izjatH/6/VZgnbiSa5DBm+SazBuNe5QAutrXIFUWa+t2D/5Esw==", "license": "CC-BY-NC-SA-4.0", "dependencies": { "@zakodium/utils": "^0.5.1", @@ -4024,13 +4024,13 @@ } }, "node_modules/@zakodium/nmrium-core-plugins": { - "version": "0.7.86", - "resolved": "https://registry.npmjs.org/@zakodium/nmrium-core-plugins/-/nmrium-core-plugins-0.7.86.tgz", - "integrity": "sha512-YJ0o+5qW4aZa3gpoQ0+FnB0l/CZhQBknMOnP6bl1LgrSk786XBsvBN0/mlwCOGyf6OztlDh5jE3dbV701vi5zA==", + "version": "0.7.87", + "resolved": "https://registry.npmjs.org/@zakodium/nmrium-core-plugins/-/nmrium-core-plugins-0.7.87.tgz", + "integrity": "sha512-g/JwP7tgfndzQAy8xmVBWb0PASoQc/IabMHONC7uZ+juXpYizX/XM31WwNSnmAWlbGVmKHM53xU7F+mCJX2Imw==", "license": "CC-BY-NC-SA-4.0", "dependencies": { "@date-fns/utc": "^2.1.1", - "@zakodium/nmrium-core": "^0.7.64", + "@zakodium/nmrium-core": "^0.7.65", "cheminfo-types": "^1.15.0", "convert-to-jcamp": "^7.0.1", "date-fns": "^4.4.0", @@ -11049,9 +11049,9 @@ } }, "node_modules/react-science": { - "version": "23.0.1", - "resolved": "https://registry.npmjs.org/react-science/-/react-science-23.0.1.tgz", - "integrity": "sha512-pT9164/dHPFkNVFhj0HUgNfMK6GJVY3jPca8v/2na6AXmxcHDOQfFvrXGTlB9z0zB9GiEzx/LD0Pd6SQ8X3NXA==", + "version": "23.1.1", + "resolved": "https://registry.npmjs.org/react-science/-/react-science-23.1.1.tgz", + "integrity": "sha512-rvTOyjg2qYFAcZTKdeXiK8fQadbuqqtSyab30eOl2QKPkEqbsD5ZgYBJBTf+neKl450+zVFJk/4VSyF2W9RtHg==", "license": "MIT", "dependencies": { "@emotion/styled": "^11.14.1", diff --git a/package.json b/package.json index 8397f0dd2..9d9a54cad 100644 --- a/package.json +++ b/package.json @@ -66,8 +66,8 @@ "@tanstack/react-form": "^1.33.5", "@tanstack/react-store": "^0.11.1", "@zakodium/nmr-types": "^0.5.26", - "@zakodium/nmrium-core": "^0.7.64", - "@zakodium/nmrium-core-plugins": "^0.7.86", + "@zakodium/nmrium-core": "^0.7.65", + "@zakodium/nmrium-core-plugins": "^0.7.87", "@zakodium/pdnd-esm": "^1.1.1", "@zakodium/utils": "^0.5.1", "@zip.js/zip.js": "^2.8.53",