diff --git a/src/data/dataExporter.ts b/src/data/dataExporter.ts index b34ce56d509a4a115293d515c64a16061596bd31..b01afcac98adeea138b2655e88add56d7d322bd5 100644 --- a/src/data/dataExporter.ts +++ b/src/data/dataExporter.ts @@ -6,6 +6,7 @@ import {Coords} from "./coords/coords"; import {MeasurementExport} from "./measurement/process/measurementExport"; import {CoordsMeasurement} from "./coords/process/coordsMeasurement"; import {PointMerger} from "./coords/process/pointMerger"; +import {Measurement} from "./measurement/measurement"; export class DataExporter { @@ -60,6 +61,10 @@ export class DataExporter { return this.measureExport.exportMeasurements(labData.workInfo,[measurement]).csv; } + public getPathDescriptors(labData : LabData) : Measurement { + let measurement = this.coordsMeasure.measure(labData.blobMaskCoords, labData.rulerCoords, labData.rulerTickCount, labData.filename); + return measurement; + } private toCsv(point : paper.Point) : string { return point.x + "\t" + point.y + "\n"; diff --git a/src/data/measurement/process/measurementExport.ts b/src/data/measurement/process/measurementExport.ts index 45c051621f0b47a5c4451bf6d9a7613ae8a1b1ac..13ca915b58e6c1a0b86903d7dd86bd450ba273b4 100644 --- a/src/data/measurement/process/measurementExport.ts +++ b/src/data/measurement/process/measurementExport.ts @@ -19,7 +19,7 @@ export class MeasurementExport { */ public exportMeasurements(work: WorkInfo, measurements: Measurement[]) : { filename: string, csv: string } { return { - filename: "Results_" + this.workInfoFormatter.format(work) + ".csv", + filename: null, //"Results_" + this.workInfoFormatter.format(work) + ".csv", csv: csvFormatRows([[" ", "Label", "Area", "Perim.", "Circ.","AR","Round","Solidity"]] .concat(measurements .sort((m1, m2) => m1.label.localeCompare(m2.label)) diff --git a/src/lab.tsx b/src/lab.tsx index 97d17585a262cf41a0e32df9c32453a94e80c7f4..a2850599db9e7a9f7f36b7438dae4f579aea7e4f 100644 --- a/src/lab.tsx +++ b/src/lab.tsx @@ -28,7 +28,7 @@ export const DEBUG_MODE = false; /** * La taille par défaut de la règle */ -export const DEFAULT_RULER_TICK_COUNT = 10; +export const DEFAULT_RULER_TICK_COUNT = 8; /** * La taille minimum de la règle @@ -168,10 +168,15 @@ export class Lab extends React.Component<{}> { public new(image : HTMLImageElement, filename : string) : boolean { console.info("Nouvelle session de travail") let workInfo = this.workInfoParser.parse(IoUtils.basename(filename)); - if(workInfo == null) { + + // Disable this check to allow any filename + /* + if(workInfo == null) { window.alert("Le nom du fichier ne respecte pas nomenclature") return false; - } + } + */ + if(this.raster != null) { if(window.confirm("Écraser le travail en cours ?")) { this.reset(); @@ -330,7 +335,7 @@ export class Lab extends React.Component<{}> { <Container fluid={true}> <Row className={"col-md-12"}> <div className="d-flex d-flex justify-content-between"> - <Navbar.Brand className={"p-0"}><i className={"fa-solid fa-flask me-2"}></i>Blob Analysis Lab <sup><Badge pill bg="secondary" text="primary">demo</Badge></sup></Navbar.Brand> + <Navbar.Brand href="./" className={"p-0"}><i className={"fa-solid fa-flask me-2"}></i>Blob Analysis Lab <sup><Badge pill bg="secondary" text="primary">école</Badge></sup></Navbar.Brand> <Form className={"inline-form"}> <Form.Group controlId="zoomGroup"> <Form.Label>Zoom :</Form.Label> @@ -362,7 +367,7 @@ export class Lab extends React.Component<{}> { <RulerStep lab={this} code="placeRuler" title="Positionner la règle"/> <PlacePetriDishStep lab={this} code="placePetriDish" title="Positionner la boîte de petri"/> <DrawBlobMaskStep lab={this} code="drawBlobMask" title="Détourer le blob"/> - <DownloadStep lab={this} code="download" title="Télécharger les résultats"/> + <DownloadStep lab={this} code="download" title="Voir les résultats"/> </StepManager> </div> </Col> diff --git a/src/ui/steps/downloadStep.tsx b/src/ui/steps/downloadStep.tsx index 804b73319ddc8c71abb32a86ec23d0eb850800d4..7ca19b8bbaeee3321faa454016159ff030820f60 100644 --- a/src/ui/steps/downloadStep.tsx +++ b/src/ui/steps/downloadStep.tsx @@ -8,6 +8,7 @@ import {ImageDataWrapper, Rgba} from "../../render/ImageDataWrapper"; import {Merger} from "../merger"; import {WorkInfoFormatter} from "../../data/work/process/workInfoFormatter"; import {MeasurementExport} from "../../data/measurement/process/measurementExport"; +import {MathUtils} from "../../utils/mathUtils"; export interface DownloadButtonProps { downloading: boolean, @@ -36,7 +37,11 @@ export interface DownloadStepState extends StepState { blobMaskDataFilename: string | null, blobMaskFilename: string | null, resultsFilename: string | null, - downloading: boolean + downloading: boolean, + area: string | null, + perimeter: string | null, + showmore: boolean | null + } @@ -63,7 +68,7 @@ export class DownloadStep extends Step<DownloadStepState> { private measureExport = new MeasurementExport(); public constructor(props: StepProps) { - super(props, { active: false, activable: false, petriDishDataFilename : null, blobMaskDataFilename: null, blobMaskFilename: null, resultsFilename: null, downloading: false }); + super(props, { active: false, activable: false, petriDishDataFilename : null, blobMaskDataFilename: null, blobMaskFilename: null, resultsFilename: null, downloading: false, area: "-", perimeter: "-", showmore : false}); } canBeActivated(): boolean { @@ -75,15 +80,27 @@ export class DownloadStep extends Step<DownloadStepState> { } onActivation(): void { + let values = this.dataExporter.getPathDescriptors(this.props.lab.data); + this.setState({ petriDishDataFilename: IoUtils.basename(this.props.lab.data.filename) + "_Coord_Boite.txt", blobMaskDataFilename: IoUtils.basename(this.props.lab.data.filename) + "_Coord_Blob.txt", blobMaskFilename: IoUtils.basename(this.props.lab.data.filename) + "_Mask.png", - resultsFilename: this.measureExport.exportDryMeasures(this.props.lab.data.workInfo).filename, // dry run juste pour le nom + resultsFilename: IoUtils.basename(this.props.lab.data.filename) + ".csv", //this.measureExport.exportDryMeasures(this.props.lab.data.workInfo).filename, // dry run juste pour le nom + area: MathUtils.round(values.area, 1).toString(), + perimeter: MathUtils.round(values.perimeter, 1).toString() }); } onDeactivation(): void { + this.setState({ + petriDishDataFilename: null, + blobMaskDataFilename: null, + blobMaskFilename: null, + resultsFilename: null, + area: "-", + perimeter: "-" + }); } /** @@ -176,30 +193,57 @@ export class DownloadStep extends Step<DownloadStepState> { render() : React.ReactNode { return <div> <Alert show={!this.state.activable} variant="warning" className={"p-1"}>Veuillez terminer les étapes précédentes.</Alert> - <p>Téléchargez les fichiers d'analyse de la photo.</p> - <InputGroup className="mb-1"> - <DownloadButton key="downloadPetriDishDataButtonKey" downloading={false} onClick={this.downloadPetriDishData.bind(this)} disabled={!this.state.active}/> - <InputGroup.Text className={"col"}>{ this.state.petriDishDataFilename }</InputGroup.Text> - </InputGroup> - <InputGroup className="mb-1"> - <DownloadButton key="downloadBlobMaskDataButtonKey" downloading={false} onClick={this.downloadBlobMaskData.bind(this)} disabled={!this.state.active}/> - <InputGroup.Text className={"col"}>{ this.state.blobMaskDataFilename }</InputGroup.Text> - </InputGroup> - <InputGroup className="mb-1"> - <DownloadButton key="downloadBlobMaskButtonKey" downloading={false} onClick={this.downloadBlobMask.bind(this)} disabled={!this.state.active}/> - <InputGroup.Text className={"col"}>{ this.state.blobMaskFilename }</InputGroup.Text> + + <p>Mesures:</p> + + <InputGroup className="mb-1"> + <Button disabled={!this.state.active} variant={"primary"} size={"sm"}> + <i className={"far fa-regular fa-square"}></i> + </Button> + <InputGroup.Text className={"col"}>Périmètre: { this.state.perimeter } cm</InputGroup.Text> </InputGroup> - <InputGroup className="mb-1"> - <DownloadButton key="downloadResultsButtonKey" downloading={this.state.downloading} onClick={this.downloadResults.bind(this)} disabled={!this.state.active}></DownloadButton> - <InputGroup.Text className ={"col"}>{ this.state.resultsFilename }</InputGroup.Text> + + <InputGroup className="mb-1"> + <Button disabled={!this.state.active} variant={"primary"} size={"sm"}> + <i className={"fas fa-square"}></i> + </Button> + <InputGroup.Text className={"col"}>Aire: { this.state.area } cm<sup>2</sup></InputGroup.Text> </InputGroup> - <Row className="mb-1"> - <Col> - <Button key="mergerButtonKey" onClick={this.openMerger.bind(this)}><i className="fa-solid fa-object-group me-2"></i>Etape 5' : fusionner les CSV</Button> - <a className={"ms-1"} href={"http://localhost:8081/docs/index.html#/"} target={"_blank"}>en savoir plus</a> - </Col> - </Row> - <Merger ref={this.mergerRef}></Merger> + + <p></p> + + <Button onClick={() => this.setState({ showmore: !this.state.showmore })}>Plus de résultats</Button> + { this.state.showmore + ? <div> + + <p>Téléchargez les fichiers d'analyse de la photo.</p> + <InputGroup className="mb-1"> + <DownloadButton key="downloadPetriDishDataButtonKey" downloading={false} onClick={this.downloadPetriDishData.bind(this)} disabled={!this.state.active}/> + <InputGroup.Text className={"col"}>{ this.state.petriDishDataFilename }</InputGroup.Text> + </InputGroup> + <InputGroup className="mb-1"> + <DownloadButton key="downloadBlobMaskDataButtonKey" downloading={false} onClick={this.downloadBlobMaskData.bind(this)} disabled={!this.state.active}/> + <InputGroup.Text className={"col"}>{ this.state.blobMaskDataFilename }</InputGroup.Text> + </InputGroup> + <InputGroup className="mb-1"> + <DownloadButton key="downloadBlobMaskButtonKey" downloading={false} onClick={this.downloadBlobMask.bind(this)} disabled={!this.state.active}/> + <InputGroup.Text className={"col"}>{ this.state.blobMaskFilename }</InputGroup.Text> + </InputGroup> + <InputGroup className="mb-1"> + <DownloadButton key="downloadResultsButtonKey" downloading={this.state.downloading} onClick={this.downloadResults.bind(this)} disabled={!this.state.active}></DownloadButton> + <InputGroup.Text className ={"col"}>{ this.state.resultsFilename }</InputGroup.Text> + </InputGroup> + <Row className="mb-1"> + <Col> + <Button key="mergerButtonKey" onClick={this.openMerger.bind(this)}><i className="fa-solid fa-object-group me-2"></i>Etape 5' : fusionner les CSV</Button> + <a className={"ms-1"} href={"docs/index.html#/"} target={"_blank"}>en savoir plus</a> + </Col> + </Row> + <Merger ref={this.mergerRef}></Merger> + + </div> + : null + } </div> }