Skip to content
Snippets Groups Projects
Commit 6aaa463f authored by Laurent Knoll's avatar Laurent Knoll
Browse files

Améliorations d'ergonomie et de code

parent f3b4b9ed
No related branches found
No related tags found
No related merge requests found
...@@ -19,8 +19,8 @@ export class EllipseCoords implements Coords { ...@@ -19,8 +19,8 @@ export class EllipseCoords implements Coords {
new paper.Size(2 * this.radiusX, 2 * this.radiusY) new paper.Size(2 * this.radiusX, 2 * this.radiusY)
) )
); );
path.rotate(this.angle);
path.remove(); path.remove();
path.rotate(this.angle);
return path; return path;
} }
......
...@@ -6,12 +6,14 @@ import {Coords} from "../coords"; ...@@ -6,12 +6,14 @@ import {Coords} from "../coords";
import * as paper from "paper"; import * as paper from "paper";
import {types} from "sass"; import {types} from "sass";
import Color = types.Color; import Color = types.Color;
import {Transformation} from "./transformation";
import {PathCoords} from "../pathCoords";
const HALFPI : number= 1.5707963267949; const HALFPI : number= 1.5707963267949;
/** /**
* Adaptation de EllipseFitter.java de ImageJ * Adaptation de EllipseFitter.java de ImageJ
*/ */
export class EllipseFitter { export class EllipseFitter implements Transformation<PathCoords, Coords>{
private bitCount : number = 0; private bitCount : number = 0;
private xsum : number = 0; private xsum : number = 0;
...@@ -31,7 +33,7 @@ export class EllipseFitter { ...@@ -31,7 +33,7 @@ export class EllipseFitter {
private u11 : number = 0; //central moments private u11 : number = 0; //central moments
private record : boolean = false; private record : boolean = false;
public getFittingEllipse(coords : Coords) : EllipseCoords { transform(from: Coords): EllipseCoords {
/** X centroid */ /** X centroid */
let xCenter : number; let xCenter : number;
...@@ -52,7 +54,7 @@ export class EllipseFitter { ...@@ -52,7 +54,7 @@ export class EllipseFitter {
let theta : number; let theta : number;
const path = coords.toPath(); const path = from.toPath();
let bounds = path.bounds; let bounds = path.bounds;
this.left = Math.round(bounds.x); this.left = Math.round(bounds.x);
this.top = Math.round(bounds.y); this.top = Math.round(bounds.y);
...@@ -111,7 +113,7 @@ export class EllipseFitter { ...@@ -111,7 +113,7 @@ export class EllipseFitter {
xCenter = this.left + xoffset + 0.5; xCenter = this.left + xoffset + 0.5;
yCenter = this.top + yoffset + 0.5; yCenter = this.top + yoffset + 0.5;
return new EllipseCoords(new paper.Point(xCenter, yCenter), major / 2, minor / 2, angle); return new EllipseCoords(new paper.Point(xCenter, yCenter), major / 2, minor / 2, -angle);
} }
private computeSums (path : paper.Path) : void { private computeSums (path : paper.Path) : void {
......
import {Coords} from "../coords";
/**
* Interface de transformation de coordonnées
*/
export interface Transformation<FROM extends Coords, TO extends Coords> {
transform(from: FROM): TO;
}
\ No newline at end of file
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
*/ */
import {LabData} from "../lab"; import {LabData} from "../lab";
import {EllipseFitter} from "./coords/transform/ellipseFitter"; import {EllipseFitter} from "./coords/transform/ellipseFitter";
import {PathCoords} from "./coords/pathCoords";
import {Coords} from "./coords/coords"; import {Coords} from "./coords/coords";
import {MathUtils} from "../utils/mathUtils"; import {MathUtils} from "../utils/mathUtils";
...@@ -36,7 +35,7 @@ export class DataExporter { ...@@ -36,7 +35,7 @@ export class DataExporter {
let linearScale = labData.rulerCoords.distance() / labData.rulerTickCount; // pixels/cm let linearScale = labData.rulerCoords.distance() / labData.rulerTickCount; // pixels/cm
let areaScale = Math.pow(linearScale, 2); let areaScale = Math.pow(linearScale, 2);
let fittingEllipse = new EllipseFitter().getFittingEllipse(coords); let fittingEllipse = new EllipseFitter().transform(coords);
let line = 1; let line = 1;
let label = labData.filename; let label = labData.filename;
......
import * as paper from "paper"; import * as paper from "paper";
import {AbstractInstrument, Handle, Instrument} from "./instrument"; import {AbstractInstrument, Handle, Instrument} from "./instrument";
import {PathCoords} from "../data/coords/pathCoords"; import {PathCoords} from "../data/coords/pathCoords";
import {Lab} from "../lab"; import {DEBUG_MODE, Lab} from "../lab";
import {EllipseFitter} from "../data/coords/transform/ellipseFitter"; import {EllipseFitter} from "../data/coords/transform/ellipseFitter";
/** /**
...@@ -9,6 +9,16 @@ import {EllipseFitter} from "../data/coords/transform/ellipseFitter"; ...@@ -9,6 +9,16 @@ import {EllipseFitter} from "../data/coords/transform/ellipseFitter";
*/ */
export class BlobMask extends AbstractInstrument<PathCoords> implements Instrument { export class BlobMask extends AbstractInstrument<PathCoords> implements Instrument {
/**
* Appelé lorsque le tracé est fermé
*/
public onClosed : () => void = () => {};
/**
* Appelé lorsque le tracé s'ouvre
*/
public onOpened : () => void = () => {};
public constructor(protected lab : Lab, coords : PathCoords) { public constructor(protected lab : Lab, coords : PathCoords) {
super(lab, coords, [ super(lab, coords, [
new Handle("startHandle", true), new Handle("startHandle", true),
...@@ -19,11 +29,12 @@ export class BlobMask extends AbstractInstrument<PathCoords> implements Instrume ...@@ -19,11 +29,12 @@ export class BlobMask extends AbstractInstrument<PathCoords> implements Instrume
let line = coords.path.clone(); let line = coords.path.clone();
group.addChild(line); group.addChild(line);
// Affiche la fitted ellipse if(DEBUG_MODE) {
// if(line.length > 10) { if(line.length > 10) {
// const pathCoords = new PathCoords(line.clone()); const pathCoords = new PathCoords(line.clone());
// group.addChild(new EllipseFitter().getFittingEllipse(pathCoords).toPath()); group.addChild(new EllipseFitter().transform(pathCoords).toPath());
// } }
}
} }
...@@ -59,7 +70,13 @@ export class BlobMask extends AbstractInstrument<PathCoords> implements Instrume ...@@ -59,7 +70,13 @@ export class BlobMask extends AbstractInstrument<PathCoords> implements Instrume
if(!this.active) { if(!this.active) {
return true; return true;
} }
this.coords.path.add(event.point); if(!this.coords.isClosed()) { // Une fois la boucle fermée, on ne peut plus ajouter de
this.coords.path.add(event.point)
if(this.coords.isClosed()) {
this.onClosed();
}
}
this.refresh(); this.refresh();
return true; return true;
} }
...@@ -78,15 +95,26 @@ export class BlobMask extends AbstractInstrument<PathCoords> implements Instrume ...@@ -78,15 +95,26 @@ export class BlobMask extends AbstractInstrument<PathCoords> implements Instrume
* Supprime quelques derniers points * Supprime quelques derniers points
*/ */
public undo() { public undo() {
this.coords.path.removeSegments(Math.max(this.coords.path.segments.length - 5, 0)); this._undo(Math.max(this.coords.path.segments.length - 5, 0));
this.refresh();
} }
/** /**
* Supprime touy * Supprime tout
*/ */
public undoAll() { public undoAll() {
this.coords.path.removeSegments(); this._undo(0);
}
/**
* En charge de la suppression
*/
private _undo(from : number) {
let wasClosed = this.coords.isClosed();
this.coords.path.removeSegments(from);
if(wasClosed) {
this.onOpened();
}
this.refresh(); this.refresh();
} }
} }
\ No newline at end of file
...@@ -10,12 +10,16 @@ import {PetriDish} from "./instruments/petriDish"; ...@@ -10,12 +10,16 @@ import {PetriDish} from "./instruments/petriDish";
import {DrawBlobMaskStep} from "./steps/drawBlobMaskStep"; import {DrawBlobMaskStep} from "./steps/drawBlobMaskStep";
import {BlobMask} from "./instruments/blobMask"; import {BlobMask} from "./instruments/blobMask";
import {VectorCoords} from "./data/coords/vectorCoords"; import {VectorCoords} from "./data/coords/vectorCoords";
import {CircleCoords} from "./data/coords/circleCoords";
import {PathCoords} from "./data/coords/pathCoords"; import {PathCoords} from "./data/coords/pathCoords";
import {DownloadStep} from "./steps/downloadStep"; import {DownloadStep} from "./steps/downloadStep";
import {PaperUtils} from "./utils/paperUtils"; import {PaperUtils} from "./utils/paperUtils";
import {EllipseCoords} from "./data/coords/ellipseCoords"; import {EllipseCoords} from "./data/coords/ellipseCoords";
/**
* Debug mode (ou pas)
*/
export const DEBUG_MODE = false;
export interface LabData { export interface LabData {
pictureSize : paper.Size, pictureSize : paper.Size,
...@@ -210,14 +214,14 @@ export class Lab extends React.Component<{}> { ...@@ -210,14 +214,14 @@ export class Lab extends React.Component<{}> {
* Plus de zoom * Plus de zoom
*/ */
public zoomIn(target? : paper.Point) : void { public zoomIn(target? : paper.Point) : void {
this.zoom(1.05, target); this.zoom(1.10, target);
} }
/** /**
* Moins de zoom * Moins de zoom
*/ */
public zoomOut(target? : paper.Point) : void { public zoomOut(target? : paper.Point) : void {
this.zoom(0.95, target); this.zoom(0.90, target);
} }
/** /**
...@@ -259,7 +263,6 @@ export class Lab extends React.Component<{}> { ...@@ -259,7 +263,6 @@ export class Lab extends React.Component<{}> {
this.blobMask.refresh(); this.blobMask.refresh();
} }
render(): React.ReactNode { render(): React.ReactNode {
return <Container fluid={true} className={"vh-100 d-flex flex-column"}> return <Container fluid={true} className={"vh-100 d-flex flex-column"}>
<Navbar bg="light" expand="lg" className={"p-0"}> <Navbar bg="light" expand="lg" className={"p-0"}>
......
...@@ -19,7 +19,7 @@ export class DownloadButton extends React.Component<DownloadButtonProps, any> { ...@@ -19,7 +19,7 @@ export class DownloadButton extends React.Component<DownloadButtonProps, any> {
render() : React.ReactNode { render() : React.ReactNode {
let faIcon = this.props.downloading ? "fa-solid fas fa-cog fa-spin" : "fa-solid fa-download"; let faIcon = this.props.downloading ? "fa-solid fas fa-cog fa-spin" : "fa-solid fa-download";
let key = this.props.downloading ? "dl" : "notdl"; let key = this.props.downloading ? "downloading" : "notDownloading";
return <Button key={key} onClick={this.props.onClick} disabled={this.props.disabled || this.props.downloading} variant={"primary"} size={"sm"}> return <Button key={key} onClick={this.props.onClick} disabled={this.props.disabled || this.props.downloading} variant={"primary"} size={"sm"}>
<i className={faIcon}></i> <i className={faIcon}></i>
</Button> </Button>
......
...@@ -2,13 +2,20 @@ import {Step, StepProps, StepState} from "./step"; ...@@ -2,13 +2,20 @@ import {Step, StepProps, StepState} from "./step";
import * as React from "react"; import * as React from "react";
import {Alert, Button} from "react-bootstrap"; import {Alert, Button} from "react-bootstrap";
interface DrawBlobMaskStepState extends StepState {
closed: boolean;
}
/** /**
* Etape de placement de la règle * Étape de placement de la boîte de petri
*/ */
export class DrawBlobMaskStep extends Step<StepState> { export class DrawBlobMaskStep extends Step<DrawBlobMaskStepState> {
public constructor(props : StepProps) { public constructor(props : StepProps) {
super(props, { active: false, activable : false }); super(props, { active: false, activable : false, closed : false });
} }
canBeActivated(): boolean { canBeActivated(): boolean {
...@@ -17,6 +24,12 @@ export class DrawBlobMaskStep extends Step<StepState> { ...@@ -17,6 +24,12 @@ export class DrawBlobMaskStep extends Step<StepState> {
onActivation(): void { onActivation(): void {
this.props.lab.blobMask.activate(); this.props.lab.blobMask.activate();
this.props.lab.blobMask.onClosed = () => {
this.setState({closed: true });
};
this.props.lab.blobMask.onOpened = () => {
this.setState({closed: false });
};
this.props.lab.zoomOn( this.props.lab.data.petriDishCoords.bounds(), 0.05); this.props.lab.zoomOn( this.props.lab.data.petriDishCoords.bounds(), 0.05);
} }
...@@ -33,7 +46,9 @@ export class DrawBlobMaskStep extends Step<StepState> { ...@@ -33,7 +46,9 @@ export class DrawBlobMaskStep extends Step<StepState> {
Revenir en arrière : <Button className={"me-2"} size={"sm"} disabled={!this.state.active} onClick={() => this.props.lab.blobMask.undo()}><i className="fa-solid fa-delete-left"></i></Button> Revenir en arrière : <Button className={"me-2"} size={"sm"} disabled={!this.state.active} onClick={() => this.props.lab.blobMask.undo()}><i className="fa-solid fa-delete-left"></i></Button>
Tout effacer : <Button className={"me-2"} size={"sm"} disabled={!this.state.active} onClick={() => this.props.lab.blobMask.undoAll()}><i className="fa-solid fa-trash-can"></i></Button> Tout effacer : <Button className={"me-2"} size={"sm"} disabled={!this.state.active} onClick={() => this.props.lab.blobMask.undoAll()}><i className="fa-solid fa-trash-can"></i></Button>
</Alert> </Alert>
<Button variant={"success"} disabled={!this.state.active} onClick={this.terminate.bind(this)}>Fini !</Button> <Button className={"col-3"} variant={"success"} disabled={!this.state.active || !this.state.closed} onClick={this.terminate.bind(this)}>
<span hidden={!this.state.closed}><i className="fa-solid fa-hands-clapping fa-beat-fade me-2"></i></span>Fini !
</Button>
</div> </div>
</div> </div>
} }
......
...@@ -33,7 +33,7 @@ export class PlacePetriDishStep extends Step<StepState> { ...@@ -33,7 +33,7 @@ export class PlacePetriDishStep extends Step<StepState> {
<div> <div>
<Alert show={!this.state.activable} variant="warning" className={"p-1"}>Veuillez charger une photo.</Alert> <Alert show={!this.state.activable} variant="warning" className={"p-1"}>Veuillez charger une photo.</Alert>
<p>Déplacez et redimensionnez le cercle à l'aide des poignées blanches pour placer la boîte de petri.</p> <p>Déplacez et redimensionnez le cercle à l'aide des poignées blanches pour placer la boîte de petri.</p>
<p>Appuyez ici <Button disabled={!this.state.active} onClick={this.zoomOnPetriDishCenter.bind(this)} size={"sm"}><i className={"fa-solid fa-magnifying-glass-location"}></i></Button> pour placer la boîte de petri avec précision.</p> <Alert variant={"light"} className={"p-2"}><i className="ms-1 me-1 fa-solid fa-circle-info"></i>Cliquez ici <Button disabled={!this.state.active} onClick={this.zoomOnPetriDishCenter.bind(this)} size={"sm"}><i className={"fa-solid fa-magnifying-glass-location"}></i></Button> pour placer la boîte de petri avec précision.</Alert>
<Button variant={"success"} disabled={!this.state.active} onClick={this.terminate.bind(this)}>C'est fait !</Button> <Button variant={"success"} disabled={!this.state.active} onClick={this.terminate.bind(this)}>C'est fait !</Button>
</div> </div>
</div> </div>
......
...@@ -33,7 +33,7 @@ export class RulerStep extends Step<StepState> { ...@@ -33,7 +33,7 @@ export class RulerStep extends Step<StepState> {
<div> <div>
<Alert show={!this.state.activable} variant="warning" className={"p-1"}>Veuillez charger une photo.</Alert> <Alert show={!this.state.activable} variant="warning" className={"p-1"}>Veuillez charger une photo.</Alert>
<p>Positionnez la règle sur la photo. La règle doit couvrir 9 cm.</p> <p>Positionnez la règle sur la photo. La règle doit couvrir 9 cm.</p>
<p>Appuyez ici <Button disabled={!this.state.active} onClick={this.zoomOnRuler.bind(this)} size={"sm"}><i className={"fa-solid fa-magnifying-glass-location"}></i></Button> pour placer la règle avec précision.</p> <Alert variant={"light"} className={"p-2"}><i className="ms-1 me-1 fa-solid fa-circle-info"></i> Appuyez ici <Button disabled={!this.state.active} onClick={this.zoomOnRuler.bind(this)} size={"sm"}><i className={"fa-solid fa-magnifying-glass-location"}></i></Button> pour placer la règle avec précision.</Alert>
<Button variant={"success"} disabled={!this.state.active} onClick={this.terminate.bind(this)}>Terminé !</Button> <Button variant={"success"} disabled={!this.state.active} onClick={this.terminate.bind(this)}>Terminé !</Button>
</div> </div>
</div> </div>
......
...@@ -23,7 +23,7 @@ export interface StepState { ...@@ -23,7 +23,7 @@ export interface StepState {
*/ */
export abstract class Step<S extends StepState> extends React.Component<StepProps, S> { export abstract class Step<S extends StepState> extends React.Component<StepProps, S> {
public onTerminated : (stepComponent : Step<S>) => void = () => { console.log("Aïe"); }; public onTerminated : (stepComponent : Step<S>) => void = () => { };
protected constructor(props : StepProps, defaultState : S) { protected constructor(props : StepProps, defaultState : S) {
super(props); super(props);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment