Skip to content

Commit

Permalink
adicionado zoom
Browse files Browse the repository at this point in the history
  • Loading branch information
Piemontez committed May 2, 2024
1 parent eec5218 commit 808ba91
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 62 deletions.
2 changes: 2 additions & 0 deletions src/ide/commons/consts.ts
@@ -1,2 +1,4 @@
export const STORAGE_NODESTORE_ID = 'NodeStore';
export const STORAGE_DARK_MODE = 'color_mode';
export const ZOOM_BOX_SIZE = 50;
export const ZOOM_BOX_SIZE_HALF = 50 / 2;
147 changes: 101 additions & 46 deletions src/ide/components/NodeComponent/index.tsx
@@ -1,29 +1,33 @@
import cv, { Mat } from 'opencv-ts';
import React from 'react';
import { Handle, Position } from 'reactflow';
import NodeDisplay from '../NodeDisplay';
import NodeDisplay, { NodeZoom } from '../NodeDisplay';
import NodeTab from '../NodeTab';
import { NodeSizes } from '../../../core/config/sizes';
import GCStore from '../../../core/contexts/GCStore';
import { SourceHandle, TargetHandle } from '../../../core/types/handle';
import { ComponentMenuAction, MenuWithElementTitleProps } from '../../types/menu';
import { CVFNodeData, CVFNodeProcessor } from '../../../core/types/node';
import { Zoom } from '../../types/Zoom';
import { ZOOM_BOX_SIZE, ZOOM_BOX_SIZE_HALF } from '../../commons/consts';

type OCVComponentProps = {
id: string;
data: CVFNodeData;
type: string;
};

type ZoomIn = {
x: number;
y: number;
width: number;
};
type OCVComponentState = {
zoom: number | 'AUTO_SCALE';
scale: number | 'AUTO_SCALE';
zoomIn?: ZoomIn;
options: number;
};

class ForkCVFNodeProcessor extends CVFNodeProcessor {}
type OCVComponentProcessor = typeof ForkCVFNodeProcessor;

