Skip to content

Commit

Permalink
ui-spacetimechart: simple rectangle
Browse files Browse the repository at this point in the history
refactor PatternRect to make it use SimpleRect using a composition

Signed-off-by: Valentin Chanas <[email protected]>
  • Loading branch information
anisometropie committed Mar 5, 2025
1 parent e40a486 commit dd56c9c
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 39 deletions.
48 changes: 10 additions & 38 deletions ui-charts/src/spaceTimeChart/components/PatternRect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,55 +2,27 @@ import { useCallback } from 'react';

import { useDraw } from '../hooks/useCanvas';
import { type DrawingFunction } from '../lib/types';
import { fillRect, type CanvasRect } from '../utils/canvas';

export type PatternRectProps = {
timeStart: Date;
timeEnd: Date;
spaceStart: number; // mm
spaceEnd: number; // mm
export type PatternRectProps = CanvasRect & {
imageElement: HTMLImageElement;
};

/**
* draws a repeating pattern in the space time chart
*/
export const PatternRect = ({
timeStart,
timeEnd,
spaceStart,
spaceEnd,
imageElement,
}: PatternRectProps) => {
const drawRegion = useCallback<DrawingFunction>(
(ctx, { getSpacePixel, getTimePixel, spaceAxis }) => {
const timeStartPixel = getTimePixel(Number(timeStart));
const endTimePixel = getTimePixel(Number(timeEnd));
const spaceStartPixel = getSpacePixel(spaceStart);
const spaceEndPixel = getSpacePixel(spaceEnd);

const areaSpaceSize = spaceEndPixel - spaceStartPixel;
const areaTimeSize = endTimePixel - timeStartPixel;
if (!areaSpaceSize || !areaTimeSize) return;

export const PatternRect = ({ imageElement, ...rect }: PatternRectProps) => {
const drawPatternRect = useCallback<DrawingFunction>(
(ctx, context) => {
const pattern = ctx.createPattern(imageElement, 'repeat');
if (!pattern) {
return;
}

ctx.save();
ctx.fillStyle = pattern;
if (spaceAxis === 'x') {
ctx.translate(spaceStartPixel, timeStartPixel);
ctx.fillRect(0, 0, areaSpaceSize, areaTimeSize);
} else {
ctx.translate(timeStartPixel, spaceStartPixel);
ctx.fillRect(0, 0, areaTimeSize, areaSpaceSize);
if (pattern) {
ctx.fillStyle = pattern;
fillRect(ctx, rect, context);
}
ctx.restore();
},
[timeStart, timeEnd, spaceStart, spaceEnd, imageElement]
[imageElement, rect]
);
useDraw('background', drawRegion);
useDraw('background', drawPatternRect);

return null;
};
53 changes: 53 additions & 0 deletions ui-charts/src/spaceTimeChart/components/ZoomRect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { useCallback } from 'react';

import { useDraw } from '../hooks/useCanvas';
import { type DrawingFunction } from '../lib/types';
import { fillRect, type CanvasRect } from '../utils/canvas';

/**
* radius 1 black dot with radius 3 white region around it
*/
function squareDot(ctx: CanvasRenderingContext2D, cx: number, cy: number) {
ctx.save();
ctx.fillStyle = 'white';
ctx.fillRect(cx - 1, cy - 1, 3, 3);
ctx.fill();

ctx.fillStyle = 'black';
ctx.fillRect(cx, cy, 1, 1);
ctx.fill();
ctx.restore();
}

const LINE_WIDTH = 1;
const SPACING = 4;

export const ZoomRect = (rect: CanvasRect) => {
const drawZoomRect = useCallback<DrawingFunction>(
(ctx, context) => {
ctx.save();
ctx.fillStyle = '#0000000D'; /* black5 */
const { areaTimeSize, areaSpaceSize } = fillRect(ctx, rect, context);
if (areaTimeSize && areaSpaceSize) {
const width = context.timeAxis === 'x' ? areaTimeSize : areaSpaceSize;
const height = context.timeAxis === 'x' ? areaSpaceSize : areaTimeSize;

ctx.lineWidth = LINE_WIDTH;

for (let i = 0; Math.abs(i) < Math.abs(width); i += SPACING * Math.sign(width)) {
squareDot(ctx, i, 0);
squareDot(ctx, i, 0 + height);
}
for (let i = 0; Math.abs(i) < Math.abs(height); i += SPACING * Math.sign(height)) {
squareDot(ctx, 0, i);
squareDot(ctx, 0 + width, i);
}
}
ctx.restore();
},
[rect]
);
useDraw('background', drawZoomRect);

return null;
};
43 changes: 42 additions & 1 deletion ui-charts/src/spaceTimeChart/utils/canvas.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import { clamp, identity } from 'lodash';

import type { Direction, PathEnd, Point, RGBAColor, RGBColor } from '../lib/types';
import type {
SpaceTimeChartContextType,
Direction,
PathEnd,
Point,
RGBAColor,
RGBColor,
} from '../lib/types';

/**
* This function draws a thick lines from "from" to "to" on the given ImageData, with no
Expand Down Expand Up @@ -331,3 +338,37 @@ export function getCrispLineCoordinate(
Math.round((rawCoordinate - centerOffset) * devicePixelRatio) / devicePixelRatio + centerOffset
);
}

export type CanvasRect = {
timeStart: Date;
timeEnd: Date;
spaceStart: number; // mm
spaceEnd: number; // mm
};

export function fillRect(
ctx: CanvasRenderingContext2D,
rect: CanvasRect,
spaceTimeContext: SpaceTimeChartContextType
) {
const { getTimePixel, getSpacePixel, timeAxis } = spaceTimeContext;
const { timeStart, timeEnd, spaceStart, spaceEnd } = rect;

const timeStartPixel = getTimePixel(Number(timeStart));
const endTimePixel = getTimePixel(Number(timeEnd));
const spaceStartPixel = getSpacePixel(spaceStart);
const spaceEndPixel = getSpacePixel(spaceEnd);

const areaSpaceSize = spaceEndPixel - spaceStartPixel;
const areaTimeSize = endTimePixel - timeStartPixel;
if (!areaSpaceSize || !areaTimeSize) return {};

if (timeAxis === 'x') {
ctx.translate(timeStartPixel, spaceStartPixel);
ctx.fillRect(0, 0, areaTimeSize, areaSpaceSize);
} else {
ctx.translate(spaceStartPixel, timeStartPixel);
ctx.fillRect(0, 0, areaSpaceSize, areaTimeSize);
}
return { areaTimeSize, areaSpaceSize };
}

0 comments on commit dd56c9c

Please sign in to comment.