Skip to content

Commit

Permalink
canvas border
Browse files Browse the repository at this point in the history
  • Loading branch information
anisometropie committed Feb 21, 2025
1 parent 94af9c2 commit 0fa1536
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 141 deletions.
60 changes: 21 additions & 39 deletions ui-spacetimechart/src/components/CanvasRect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,18 @@ export type FillStyle =
| CanvasGradient
| ((ctx: CanvasRenderingContext2D) => string | CanvasPattern | CanvasGradient | null);

export type CanvasProps = {
export type CanvasRectProps = {
timeStart: Date;
timeEnd: Date;
spaceStart: number; // mm
spaceEnd: number; // mm
fillStyle: FillStyle;
border?: boolean;
drawBorder?: (
ctx: CanvasRenderingContext2D,
timeAxis: string,
timePixelSize: number,
spacePixelSize: number
) => void;
};

export const CanvasRect = ({
Expand All @@ -24,11 +29,11 @@ export const CanvasRect = ({
spaceStart,
spaceEnd,
fillStyle,
border,
}: CanvasProps) => {
drawBorder,
}: CanvasRectProps) => {
const overlayRef = useRef<HTMLDivElement>(null);

Check warning on line 34 in ui-spacetimechart/src/components/CanvasRect.tsx

View workflow job for this annotation

GitHub Actions / build

'overlayRef' is assigned a value but never used

Check warning on line 34 in ui-spacetimechart/src/components/CanvasRect.tsx

View workflow job for this annotation

GitHub Actions / build

'overlayRef' is assigned a value but never used
const drawRegion = useCallback<DrawingFunction>(
(ctx, { getSpacePixel, getTimePixel, spaceAxis }) => {
(ctx, { getSpacePixel, getTimePixel, timeAxis }) => {
const timeStartPixel = getTimePixel(Number(timeStart));
const endTimePixel = getTimePixel(Number(timeEnd));
const spaceStartPixel = getSpacePixel(spaceStart);
Expand All @@ -48,46 +53,23 @@ export const CanvasRect = ({
} else {
ctx.fillStyle = fillStyle;
}
if (spaceAxis === 'x') {
ctx.translate(spaceStartPixel, timeStartPixel);
ctx.fillRect(0, 0, areaSpaceSize, areaTimeSize);
} else {

if (timeAxis === 'x') {
ctx.translate(timeStartPixel, spaceStartPixel);
ctx.fillRect(0, 0, areaTimeSize, areaSpaceSize);
} else {
ctx.translate(spaceStartPixel, timeStartPixel);
ctx.fillRect(0, 0, areaSpaceSize, areaTimeSize);
}
ctx.restore();
if (border && overlayRef.current) {
let borderSpaceSize, borderTimeSize, borderSpaceStart, borderTimeStart;
if (areaSpaceSize >= 0) {
borderSpaceSize = areaSpaceSize;
borderSpaceStart = spaceStartPixel;
} else {
borderSpaceSize = -areaSpaceSize;
borderSpaceStart = spaceStartPixel + areaSpaceSize;
}
if (areaTimeSize >= 0) {
borderTimeSize = areaTimeSize;
borderTimeStart = timeStartPixel;
} else {
borderTimeSize = -areaTimeSize;
borderTimeStart = timeStartPixel + areaTimeSize;
}
if (spaceAxis === 'x') {
overlayRef.current.style.left = `${borderSpaceStart}px`;
overlayRef.current.style.top = `${borderTimeStart}px`;
overlayRef.current.style.width = `${borderSpaceSize}px`;
overlayRef.current.style.height = `${borderTimeSize}px`;
} else {
overlayRef.current.style.left = `${borderTimeStart}px`;
overlayRef.current.style.top = `${borderSpaceStart}px`;
overlayRef.current.style.width = `${borderTimeSize}px`;
overlayRef.current.style.height = `${borderSpaceSize}px`;
}

if (drawBorder) {
drawBorder(ctx, timeAxis, areaTimeSize, areaSpaceSize);
}
ctx.restore();
},
[timeStart, timeEnd, spaceStart, spaceEnd, fillStyle, border]
[timeStart, timeEnd, spaceStart, spaceEnd, fillStyle, drawBorder]
);
useDraw('background', drawRegion);

return <div ref={overlayRef} className="simple-rect-border-box" />;
return null;
};
62 changes: 62 additions & 0 deletions ui-spacetimechart/src/components/DottedBorderRect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React from 'react';

import { CanvasRect, type CanvasRectProps } from './CanvasRect';

export type DottedBorderRectProps = CanvasRectProps & {
lineWidth: number;
spacing: number;
};

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

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

export const DottedBorderRect = ({
timeStart,
timeEnd,
spaceStart,
spaceEnd,
fillStyle,
lineWidth,
spacing,
}: DottedBorderRectProps) => {
const drawBorder = (
ctx: CanvasRenderingContext2D,
timeAxis: string,
timePixelSize: number,
spacePixelSize: number
) => {
const width = timeAxis === 'x' ? timePixelSize : spacePixelSize;
const height = timeAxis === 'x' ? spacePixelSize : timePixelSize;

ctx.lineWidth = lineWidth;

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);
}
};
return (
<CanvasRect
timeStart={timeStart}
timeEnd={timeEnd}
spaceStart={spaceStart}
spaceEnd={spaceEnd}
fillStyle={fillStyle}
drawBorder={drawBorder}
/>
);
};
7 changes: 4 additions & 3 deletions ui-spacetimechart/src/stories/draw-rectangle.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import { clamp } from 'lodash';
import '@osrd-project/ui-core/dist/theme.css';

import { OPERATIONAL_POINTS, PATHS } from './lib/paths';
import { DottedBorderRect } from '../components/DottedBorderRect';
import { PathLayer } from '../components/PathLayer';
import { CanvasRect } from '../components/SimpleRect';
import { SpaceTimeChart } from '../components/SpaceTimeChart';
import { type Point, type PathData, type OperationalPoint } from '../lib/types';
import { getDiff } from '../utils/vectors';
Expand Down Expand Up @@ -174,13 +174,14 @@ const DrawRectangleWrapper = ({
<PathLayer key={path.id} path={path} color={path.color} />
))}
{state.rect && (
<CanvasRect
<DottedBorderRect
timeStart={state.rect.timeStart}
timeEnd={state.rect.timeEnd}
spaceStart={state.rect.spaceStart}
spaceEnd={state.rect.spaceEnd}
lineWidth={1}
spacing={4}
fillStyle={'#0000000D'} /* black5 */
border
/>
)}
<MouseTracker />
Expand Down
99 changes: 0 additions & 99 deletions ui-spacetimechart/src/styles/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@
@import 'tailwindcss/components';
@import 'tailwindcss/utilities';

.space-time-chart {
overflow: hidden;
}

.spacetimechart-tooltip {
position: absolute;
background-color: rgba(0, 0, 0, 0.85);
Expand Down Expand Up @@ -50,98 +46,3 @@
text-overflow: ellipsis;
}
}

.simple-rect-border-box {
position: relative;
border: none; /* Remove default border */
}

.simple-rect-border-box::after {
content: "";
position: absolute;
inset: 0;
background:
/* TOP BORDER */
repeating-linear-gradient(
90deg,
theme('colors.primary.90') 0px,
theme('colors.primary.90') 1px,
transparent 1px,
transparent 4px
)
top 1px left 1px / 100% 1px no-repeat,
/* BOTTOM BORDER */
repeating-linear-gradient(
0deg,
theme('colors.primary.90') 0px,
theme('colors.primary.90') 1px,
transparent 1px,
transparent 4px
)
bottom 1px right 1px / 1px 100% no-repeat,
/* BOTTOM BORDER */
repeating-linear-gradient(
90deg,
theme('colors.primary.90') 0px,
theme('colors.primary.90') 1px,
transparent 1px,
transparent 4px
)
bottom 1px left 1px / 100% 1px no-repeat,
/* LEFT BORDER */
repeating-linear-gradient(
0deg,
theme('colors.primary.90') 0px,
theme('colors.primary.90') 1px,
transparent 1px,
transparent 4px
)
left 1px bottom 1px / 1px 100% no-repeat;
content: "";
pointer-events: none;
}

.simple-rect-border-box::before {
content: "";
position: absolute;
inset: 0;
background:
/* TOP BORDER */
repeating-linear-gradient(
90deg,
theme('colors.white.100') 0px,
theme('colors.white.100') 3px,
transparent 3px,
transparent 4px
)
top / 100% 3px no-repeat,
/* RIGHT BORDER */
repeating-linear-gradient(
0deg,
theme('colors.white.100') 0px,
theme('colors.white.100') 3px,
transparent 3px,
transparent 4px
)
right / 3px 100% no-repeat,
/* BOTTOM BORDER */
repeating-linear-gradient(
90deg,
theme('colors.white.100') 0px,
theme('colors.white.100') 3px,
transparent 3px,
transparent 4px
)
bottom / 100% 3px no-repeat,
/* LEFT BORDER */
repeating-linear-gradient(
0deg,
theme('colors.white.100') 0px,
theme('colors.white.100') 3px,
transparent 3px,
transparent 4px
)
left / 3px 100% no-repeat;
content: "";
pointer-events: none;
}

0 comments on commit 0fa1536

Please sign in to comment.