export enum CVFComponentOptions {
NONE = 0,
NOT_DISPLAY = 1,
Expand All @@ -32,12 +36,15 @@ export enum CVFComponentOptions {
}

class EmptyNodeProcessor extends CVFNodeProcessor {}
class ForkCVFNodeProcessor extends CVFNodeProcessor {}
type OCVComponentProcessor = typeof ForkCVFNodeProcessor;

/**
* Componente/NodeType
*/
export abstract class CVFComponent extends React.Component<OCVComponentProps, OCVComponentState> {
private output: HTMLCanvasElement | null = null;
private canvasRef: HTMLCanvasElement | null = null;
private canvasZoomRef: HTMLCanvasElement | null = null;
// Conexões que o componente pode receber
targets: TargetHandle[] = [];
// Conexões que o componente irá disparar
Expand All @@ -48,7 +55,8 @@ export abstract class CVFComponent extends React.Component<OCVComponentProps, OC
static menu?: ComponentMenuAction;
// Opções
state = {
zoom: Zoom.PERC_AUTO as Zoom,
scale: Zoom.PERC_AUTO as Zoom,
zoomIn: undefined as ZoomIn | undefined,
options: CVFComponentOptions.NONE,
};

Expand All @@ -58,10 +66,26 @@ export abstract class CVFComponent extends React.Component<OCVComponentProps, OC
return (menu as MenuWithElementTitleProps)?.name || (menu?.title as string) || this.constructor.name;
}

changeZoom(zoom: Zoom) {
this.setState({ zoom });
changeScale(scale: Zoom) {
this.setState({ scale });
}

showZoom(x: number, y: number) {
const { left, top, width } = this.canvasRef!.getBoundingClientRect();

x = x - Math.floor(left);
y = y - Math.floor(top);
this.setState({
zoomIn: { x, y, width: Math.floor(width) },
});
}

hideZoom = () => {
this.setState({
zoomIn: undefined,
});
};

addOption(opt: number) {
const { options } = this.state;
this.setState({
Expand All @@ -86,53 +110,71 @@ export abstract class CVFComponent extends React.Component<OCVComponentProps, OC

processor.componentPointer = { current: this };

processor.output = (mat: Mat) => {
if (this.output) {
const { zoom, options } = this.state;
const notDisplay = options & CVFComponentOptions.NOT_DISPLAY;
if (notDisplay) {
return;
}
processor.output = this.output;
processor.outputMsg = this.outputMsg;
}

output = (mat: Mat) => {
if (this.canvasRef) {
const { scale: zoom, options } = this.state;
const notDisplay = options & CVFComponentOptions.NOT_DISPLAY;
if (notDisplay) {
return;
}

let out: Mat;
if (Zoom.PERC_100 === zoom) {
out = mat;
let out: Mat;
if (Zoom.PERC_100 === zoom) {
out = mat;
} else {
let cols;
let rows;
if (zoom === 'AUTO_SCALE') {
const min = Math.min(mat.rows, mat.cols);
const scale = NodeSizes.defaultHeight / min;
cols = mat.cols * scale;
rows = mat.rows * scale;
} else {
let cols;
let rows;
if (zoom === 'AUTO_SCALE') {
const min = Math.min(mat.rows, mat.cols);
const scale = NodeSizes.defaultHeight / min;
cols = mat.cols * scale;
rows = mat.rows * scale;
} else {
rows = mat.rows * zoom;
cols = mat.cols * zoom;
}

out = new cv.Mat(rows, cols, mat.type());
GCStore.add(out);
cv.resize(mat, out, new cv.Size(cols, rows), 0, 0, cv.INTER_NEAREST);
rows = mat.rows * zoom;
cols = mat.cols * zoom;
}

cv.imshow(this.output, out);
out = GCStore.add(new cv.Mat(rows, cols, mat.type()));
cv.resize(mat, out, new cv.Size(cols, rows), 0, 0, cv.INTER_NEAREST);
}
};
cv.imshow(this.canvasRef, out);

processor.outputMsg = (msg: string) => {
if (this.output) {
const ctx = this.output.getContext('2d');
if (ctx) {
ctx.fillText(msg, 15, 15);
}
this.outputZoom(mat);
}
};

outputZoom = (mat: Mat) => {
if (this.canvasZoomRef && this.state.zoomIn) {
let { x, y, width } = this.state.zoomIn;
const scale = mat.cols / width;

x = Math.max(Math.min(x * scale + ZOOM_BOX_SIZE_HALF, mat.rows) - ZOOM_BOX_SIZE_HALF, 0);
y = Math.max(Math.min(y * scale + ZOOM_BOX_SIZE_HALF, mat.rows) - ZOOM_BOX_SIZE_HALF, 0);

const zoom = GCStore.add(mat.roi(new cv.Rect(x, y, ZOOM_BOX_SIZE, ZOOM_BOX_SIZE)));
const resize = GCStore.add(new cv.Mat());
cv.resize(zoom, resize, new cv.Size(NodeSizes.defaultWidth, NodeSizes.defaultHeight), 0, 0, cv.INTER_NEAREST);
cv.imshow(this.canvasZoomRef, resize);
}
};

outputMsg = (msg: string) => {
if (this.canvasRef) {
const ctx = this.canvasRef.getContext('2d');
if (ctx) {
ctx.fillText(msg, 15, 15);
}
};
}
}
};

render() {
const { zoomIn } = this.state;
const { data } = this.props;
const { processor } = data;

return (
<div className="node">
{processor.header()}
Expand All @@ -155,7 +197,20 @@ export abstract class CVFComponent extends React.Component<OCVComponentProps, OC

<NodeTab component={this} />

<div className="node-body">{processor.body() || <NodeDisplay component={this} canvasRef={(ref) => (this.output = ref)} />}</div>
<div className="node-body">
{!!zoomIn && (
<NodeZoom canvasRef={(ref) => (this.canvasZoomRef = ref)} pos={zoomIn} scale={(this.canvasRef?.width || 1) / (zoomIn?.width || 1)} />
)}

{processor.body() || (
<NodeDisplay
component={this}
canvasRef={(ref) => (this.canvasRef = ref)}
onMouseMove={(ev) => this.showZoom(ev.clientX, ev.clientY)}
onMouseLeave={this.hideZoom}
/>
)}
</div>

{this.sources.map((source, idx) => (
<Handle id={source.title} key={source.title} type="source" position={source.position} style={{ top: 40 + 40 * idx, borderRadius: 0 }}>
Expand Down
44 changes: 30 additions & 14 deletions src/ide/components/NodeDisplay/index.tsx
@@ -1,25 +1,41 @@
import { CVFComponent } from '../NodeComponent';
import { NodeSizes } from '../../../core/config/sizes';
import { MouseEventHandler } from 'react';

const NodeDisplay = ({
canvasRef,
component,
}: {
type NodeDisplayProps = {
canvasRef: (ref: HTMLCanvasElement) => void;
component: CVFComponent;
}) => {
const { data } = component.props;
const { processor } = data;
if (processor.errorMessage) {
return <div className="node-error-msg">{processor.errorMessage}</div>;
onMouseMove: MouseEventHandler;
onMouseLeave: () => void;
};

const NodeDisplay = ({ canvasRef, component, onMouseMove, onMouseLeave }: NodeDisplayProps) => {
if (component?.props) {
const { data } = component.props;
const { processor } = data;
if (processor.errorMessage) {
return <div className="node-error-msg">{processor.errorMessage}</div>;
}
}

return (
<canvas
ref={canvasRef}
width={NodeSizes.defaultWidth}
height={NodeSizes.defaultHeight}
/>
<canvas ref={canvasRef} width={NodeSizes.defaultWidth} height={NodeSizes.defaultHeight} onMouseMove={onMouseMove} onMouseLeave={onMouseLeave} />
);
};

export const NodeZoom = ({
canvasRef,
pos,
scale,
}: {
canvasRef: (ref: HTMLCanvasElement) => void;
pos: { x: number; y: number };
scale: number;
}) => {
return (
<div style={{ marginLeft: 50 + pos.x * scale, top: 75 + pos.y * scale, position: 'absolute', border: '1px solid' }}>
<canvas ref={canvasRef} height={NodeSizes.defaultHeight} />
</div>
);
};

Expand Down
4 changes: 2 additions & 2 deletions src/ide/components/NodeTab/index.tsx
Expand Up @@ -5,7 +5,7 @@ import { useNodeStore } from '../../../core/contexts/NodeStore';
import { Zoom } from '../../types/Zoom';

const NodeTab = ({ component }: { component: CVFComponent }) => {
const { zoom, options } = component.state;
const { scale: zoom, options } = component.state;
const notDisplay = options & CVFComponentOptions.NOT_DISPLAY;
return (
<div className="node-header">
Expand All @@ -26,7 +26,7 @@ const NodeTab = ({ component }: { component: CVFComponent }) => {
</Button>
<Form.Select
value={(zoom as number).toFixed ? (zoom as number).toFixed(2) : zoom}
onChange={(e) => component.changeZoom(e.target.value === Zoom.PERC_AUTO ? Zoom.PERC_AUTO : parseFloat(e.target.value || '1'))}
onChange={(e) => component.changeScale(e.target.value === Zoom.PERC_AUTO ? Zoom.PERC_AUTO : parseFloat(e.target.value || '1'))}
>
<option>Zoom</option>
<option value={Zoom.PERC_025}>25%</option>
Expand Down

0 comments on commit 808ba91

Please sign in to comment.