lost-sia 3.2.0 → 3.3.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- import{jsxs as X,jsx as p,Fragment as nt}from"react/jsx-runtime";import{useState as E,useRef as Q,useEffect as v}from"react";import O from"../models/AnnotationTool.js";import r from"../models/EditorModes.js";import ot from"../utils/KeyMapper.js";import a from"../models/KeyAction.js";import ye from"../Annotation/logic/Annotation.js";import st from"../models/CanvasAction.js";import rt from"../Annotation/ui/AnnotationComponent.js";import xe from"../utils/mouse.js";import S from"../models/AnnotationMode.js";import it from"./LabelInput.js";import{FontAwesomeIcon as at}from"@fortawesome/react-fontawesome";import{faBan as ct}from"@fortawesome/free-solid-svg-icons";import w from"../models/AnnotationStatus.js";import x from"../utils/transform.js";import lt from"../models/NotificationType.js";import dt from"../utils/TimeUtils.js";import he from"../utils/windowViewport.js";const Rt=({annotations:C=[],annotationSettings:U,defaultLabelId:Z,image:Y,isFullscreen:Ce=!1,isImageJunk:F=!1,isPolygonSelectionMode:H=!1,polygonOperationResult:G={annotationsToDelete:[],polygonsToCreate:[]},possibleLabels:q,preventScrolling:ee=!0,selectedAnnotation:i,selectedAnnoTool:R,toolbarHeight:z=0,uiConfig:N,onAnnoCreated:ve,onAnnoCreationFinished:te,onAnnoChanged:ne,onAnnoEditing:Ie=c=>{},onNotification:oe=c=>{},onRequestNewAnnoId:P,onSelectAnnotation:I,onSetIsImageJunk:Te,onSetSelectedTool:Me=c=>{},onShouldDeleteAnno:se,onTraverseAnnotationHistory:re})=>{const[c,A]=E(r.VIEW),[Oe,we]=E(),[ie,De]=E(Z),[V,_e]=E({x:-1,y:-1}),[ae,ce]=E(0),L={x:V.x,y:V.y},[g,le]=E({x:-1,y:-1}),[d,$]=E({x:-1,y:-1}),[m,de]=E({x:-1,y:-1}),[u,k]=E(1),[f,D]=E({x:0,y:0}),W={x:f.x+ae,y:f.y},[ue,B]=E(void 0),[j,_]=E(!1),fe=Q(null),y=Q(null),T=Q(null),h=((e,t)=>{if(e.x===0||e.y===0||t.x===0||t.y===0)return 0;const n=t.x/e.x,o=t.y/e.y;return Math.min(n,o)})(g,d),be=()=>{if(T?.current===null)return{x:0,y:0};const e=g.x*h;if(N.imageCentered&&d.x>e){const l=(d.x-e)/2;ce(l)}else ce(0);const{top:t,left:n}=y.current.getBoundingClientRect(),o={x:n+window.scrollX,y:t+window.scrollY};_e(o)},Se=new ot(e=>Ue(e)),Re=e=>{A(r.CREATE);const t=x.convertStageCoordinatesToPercentaged([e],h,g);R===O.BBox&&t.push(t[0]);const n=P(),o=new ye(n,R,t);if(we(performance.now()),ie!==void 0&&(o.labelIds=[ie]),ve(o),R===O.Point){const s={...o,coordinates:[e],annoTime:0};J(s)}},Ne=()=>{if(i&&![O.Line,O.Polygon].includes(i.type))return;const e=C.find(n=>n.internalId===i?.internalId);if(e===void 0)return;A(r.CREATE),Me(e.type);const t={...e,mode:S.CREATE,status:w.CREATING,internalId:P(),selectedNode:e.coordinates.length-1};Ie(t)},Pe=()=>{const e=i?i.internalId:0,t=C.find(n=>n.internalId>e);if(t)return I(t);if(C.length>0)return I(C[0])},Ve=()=>{const e=i?i.internalId:0,t=[...C];t.sort((o,s)=>s.internalId-o.internalId);const n=t.find(o=>o.internalId<e);if(n)return I(n);if(C.length>0)return I(C.at(-1))},Le=()=>{if(i){const e=JSON.stringify(i);localStorage.setItem("lostAnnotationClipboard",e);const t={title:"Success",message:"Annotation copied",type:lt.SUCCESS};oe(t)}},ke=()=>{const e=localStorage.getItem("lostAnnotationClipboard");if(e==null)return;const t=JSON.parse(e);t.internalId=P(),t.externalId="",te(t,!0),I(t)},We=e=>{const t=x.getMostLeftPoints(e),n=x.getTopPoint(t)[0];return x.convertStageToPage(n,L,u,W)},Be=250,Ke=350,Xe=e=>{const t=y.current?.getBoundingClientRect(),n=t?.left??0,o=t?.top??0,s=(t?.right??window.innerWidth)-Be,l=(t?.bottom??window.innerHeight)-Ke;return{x:Math.min(Math.max(e.x,n),s),y:Math.min(Math.max(e.y,o),l)}},Ue=e=>{switch(e){case a.EDIT_LABEL:if(i){const t=x.convertPercentagedCoordinatesToStage(i.coordinates,g,m);B(t),_(!0)}break;case a.DELETE_ANNO:i&&se(i.internalId);break;case a.DELETE_ANNO_IN_CREATION:c===r.CREATE&&(se(i.internalId),A(r.VIEW));break;case a.ENTER_ANNO_ADD_MODE:console.log("KeyAction TODO: ENTER_ANNO_ADD_MODE");break;case a.LEAVE_ANNO_ADD_MODE:console.log("KeyAction TODO: LEAVE_ANNO_ADD_MODE");break;case a.UNDO:re(!0);break;case a.REDO:re(!1);break;case a.TRAVERSE_ANNOS:Pe();break;case a.TRAVERSE_ANNOS_BACKWARDS:Ve();break;case a.CAM_MOVE_LEFT:b(20*u,0);break;case a.CAM_MOVE_RIGHT:b(-20*u,0);break;case a.CAM_MOVE_UP:b(0,20*u);break;case a.CAM_MOVE_DOWN:b(0,-20*u);break;case a.CAM_MOVE_STOP:console.log("KeyAction TODO: CAM_MOVE_STOP");break;case a.COPY_ANNOTATION:Le();break;case a.PASTE_ANNOTATION:ke();break;case a.RECREATE_ANNO:console.log("KeyAction TODO: RECREATE_ANNO"),Ne();break;case a.TOGGLE_IMAGE_JUNK:if(c===r.ADD||c===r.CREATE)return;Te(!F);break;default:console.log("Unknown KeyAction",e);break}},b=(e,t)=>{let n=f.x+e/u,o=f.y+t/u;const s=d.x*.45,l=d.x*.55,M=d.y*.45,et=d.y*.55,tt={x:0,y:0},pe=he.getViewportCoordinates(f,d,u,tt),Ae=he.getViewportCoordinates(f,d,u,d);pe.vX>=s?n=f.x-5:Ae.vX<=l?n=f.x+5:pe.vY>=M?o=f.y-5:Ae.vY<=et&&(o=f.y+5),D({x:n,y:o})},K=(e=>m.x<=0||m.y<=0||g.x<=0||g.y<=0?[]:C.map(n=>({...n,coordinates:x.convertPercentagedCoordinatesToStage(n.coordinates,g,m)})))(),Ye=()=>{if(A(r.VIEW),de({x:-1,y:-1}),T.current!==null){const{width:e,height:t}=T.current.getBoundingClientRect();le({x:e,y:t})}k(1),D({x:0,y:0}),B(void 0),_(!1)};v(()=>{fe.current?.focus()},[]),v(()=>{const e=t=>{t.button===1&&c===r.CAMERA_MOVE&&A(r.VIEW)};return window.addEventListener("mouseup",e),()=>window.removeEventListener("mouseup",e)},[c]),v(()=>{if(y?.current!==void 0){Ye();const{width:e,height:t}=y.current.getBoundingClientRect(),n=t-z;$({x:e,y:n});const o=new ResizeObserver(()=>{const{width:s,height:l}=y.current.getBoundingClientRect(),M=l-z;$({x:s,y:M})});return o.observe(y.current),()=>o.disconnect()}},[Y,Ce]),v(()=>{be()},[d,g,N]),v(()=>{if(y.current===null)return;const{width:e,height:t}=y.current.getBoundingClientRect(),n=t-z;$({x:e,y:n})},[y]),v(()=>{if(T.current===null)return;const{width:e,height:t}=T.current.getBoundingClientRect();le({x:e,y:t})},[Y,d]),v(()=>{if(h===0)return;const e={x:g.x*h,y:g.y*h};de(e)},[h,g]),v(()=>{H&&G.polygonsToCreate!==void 0&&G.polygonsToCreate.forEach(e=>{const t=P(),n=new ye(t,e.type,x.convertPercentagedCoordinatesToStage(e.coordinates,g,m),S.VIEW,w.CREATED);e.labelIds!==void 0&&(n.labelIds=e.labelIds),J(n)})},[G]);const J=e=>{A(r.VIEW);const t={...e,mode:S.VIEW};if(e.type!==O.Point){const s=dt.getRoundedDuration(Oe,performance.now());t.annoTime=s}const n=x.convertStageCoordinatesToPercentaged(e.coordinates,h,g);t.coordinates=n,ne(t);const o=R===O.Point||H;te(t,o)},Fe=e=>{Se.keyDown(e.key,e.shiftKey,e.ctrlKey)&&e.preventDefault()},He=e=>{e.preventDefault()},Ge=e=>{if(e.button!==0){if(e.button===1){if(c===r.CREATE||c===r.ADD){e.preventDefault();return}A(r.CAMERA_MOVE)}else if(e.button===2){if(!U.canCreate||c===r.ADD||c===r.CREATE)return;const t=xe.getAntiScaledMouseStagePosition(e,L,u,f),n={x:t.x-ae,y:t.y};Re(n)}}},ze=()=>{ee&&(document.body.style.overflow="hidden")},$e=e=>{e.button===1&&c===r.CAMERA_MOVE&&A(r.VIEW)},ge=(e,t)=>{c===r.CAMERA_MOVE&&b(e,t)},je=()=>{ee&&(document.body.style.overflow="")},Je=e=>{const o=(e.deltaY<0?1:-1)>0?u*1.25:u/1.25,s=xe.getAntiScaledMouseStagePosition(e,L,u,f),l=u/o,M={x:l*(s.x+f.x)-s.x,y:l*(s.y+f.y)-s.y};o<1?(k(1),(f.x!=0||f.y!=0)&&D({x:0,y:0})):o>200?(k(200),D(M)):(k(o),D(M))},Qe=(e,t)=>{if(t!==st.ANNO_SELECTED){console.log("Unknown Canvas Action:",t);return}const n={...e,coordinates:x.convertStageCoordinatesToPercentaged([...e.coordinates],h,g)};I(n),B(e.coordinates)},Ee=e=>{const t=x.convertStageCoordinatesToPercentaged(e.coordinates,h,g),n={...e,coordinates:t};n.status===w.LOADED&&(n.status=w.CHANGED),ne(n)},Ze=()=>{if(c===r.CAMERA_MOVE)return p(nt,{});const t=[r.CREATE,r.ADD,r.MOVE].includes(c),n=K.map(o=>{const s=o.internalId===i?.internalId;return t&&!s?p("g",{},`annotationComponent_${o.internalId}`):p(rt,{scaledAnnotation:o,annotationSettings:U,possibleLabels:q,svgScale:u,svgTranslation:W,pageToStageOffset:L,nodeRadius:N.nodeRadius,strokeWidth:N.strokeWidth,isSelected:s,isDisabled:H&&s,onFinishAnnoCreate:J,onLabelIconClicked:()=>_(!0),onAction:Qe,onAnnoChanged:Ee,onAnnotationModeChange:l=>{l===S.MOVE&&A(r.MOVE),c===r.MOVE&&l===S.VIEW&&A(r.VIEW)},onNotification:oe},`annotationComponent_${o.internalId}`)});if(i){const o=K.find(l=>l.internalId===i?.internalId),s=K.indexOf(o);n.push(n.splice(s,1)[0])}return p("g",{children:n})},qe=()=>p("circle",{cx:m.x/2,cy:m.y/2,r:"100%",style:{opacity:0},onContextMenu:e=>e.preventDefault(),onClick:()=>{_(!1)}}),me={x:V.x+d.x/2,y:V.y+d.y/2};return X("div",{ref:y,style:{flex:"1 1 auto",minHeight:0,display:"flex",flexDirection:"column"},children:[p("div",{style:(()=>{const e=ue?Xe(We(ue)):void 0;return{position:"fixed",left:e?e.x-window.scrollX:0,top:e?e.y-window.scrollY:0,display:e===void 0?"none":"inherit",zIndex:j?7e3:-1}})(),children:p(it,{defaultLabelId:Z,isVisible:j,selectedLabelsIds:i?.labelIds,possibleLabels:q,isMultilabel:U.canHaveMultipleLabels,onLabelSelect:e=>{if(setTimeout(()=>_(!1),0),e.length>0){const s=e.filter(l=>!i.labelIds.includes(l));s.length>0&&De(s[0])}const t=K.find(s=>s.internalId===i.internalId);if(!t)return;const n=t.status===w.LOADED?w.CHANGED:t.status,o={...i,coordinates:t.coordinates,labelIds:[...e],status:n};Ee(o)}})}),F&&X("div",{style:{position:"absolute",left:me.x,top:me.y,transform:"translate(-50%, -50%)",textAlign:"center",color:"white"},children:[p(at,{icon:ct,size:"5x",style:{marginBottom:15}}),p("h2",{children:"Marked as Junk"})]}),X("svg",{ref:fe,style:{flex:"1 1 auto",minHeight:0},onKeyDown:Fe,onKeyUp:He,onMouseMove:e=>ge(e.movementX,e.movementY),tabIndex:0,onMouseDown:e=>Ge(e),children:[X("g",{transform:`scale(${u}) translate(${W.x}, ${W.y})`,onMouseOver:ze,onMouseLeave:je,onMouseUp:$e,onWheel:Je,onMouseMove:e=>ge(e.movementX,e.movementY),onClick:()=>{I(void 0)},children:[p("image",{onContextMenu:e=>e.preventDefault(),href:Y,ref:T,width:m.x>0?m.x:void 0,height:m.y>0?m.y:void 0}),Ze()]}),j&&qe(),F&&p("rect",{x:"0",y:"0",width:d.x,height:d.y,style:{opacity:.8},onContextMenu:e=>e.preventDefault(),onClick:()=>{B(void 0)}})]})]})};export{Rt as default};
1
+ import{jsxs as K,jsx as A,Fragment as nt}from"react/jsx-runtime";import{useState as m,useRef as Q,useEffect as C}from"react";import M from"../models/AnnotationTool.js";import r from"../models/EditorModes.js";import ot from"../utils/KeyMapper.js";import a from"../models/KeyAction.js";import ye from"../Annotation/logic/Annotation.js";import st from"../models/CanvasAction.js";import rt from"../Annotation/ui/AnnotationComponent.js";import xe from"../utils/mouse.js";import b from"../models/AnnotationMode.js";import it from"./LabelInput.js";import{FontAwesomeIcon as at}from"@fortawesome/react-fontawesome";import{faBan as ct}from"@fortawesome/free-solid-svg-icons";import O from"../models/AnnotationStatus.js";import x from"../utils/transform.js";import lt from"../models/NotificationType.js";import dt from"../utils/TimeUtils.js";import he from"../utils/windowViewport.js";const Nt=({annotations:v=[],annotationSettings:X,defaultLabelId:Z,image:U,isFullscreen:ve=!1,isImageJunk:Y=!1,isPolygonSelectionMode:F=!1,polygonOperationResult:H={annotationsToDelete:[],polygonsToCreate:[]},possibleLabels:q,preventScrolling:ee=!0,selectedAnnotation:i,selectedAnnoTool:S,toolbarHeight:G=0,uiConfig:N,onAnnoCreated:Ce,onAnnoCreationFinished:te,onAnnoChanged:ne,onAnnoEditing:Ie=c=>{},onNotification:oe=c=>{},onRequestNewAnnoId:R,onSelectAnnotation:I,onSetIsImageJunk:Te,onSetSelectedTool:Me=c=>{},onShouldDeleteAnno:se,onTraverseAnnotationHistory:re})=>{const[c,y]=m(r.VIEW),[Oe,we]=m(),[ie,De]=m(Z),[P,_e]=m({x:-1,y:-1}),[ae,ce]=m(0),V={x:P.x,y:P.y},[E,le]=m({x:-1,y:-1}),[d,z]=m({x:-1,y:-1}),[g,de]=m({x:-1,y:-1}),[u,L]=m(1),[f,w]=m({x:0,y:0}),k={x:f.x+ae,y:f.y},[ue,W]=m(void 0),[$,D]=m(!1),fe=Q(null),p=Q(null),j=Q(null),h=((e,t)=>{if(e.x===0||e.y===0||t.x===0||t.y===0)return 0;const n=t.x/e.x,o=t.y/e.y;return Math.min(n,o)})(E,d),be=()=>{if(j?.current===null)return{x:0,y:0};const e=E.x*h;if(N.imageCentered&&d.x>e){const l=(d.x-e)/2;ce(l)}else ce(0);const{top:t,left:n}=p.current.getBoundingClientRect(),o={x:n+window.scrollX,y:t+window.scrollY};_e(o)},Se=new ot(e=>Ue(e)),Ne=e=>{y(r.CREATE);const t=x.convertStageCoordinatesToPercentaged([e],h,E);S===M.BBox&&t.push(t[0]);const n=R(),o=new ye(n,S,t);if(we(performance.now()),ie!==void 0&&(o.labelIds=[ie]),Ce(o),S===M.Point){const s={...o,coordinates:[e],annoTime:0};J(s)}},Re=()=>{if(i&&![M.Line,M.Polygon].includes(i.type))return;const e=v.find(n=>n.internalId===i?.internalId);if(e===void 0)return;y(r.CREATE),Me(e.type);const t={...e,mode:b.CREATE,status:O.CREATING,internalId:R(),selectedNode:e.coordinates.length-1};Ie(t)},Pe=()=>{const e=i?i.internalId:0,t=v.find(n=>n.internalId>e);if(t)return I(t);if(v.length>0)return I(v[0])},Ve=()=>{const e=i?i.internalId:0,t=[...v];t.sort((o,s)=>s.internalId-o.internalId);const n=t.find(o=>o.internalId<e);if(n)return I(n);if(v.length>0)return I(v.at(-1))},Le=()=>{if(i){const e=JSON.stringify(i);localStorage.setItem("lostAnnotationClipboard",e);const t={title:"Success",message:"Annotation copied",type:lt.SUCCESS};oe(t)}},ke=()=>{const e=localStorage.getItem("lostAnnotationClipboard");if(e==null)return;const t=JSON.parse(e);t.internalId=R(),t.externalId="",te(t,!0),I(t)},We=e=>{const t=x.getMostLeftPoints(e),n=x.getTopPoint(t)[0];return x.convertStageToPage(n,V,u,k)},Be=250,Ke=350,Xe=e=>{const t=p.current?.getBoundingClientRect(),n=t?.left??0,o=t?.top??0,s=(t?.right??window.innerWidth)-Be,l=(t?.bottom??window.innerHeight)-Ke;return{x:Math.min(Math.max(e.x,n),s),y:Math.min(Math.max(e.y,o),l)}},Ue=e=>{switch(e){case a.EDIT_LABEL:if(i){const t=x.convertPercentagedCoordinatesToStage(i.coordinates,E,g);W(t),D(!0)}break;case a.DELETE_ANNO:i&&se(i.internalId);break;case a.DELETE_ANNO_IN_CREATION:c===r.CREATE&&(se(i.internalId),y(r.VIEW));break;case a.ENTER_ANNO_ADD_MODE:console.log("KeyAction TODO: ENTER_ANNO_ADD_MODE");break;case a.LEAVE_ANNO_ADD_MODE:console.log("KeyAction TODO: LEAVE_ANNO_ADD_MODE");break;case a.UNDO:re(!0);break;case a.REDO:re(!1);break;case a.TRAVERSE_ANNOS:Pe();break;case a.TRAVERSE_ANNOS_BACKWARDS:Ve();break;case a.CAM_MOVE_LEFT:_(20*u,0);break;case a.CAM_MOVE_RIGHT:_(-20*u,0);break;case a.CAM_MOVE_UP:_(0,20*u);break;case a.CAM_MOVE_DOWN:_(0,-20*u);break;case a.CAM_MOVE_STOP:console.log("KeyAction TODO: CAM_MOVE_STOP");break;case a.COPY_ANNOTATION:Le();break;case a.PASTE_ANNOTATION:ke();break;case a.RECREATE_ANNO:console.log("KeyAction TODO: RECREATE_ANNO"),Re();break;case a.TOGGLE_IMAGE_JUNK:if(c===r.ADD||c===r.CREATE)return;Te(!Y);break;default:console.log("Unknown KeyAction",e);break}},_=(e,t)=>{let n=f.x+e/u,o=f.y+t/u;const s=d.x*.45,l=d.x*.55,T=d.y*.45,et=d.y*.55,tt={x:0,y:0},pe=he.getViewportCoordinates(f,d,u,tt),Ae=he.getViewportCoordinates(f,d,u,d);pe.vX>=s?n=f.x-5:Ae.vX<=l?n=f.x+5:pe.vY>=T?o=f.y-5:Ae.vY<=et&&(o=f.y+5),w({x:n,y:o})},B=(e=>g.x<=0||g.y<=0||E.x<=0||E.y<=0?[]:v.map(n=>({...n,coordinates:x.convertPercentagedCoordinatesToStage(n.coordinates,E,g)})))(),Ye=()=>{y(r.VIEW),de({x:-1,y:-1}),le({x:0,y:0}),L(1),w({x:0,y:0}),W(void 0),D(!1)};C(()=>{fe.current?.focus()},[]),C(()=>{const e=t=>{t.button===1&&c===r.CAMERA_MOVE&&y(r.VIEW)};return window.addEventListener("mouseup",e),()=>window.removeEventListener("mouseup",e)},[c]),C(()=>{if(p?.current!==null){Ye();const{width:e,height:t}=p.current.getBoundingClientRect(),n=t-G;z({x:e,y:n});const o=new ResizeObserver(()=>{if(p.current===null)return;const{width:s,height:l}=p.current.getBoundingClientRect(),T=l-G;z({x:s,y:T})});return o.observe(p.current),()=>o.disconnect()}},[U,ve]),C(()=>{be()},[d,E,N]),C(()=>{if(p.current===null)return;const{width:e,height:t}=p.current.getBoundingClientRect(),n=t-G;z({x:e,y:n})},[p]),C(()=>{const e=j.current;if(e===null)return;let t=!0;const n=()=>{if(!t)return;const{width:o,height:s}=e.getBoundingClientRect();le({x:o,y:s})};return e.addEventListener("load",n),n(),()=>{t=!1,e.removeEventListener("load",n)}},[U,d]),C(()=>{if(h===0)return;const e={x:E.x*h,y:E.y*h};de(e)},[h,E]),C(()=>{F&&H.polygonsToCreate!==void 0&&H.polygonsToCreate.forEach(e=>{const t=R(),n=new ye(t,e.type,x.convertPercentagedCoordinatesToStage(e.coordinates,E,g),b.VIEW,O.CREATED);e.labelIds!==void 0&&(n.labelIds=e.labelIds),J(n)})},[H]);const J=e=>{y(r.VIEW);const t={...e,mode:b.VIEW};if(e.type!==M.Point){const s=dt.getRoundedDuration(Oe,performance.now());t.annoTime=s}const n=x.convertStageCoordinatesToPercentaged(e.coordinates,h,E);t.coordinates=n,ne(t);const o=S===M.Point||F;te(t,o)},Fe=e=>{Se.keyDown(e.key,e.shiftKey,e.ctrlKey)&&e.preventDefault()},He=e=>{e.preventDefault()},Ge=e=>{if(e.button!==0){if(e.button===1){if(c===r.CREATE||c===r.ADD){e.preventDefault();return}y(r.CAMERA_MOVE)}else if(e.button===2){if(!X.canCreate||c===r.ADD||c===r.CREATE)return;const t=xe.getAntiScaledMouseStagePosition(e,V,u,f),n={x:t.x-ae,y:t.y};Ne(n)}}},ze=()=>{ee&&(document.body.style.overflow="hidden")},$e=e=>{e.button===1&&c===r.CAMERA_MOVE&&y(r.VIEW)},Ee=(e,t)=>{c===r.CAMERA_MOVE&&_(e,t)},je=()=>{ee&&(document.body.style.overflow="")},Je=e=>{const o=(e.deltaY<0?1:-1)>0?u*1.25:u/1.25,s=xe.getAntiScaledMouseStagePosition(e,V,u,f),l=u/o,T={x:l*(s.x+f.x)-s.x,y:l*(s.y+f.y)-s.y};o<1?(L(1),(f.x!=0||f.y!=0)&&w({x:0,y:0})):o>200?(L(200),w(T)):(L(o),w(T))},Qe=(e,t)=>{if(t!==st.ANNO_SELECTED){console.log("Unknown Canvas Action:",t);return}const n={...e,coordinates:x.convertStageCoordinatesToPercentaged([...e.coordinates],h,E)};I(n),W(e.coordinates)},me=e=>{const t=x.convertStageCoordinatesToPercentaged(e.coordinates,h,E),n={...e,coordinates:t};n.status===O.LOADED&&(n.status=O.CHANGED),ne(n)},Ze=()=>{if(c===r.CAMERA_MOVE)return A(nt,{});const t=[r.CREATE,r.ADD,r.MOVE].includes(c),n=B.map(o=>{const s=o.internalId===i?.internalId;return t&&!s?A("g",{},`annotationComponent_${o.internalId}`):A(rt,{scaledAnnotation:o,annotationSettings:X,possibleLabels:q,svgScale:u,svgTranslation:k,pageToStageOffset:V,nodeRadius:N.nodeRadius,strokeWidth:N.strokeWidth,isSelected:s,isDisabled:F&&s,onFinishAnnoCreate:J,onLabelIconClicked:()=>D(!0),onAction:Qe,onAnnoChanged:me,onAnnotationModeChange:l=>{l===b.MOVE&&y(r.MOVE),c===r.MOVE&&l===b.VIEW&&y(r.VIEW)},onNotification:oe},`annotationComponent_${o.internalId}`)});if(i){const o=B.find(l=>l.internalId===i?.internalId),s=B.indexOf(o);n.push(n.splice(s,1)[0])}return A("g",{children:n})},qe=()=>A("circle",{cx:g.x/2,cy:g.y/2,r:"100%",style:{opacity:0},onContextMenu:e=>e.preventDefault(),onClick:()=>{D(!1)}}),ge={x:P.x+d.x/2,y:P.y+d.y/2};return K("div",{ref:p,style:{flex:"1 1 auto",minHeight:0,display:"flex",flexDirection:"column"},children:[A("div",{style:(()=>{const e=ue?Xe(We(ue)):void 0;return{position:"fixed",left:e?e.x-window.scrollX:0,top:e?e.y-window.scrollY:0,display:e===void 0?"none":"inherit",zIndex:$?7e3:-1}})(),children:A(it,{defaultLabelId:Z,isVisible:$,selectedLabelsIds:i?.labelIds,possibleLabels:q,isMultilabel:X.canHaveMultipleLabels,onLabelSelect:e=>{if(setTimeout(()=>D(!1),0),e.length>0){const s=e.filter(l=>!i.labelIds.includes(l));s.length>0&&De(s[0])}const t=B.find(s=>s.internalId===i.internalId);if(!t)return;const n=t.status===O.LOADED?O.CHANGED:t.status,o={...i,coordinates:t.coordinates,labelIds:[...e],status:n};me(o)}})}),Y&&K("div",{style:{position:"absolute",left:ge.x,top:ge.y,transform:"translate(-50%, -50%)",textAlign:"center",color:"white"},children:[A(at,{icon:ct,size:"5x",style:{marginBottom:15}}),A("h2",{children:"Marked as Junk"})]}),K("svg",{ref:fe,style:{flex:"1 1 auto",minHeight:0},onKeyDown:Fe,onKeyUp:He,onMouseMove:e=>Ee(e.movementX,e.movementY),tabIndex:0,onMouseDown:e=>Ge(e),children:[K("g",{transform:`scale(${u}) translate(${k.x}, ${k.y})`,onMouseOver:ze,onMouseLeave:je,onMouseUp:$e,onWheel:Je,onMouseMove:e=>Ee(e.movementX,e.movementY),onClick:()=>{I(void 0)},children:[A("image",{onContextMenu:e=>e.preventDefault(),href:U,ref:j,width:g.x>0?g.x:void 0,height:g.y>0?g.y:void 0}),Ze()]}),$&&qe(),Y&&A("rect",{x:"0",y:"0",width:d.x,height:d.y,style:{opacity:.8},onContextMenu:e=>e.preventDefault(),onClick:()=>{W(void 0)}})]})]})};export{Nt as default};
package/dist/Sia.js CHANGED
@@ -1 +1 @@
1
- import{jsx as m,jsxs as Q}from"react/jsx-runtime";import{useRef as k,useState as r,useEffect as l}from"react";import{CSpinner as X}from"@coreui/react";import Cn from"./Canvas/Canvas.js";import _n from"./models/AnnotationTool.js";import Hn from"./Toolbar/Toolbar.js";import wn from"./models/AnnotationMode.js";import Y from"./models/AnnotationStatus.js";const jn=({additionalButtons:Z,allowedTools:T,polygonOperationResult:p={annotationsToDelete:[],polygonsToCreate:[]},annotationSettings:D,uiConfig:E,defaultAnnotationTool:$,defaultLabelId:nn,image:g,isLoading:F=!1,isPolygonSelectionMode:N=!1,initialAnnotations:v=void 0,initialImageLabelIds:C=void 0,initialIsImageJunk:tn=!1,possibleLabels:J,onAnnoCreated:en=(u,H)=>{},onAnnoCreationFinished:on=u=>{},onAnnoChanged:sn=u=>{},onAnnoDeleted:_=(u,H)=>{},onImageLabelsChanged:rn=()=>{},onIsImageJunk:an=()=>{},onNotification:dn=u=>{},onSelectAnnotation:R=u=>{},onTimeTravel:cn=u=>{}})=>{const H=k(null),[j,B]=r(),[I,f]=r([]),[L,ln]=r(),[c,w]=r(),[x,P]=r([]),[fn,un]=r(),[A,h]=r(),[U,W]=r($??_n.Point),a=k(I);l(()=>{a.current=I},[I]);const y=k(c);l(()=>{y.current=c},[c]);const S=n=>{const t=[...n];P(e=>{const s=[...e];return y.current!==void 0&&s.splice(y.current+1),s.push(t),s}),y.current=void 0,w(void 0)},[In,q]=r(C),[z,G]=r(),[b,mn]=r(!1),[M,V]=r([]),K=n=>{const t=a.current,e=t.findIndex(i=>i.internalId===n);if(e===-1)return;const s=[...t],o=s.splice(e,1)[0];a.current=s,f(s),h(void 0),S(s),_(o,s)},An=()=>{A!==void 0&&K(A.internalId)},hn=()=>{let n=0;const t=v.map(e=>({...e,internalId:n++,mode:wn.VIEW,selectedNode:1,status:e.status,annoTime:e.annoTime??0}));V([...new Array(n).keys()]),f(t),S(t)},gn=()=>{let n=0;for(;M.includes(n);)n++;const t=[...M];return t.push(n),V(t),n},vn=n=>{const t=[...a.current],e=t.findIndex(i=>i.internalId===A?.internalId);if(e===-1)return;const s=t.splice(e,1)[0];_(s,t);const o=[...t];o.push(n),f(o),h(n)},O=n=>{G(n),an(n)},xn=n=>{const t=c??x.length-1,e=t==x.length-1,s=t==0;if(e&&!n||s&&n)return;const o=t+(n?-1:1);w(o)},yn=n=>{const t=[],e=[],s=[];for(const o of n){const i=I.find(d=>d.internalId===o.internalId);i?JSON.stringify(i)!==JSON.stringify(o)&&s.push(o):t.push(o)}for(const o of I)n.find(d=>d.internalId===o.internalId)||e.push(o);return{addedAnnotations:t,removedAnnotations:e,changedAnnotations:s}};l(()=>{if(c==null||c<0||c>x.length-1)return;const t=[...x[c]];f(t);const e=yn(t);cn(e)},[c]),l(()=>{g===void 0&&(f([]),h(void 0),P([]),w(void 0))},[g]),l(()=>{G(tn),!(g!==void 0||v===void 0||v.length===0)&&hn()},[v]),l(()=>{q(C)},[C]),l(()=>{const t={...{canCreate:!0,canEdit:!0,canHaveMultipleLabels:!1,canLabel:!0,minimalArea:250},...D};ln(t)},[D]),l(()=>{const t={...{nodeRadius:4,strokeWidth:4,imageCentered:!1},...E};un(t)},[E]),l(()=>{const n={bbox:!0,point:!0,line:!0,junk:!0,polygon:!0};if(T===void 0)return B(n);B(T)},[T]);const Sn={position:"fixed",top:0,left:0,zIndex:1040,backgroundColor:"#ffff",width:"100%",height:"100%",padding:15},Tn={flex:"1 1 auto",minHeight:0,display:"flex",flexDirection:"column"};return j===void 0?m("div",{className:"d-flex justify-content-center",children:m(X,{color:"primary",style:{width:"5rem",height:"5rem"}})}):Q("div",{style:{...b?Sn:{},flex:"1 1 auto",minHeight:0,display:"flex",flexDirection:"column"},children:[m("div",{ref:H,style:{marginBottom:10},children:m(Hn,{annotationSettings:L,allowedTools:j,additionalButtons:Z,isDisabled:F,isFullscreen:b,isImageJunk:z,imageLabelIds:In,possibleLabels:J,selectedTool:U,onImageLabelsChanged:n=>{q(n),rn(n)},onSetIsFullscreen:mn,onSetIsImageJunk:O,onSetSelectedTool:W,onShouldDeleteSelectedAnnotation:An})}),Q("div",{style:Tn,children:[F&&m("div",{className:"d-flex justify-content-center",children:m(X,{color:"primary",style:{width:"5rem",height:"5rem",marginTop:200}})}),g&&I&&m(Cn,{annotations:I,annotationSettings:L,defaultLabelId:nn,image:g,isFullscreen:b,isImageJunk:z,isPolygonSelectionMode:N,selectedAnnotation:A,selectedAnnoTool:U,polygonOperationResult:p,possibleLabels:J,uiConfig:fn,onAnnoCreated:n=>{const t=[...a.current,n];a.current=t,f(t),en(n,t),h(n)},onAnnoChanged:n=>{const t=a.current.findIndex(e=>e.internalId===n.internalId);if(t!==-1){const e=[...a.current];e[t]=n,a.current=e,f(e),n.status!==Y.CREATING&&S(e)}sn(n)},onAnnoCreationFinished:(n,t)=>{const e={...n,status:Y.CREATED},s=N&&p?.annotationsToDelete!==void 0?[...p.annotationsToDelete,...A!==void 0?[A]:[]]:[],o=[...a.current];for(const i of s){const d=o.findIndex(pn=>pn.internalId===i.internalId);d!==-1&&o.splice(d,1)}if(t){const i=o.findIndex(d=>d.internalId===e.internalId);i!==-1&&o.splice(i,1),o.push(e)}else{const i=o.findIndex(d=>d.internalId===e.internalId);i!==-1&&(o[i]=e)}a.current=o,f(o),S(o),h(e),R(e);for(const i of s)_(i);on(e)},onAnnoEditing:vn,onSetIsImageJunk:O,onNotification:dn,onRequestNewAnnoId:gn,onSelectAnnotation:n=>{h(n),R(n)},onSetSelectedTool:W,onShouldDeleteAnno:K,onTraverseAnnotationHistory:xn})]})]})};export{jn as default};
1
+ import{jsx as m,jsxs as Q}from"react/jsx-runtime";import{useRef as k,useState as r,useEffect as c}from"react";import{CSpinner as X}from"@coreui/react";import Cn from"./Canvas/Canvas.js";import _n from"./models/AnnotationTool.js";import Hn from"./Toolbar/Toolbar.js";import wn from"./models/AnnotationMode.js";import Y from"./models/AnnotationStatus.js";const jn=({additionalButtons:Z,allowedTools:T,polygonOperationResult:p={annotationsToDelete:[],polygonsToCreate:[]},annotationSettings:D,uiConfig:E,defaultAnnotationTool:$,defaultLabelId:nn,image:g,isLoading:F=!1,isPolygonSelectionMode:N=!1,initialAnnotations:v=void 0,initialImageLabelIds:C=void 0,initialIsImageJunk:tn=!1,possibleLabels:J,onAnnoCreated:en=(f,H)=>{},onAnnoCreationFinished:on=f=>{},onAnnoChanged:sn=f=>{},onAnnoDeleted:_=(f,H)=>{},onImageLabelsChanged:rn=()=>{},onIsImageJunk:an=()=>{},onNotification:dn=f=>{},onSelectAnnotation:R=f=>{},onTimeTravel:ln=f=>{}})=>{const H=k(null),[j,L]=r(),[I,u]=r([]),[B,cn]=r(),[l,w]=r(),[x,P]=r([]),[un,fn]=r(),[A,h]=r(),[U,W]=r($??_n.Point),a=k(I);c(()=>{a.current=I},[I]);const y=k(l);c(()=>{y.current=l},[l]);const S=n=>{const t=[...n];P(e=>{const s=[...e];return y.current!==void 0&&s.splice(y.current+1),s.push(t),s}),y.current=void 0,w(void 0)},[In,q]=r(C),[z,G]=r(),[b,mn]=r(!1),[M,V]=r([]),K=n=>{const t=a.current,e=t.findIndex(i=>i.internalId===n);if(e===-1)return;const s=[...t],o=s.splice(e,1)[0];a.current=s,u(s),h(void 0),S(s),_(o,s)},An=()=>{A!==void 0&&K(A.internalId)},hn=()=>{let n=0;const t=v.map(e=>({...e,internalId:n++,mode:wn.VIEW,selectedNode:1,status:e.status,annoTime:e.annoTime??0}));V([...new Array(n).keys()]),u(t),S(t)},gn=()=>{let n=0;for(;M.includes(n);)n++;const t=[...M];return t.push(n),V(t),n},vn=n=>{const t=[...a.current],e=t.findIndex(i=>i.internalId===A?.internalId);if(e===-1)return;const s=t.splice(e,1)[0];_(s,t);const o=[...t];o.push(n),u(o),h(n)},O=n=>{G(n),an(n)},xn=n=>{const t=l??x.length-1,e=t==x.length-1,s=t==0;if(e&&!n||s&&n)return;const o=t+(n?-1:1);w(o)},yn=n=>{const t=[],e=[],s=[];for(const o of n){const i=I.find(d=>d.internalId===o.internalId);i?JSON.stringify(i)!==JSON.stringify(o)&&s.push(o):t.push(o)}for(const o of I)n.find(d=>d.internalId===o.internalId)||e.push(o);return{addedAnnotations:t,removedAnnotations:e,changedAnnotations:s}};c(()=>{if(l==null||l<0||l>x.length-1)return;const t=[...x[l]];u(t);const e=yn(t);ln(e)},[l]),c(()=>{g===void 0&&(u([]),h(void 0),P([]),w(void 0))},[g]),c(()=>{G(tn),!(g!==void 0||v===void 0||v.length===0)&&hn()},[v]),c(()=>{q(C)},[C]),c(()=>{const t={...{canCreate:!0,canEdit:!0,canHaveMultipleLabels:!1,canLabel:!0,minimalArea:250},...D};cn(t)},[D]),c(()=>{const t={...{nodeRadius:4,strokeWidth:4,imageCentered:!1},...E};fn(t)},[E]),c(()=>{const n={bbox:!0,point:!0,line:!0,junk:!0,polygon:!0,imageLabel:!0,delete:!0};if(T===void 0)return L(n);L({...n,...T})},[T]);const Sn={position:"fixed",top:0,left:0,zIndex:1040,backgroundColor:"#ffff",width:"100%",height:"100%",padding:15},Tn={flex:"1 1 auto",minHeight:0,display:"flex",flexDirection:"column"};return j===void 0?m("div",{className:"d-flex justify-content-center",children:m(X,{color:"primary",style:{width:"5rem",height:"5rem"}})}):Q("div",{style:{...b?Sn:{},flex:"1 1 auto",minHeight:0,display:"flex",flexDirection:"column"},children:[m("div",{ref:H,style:{marginBottom:10},children:m(Hn,{annotationSettings:B,allowedTools:j,additionalButtons:Z,isDisabled:F,isFullscreen:b,isImageJunk:z,imageLabelIds:In,possibleLabels:J,selectedTool:U,onImageLabelsChanged:n=>{q(n),rn(n)},onSetIsFullscreen:mn,onSetIsImageJunk:O,onSetSelectedTool:W,onShouldDeleteSelectedAnnotation:An})}),Q("div",{style:Tn,children:[F&&m("div",{className:"d-flex justify-content-center",children:m(X,{color:"primary",style:{width:"5rem",height:"5rem",marginTop:200}})}),g&&I&&m(Cn,{annotations:I,annotationSettings:B,defaultLabelId:nn,image:g,isFullscreen:b,isImageJunk:z,isPolygonSelectionMode:N,selectedAnnotation:A,selectedAnnoTool:U,polygonOperationResult:p,possibleLabels:J,uiConfig:un,onAnnoCreated:n=>{const t=[...a.current,n];a.current=t,u(t),en(n,t),h(n)},onAnnoChanged:n=>{const t=a.current.findIndex(e=>e.internalId===n.internalId);if(t!==-1){const e=[...a.current];e[t]=n,a.current=e,u(e),n.status!==Y.CREATING&&S(e)}sn(n)},onAnnoCreationFinished:(n,t)=>{const e={...n,status:Y.CREATED},s=N&&p?.annotationsToDelete!==void 0?[...p.annotationsToDelete,...A!==void 0?[A]:[]]:[],o=[...a.current];for(const i of s){const d=o.findIndex(pn=>pn.internalId===i.internalId);d!==-1&&o.splice(d,1)}if(t){const i=o.findIndex(d=>d.internalId===e.internalId);i!==-1&&o.splice(i,1),o.push(e)}else{const i=o.findIndex(d=>d.internalId===e.internalId);i!==-1&&(o[i]=e)}a.current=o,u(o),S(o),h(e),R(e);for(const i of s)_(i);on(e)},onAnnoEditing:vn,onSetIsImageJunk:O,onNotification:dn,onRequestNewAnnoId:gn,onSelectAnnotation:n=>{h(n),R(n)},onSetSelectedTool:W,onShouldDeleteAnno:K,onTraverseAnnotationHistory:xn})]})]})};export{jn as default};
@@ -1 +1 @@
1
- import{jsxs as j,jsx as o}from"react/jsx-runtime";import{CRow as C,CCol as t}from"@coreui/react";import d from"./ToolbarItems/AnnoToolSelector.js";import g from"./ToolbarItems/ImageTools.js";import y from"./ToolbarItems/AccessibilityTools.js";const J=({annotationSettings:a,allowedTools:e,additionalButtons:c,isImageJunk:m=!1,imageLabelIds:f=[],isDisabled:r=!1,isFullscreen:n=!1,possibleLabels:l,selectedTool:s,onImageLabelsChanged:i=()=>{},onSetIsFullscreen:p=()=>{},onSetIsImageJunk:x=()=>{},onSetSelectedTool:u=()=>{},onShouldDeleteSelectedAnnotation:h=()=>{}})=>j(C,{className:"d-flex justify-content-center flex-wrap align-items-center gap-0 py-2 px-4",children:[o(t,{xs:"auto",children:o(g,{canJunk:e.junk,isImageJunk:m,imageLabelIds:f,isDisabled:r,isFullscreen:n,possibleLabels:l,onImageLabelsChanged:i,onSetIsImageJunk:x})}),o(t,{xs:"auto",children:o(y,{isDisabled:r,isFullscreen:n,onSetIsFullscreen:p})}),a.canCreate&&o(t,{xs:"auto",children:o(d,{allowedTools:e,isDisabled:r,selectedTool:s,onSetSelectedTool:u,onShouldDeleteSelectedAnnotation:h})}),c]});export{J as default};
1
+ import{jsxs as h,jsx as e}from"react/jsx-runtime";import{CRow as j,CCol as t}from"@coreui/react";import C from"./ToolbarItems/AnnoToolSelector.js";import y from"./ToolbarItems/ImageTools.js";import b from"./ToolbarItems/AccessibilityTools.js";const I=({annotationSettings:n,allowedTools:o,additionalButtons:c,isImageJunk:m=!1,imageLabelIds:f=[],isDisabled:r=!1,isFullscreen:a=!1,possibleLabels:i,selectedTool:l,onImageLabelsChanged:s=()=>{},onSetIsFullscreen:p=()=>{},onSetIsImageJunk:x=()=>{},onSetSelectedTool:u=()=>{},onShouldDeleteSelectedAnnotation:g=()=>{}})=>h(j,{className:"d-flex justify-content-center flex-wrap align-items-center gap-0 py-2 px-4",children:[e(t,{xs:"auto",children:e(y,{canJunk:o.junk,isImageJunk:m,canLabel:o.imageLabel,imageLabelIds:f,isDisabled:r,isFullscreen:a,possibleLabels:i,onImageLabelsChanged:s,onSetIsImageJunk:x})}),e(t,{xs:"auto",children:e(b,{isDisabled:r,isFullscreen:a,onSetIsFullscreen:p})}),n.canCreate&&e(t,{xs:"auto",children:e(C,{allowedTools:o,isDisabled:r,selectedTool:l,onSetSelectedTool:u,onShouldDeleteSelectedAnnotation:g})}),c]});export{I as default};
@@ -1 +1 @@
1
- import{jsxs as l,jsx as i}from"react/jsx-runtime";import{CButtonGroup as e}from"@coreui/react";import{pointIcon as c,lineIcon as x,bBoxIcon as u,polygonIcon as g}from"../../utils/siaIcons.js";import o from"../../models/AnnotationTool.js";import{faTrash as y}from"@fortawesome/free-solid-svg-icons";import t from"../../IconButton.js";const k=({allowedTools:r,isDisabled:n=!1,selectedTool:a,onSetSelectedTool:p,onShouldDeleteSelectedAnnotation:m=()=>{}})=>l(e,{role:"group","aria-label":"Basic example",children:[r.point&&i(t,{color:"primary",text:c(),isOutline:a!=o.Point,disabled:n,onClick:()=>p(o.Point),tooltip:"Create Point Annotation",margin:0}),r.line&&i(t,{color:"primary",text:x(),isOutline:a!=o.Line,disabled:n,onClick:()=>p(o.Line),tooltip:"Create Line Annotation",margin:0}),r.bbox&&i(t,{color:"primary",text:u(),isOutline:a!=o.BBox,disabled:n,onClick:()=>p(o.BBox),tooltip:"Create BBox Annotation",margin:0}),r.polygon&&i(t,{color:"primary",text:g(),isOutline:a!=o.Polygon,disabled:n,onClick:()=>p(o.Polygon),tooltip:"Create Polygon Annotation",margin:0}),i(t,{color:"primary",icon:y,isOutline:!0,disabled:n,onClick:m,tooltip:"Delete selected annotation",margin:0})]});export{k as default};
1
+ import{jsxs as e,jsx as i}from"react/jsx-runtime";import{CButtonGroup as l}from"@coreui/react";import{pointIcon as c,lineIcon as x,bBoxIcon as u,polygonIcon as g}from"../../utils/siaIcons.js";import o from"../../models/AnnotationTool.js";import{faTrash as y}from"@fortawesome/free-solid-svg-icons";import r from"../../IconButton.js";const k=({allowedTools:n,isDisabled:t=!1,selectedTool:a,onSetSelectedTool:p,onShouldDeleteSelectedAnnotation:m=()=>{}})=>e(l,{role:"group","aria-label":"Basic example",children:[n.point&&i(r,{color:"primary",text:c(),isOutline:a!=o.Point,disabled:t,onClick:()=>p(o.Point),tooltip:"Create Point Annotation",margin:0}),n.line&&i(r,{color:"primary",text:x(),isOutline:a!=o.Line,disabled:t,onClick:()=>p(o.Line),tooltip:"Create Line Annotation",margin:0}),n.bbox&&i(r,{color:"primary",text:u(),isOutline:a!=o.BBox,disabled:t,onClick:()=>p(o.BBox),tooltip:"Create BBox Annotation",margin:0}),n.polygon&&i(r,{color:"primary",text:g(),isOutline:a!=o.Polygon,disabled:t,onClick:()=>p(o.Polygon),tooltip:"Create Polygon Annotation",margin:0}),n.delete&&i(r,{color:"primary",icon:y,isOutline:!0,disabled:t,onClick:m,tooltip:"Delete selected annotation",margin:0})]});export{k as default};
@@ -1,6 +1,7 @@
1
1
  import { Label } from '../../types';
2
2
  type ImageToolsProps = {
3
3
  canJunk: boolean;
4
+ canLabel?: boolean;
4
5
  imageLabelIds?: number[];
5
6
  isDisabled?: boolean;
6
7
  isFullscreen?: boolean;
@@ -9,5 +10,5 @@ type ImageToolsProps = {
9
10
  onImageLabelsChanged?: (selectedImageIds: number[]) => void;
10
11
  onSetIsImageJunk?: (isImageJunk: boolean) => void;
11
12
  };
12
- declare const ImageTools: ({ canJunk, isDisabled, isFullscreen, isImageJunk, imageLabelIds, possibleLabels, onImageLabelsChanged, onSetIsImageJunk, }: ImageToolsProps) => import("react/jsx-runtime").JSX.Element;
13
+ declare const ImageTools: ({ canJunk, canLabel, isDisabled, isFullscreen, isImageJunk, imageLabelIds, possibleLabels, onImageLabelsChanged, onSetIsImageJunk, }: ImageToolsProps) => import("react/jsx-runtime").JSX.Element;
13
14
  export default ImageTools;
@@ -1 +1 @@
1
- import{jsxs as n,jsx as r}from"react/jsx-runtime";import{CButtonGroup as c}from"@coreui/react";import{faBan as b}from"@fortawesome/free-solid-svg-icons";import{useState as I,useEffect as d}from"react";import g from"./ImageToolItems/ImageLabelInput.js";import L from"../../IconButton.js";const S=({canJunk:i,isDisabled:o=!1,isFullscreen:e=!1,isImageJunk:t=!1,imageLabelIds:s=[],possibleLabels:l,onImageLabelsChanged:f=()=>{},onSetIsImageJunk:m=()=>{}})=>{const[p,a]=I(!1);return d(()=>{a(!1)},[e]),n(c,{role:"group","aria-label":"Image Tools",children:[l&&r(g,{isDisabled:o,isMultilabel:!0,isFullscreen:e,isVisible:p,selectedLabelsIds:s,possibleLabels:l,onLabelSelect:u=>{a(!1),f(u)}}),i&&r(L,{color:"primary",icon:b,isOutline:!t,disabled:o,onClick:()=>m(!t),tooltip:"Junk image"})]})};export{S as default};
1
+ import{jsxs as c,jsx as a}from"react/jsx-runtime";import{CButtonGroup as b}from"@coreui/react";import{faBan as I}from"@fortawesome/free-solid-svg-icons";import{useState as d,useEffect as g}from"react";import L from"./ImageToolItems/ImageLabelInput.js";import x from"../../IconButton.js";const T=({canJunk:i,canLabel:s=!0,isDisabled:e=!1,isFullscreen:o=!1,isImageJunk:t=!1,imageLabelIds:f=[],possibleLabels:l,onImageLabelsChanged:m=()=>{},onSetIsImageJunk:p=()=>{}})=>{const[u,r]=d(!1);return g(()=>{r(!1)},[o]),c(b,{role:"group","aria-label":"Image Tools",children:[l&&s&&a(L,{isDisabled:e,isMultilabel:!0,isFullscreen:o,isVisible:u,selectedLabelsIds:f,possibleLabels:l,onLabelSelect:n=>{r(!1),m(n)}}),i&&a(x,{color:"primary",icon:I,isOutline:!t,disabled:e,onClick:()=>p(!t),tooltip:"Junk image"})]})};export{T as default};
package/dist/types.d.ts CHANGED
@@ -7,6 +7,8 @@ export type AllowedTools = {
7
7
  line: boolean;
8
8
  point: boolean;
9
9
  polygon: boolean;
10
+ imageLabel?: boolean;
11
+ delete?: boolean;
10
12
  };
11
13
  export type AnnotationSettings = {
12
14
  minimalArea?: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lost-sia",
3
- "version": "3.2.0",
3
+ "version": "3.3.0-rc.1",
4
4
  "description": "Single Image Annotation Tool",
5
5
  "license": "MIT",
6
6
  "repository": "l3p-cv/lost-sia",
@@ -505,18 +505,14 @@ const Canvas = ({
505
505
  }
506
506
 
507
507
  const scaledAnnotations = calculateScaledAnnotations(annotations)
508
-
508
+ // imgSize is measured separately once in the <image> element's 'load' event
509
509
  const resetCanvas = () => {
510
510
  setEditorMode(EditorModes.VIEW)
511
511
 
512
512
  // largest possible annotation size fitting the whole image
513
513
  setStageSize({ x: -1, y: -1 })
514
-
515
- if (imageRef.current !== null) {
516
- const { width, height } = imageRef.current.getBoundingClientRect()
517
- setImgSize({ x: width, y: height })
518
- }
519
-
514
+ // use {0, 0} so it hits the `=== 0` guards
515
+ setImgSize({ x: 0, y: 0 })
520
516
  setSvgScale(1)
521
517
  setSvgTranslation({ x: 0, y: 0 })
522
518
 
@@ -542,11 +538,10 @@ const Canvas = ({
542
538
 
543
539
  // image changed after init -> reset everything
544
540
  useEffect(() => {
545
- if (canvasRef?.current !== undefined) {
541
+ if (canvasRef?.current !== null) {
546
542
  // clear stale sizing state only when the canvas ref is ready, to avoid
547
543
  // leaving imgSize stuck at {x:-1, y:-1} on the early-exit path
548
544
  resetCanvas()
549
-
550
545
  const { width, height } = canvasRef.current!.getBoundingClientRect()
551
546
 
552
547
  // for whatever reason the ref adds the toolbars height to the available space, leading to a container size reaching outside the bottom
@@ -557,7 +552,8 @@ const Canvas = ({
557
552
 
558
553
  // listen for size changes on div element
559
554
  const resizeObserver = new ResizeObserver(() => {
560
- const { width, height } = canvasRef.current!.getBoundingClientRect()
555
+ if (canvasRef.current === null) return
556
+ const { width, height } = canvasRef.current.getBoundingClientRect()
561
557
  const heightWithoutToolbar: number = height - toolbarHeight
562
558
 
563
559
  setCanvasSize({ x: width, y: heightWithoutToolbar })
@@ -587,14 +583,31 @@ const Canvas = ({
587
583
  setCanvasSize({ x: width, y: heightWithoutToolbar })
588
584
  }, [canvasRef])
589
585
 
590
- // notify component about default image size
591
- // read rendered size when image or canvas size changes — no ResizeObserver needed here
592
- // since canvasRef's ResizeObserver already handles container resize events and updates canvasSize
586
+ // notify component about default image size; wait for native 'load' event of <image> element before measuring
593
587
  useEffect(() => {
594
- if (imageRef.current === null) return
588
+ const imageEl = imageRef.current
589
+ if (imageEl === null) return
595
590
 
596
- const { width, height } = imageRef.current.getBoundingClientRect()
597
- setImgSize({ x: width, y: height })
591
+ // guards against a queued 'load' event firing setImgSize after this
592
+ let active = true
593
+
594
+ const measure = () => {
595
+ if (!active) return
596
+ // use the captured imageEl (not imageRef.current) so this always measures
597
+ // the element this effect actually attached the listener to
598
+ const { width, height } = imageEl.getBoundingClientRect()
599
+ setImgSize({ x: width, y: height })
600
+ }
601
+
602
+ // SVGImageElement fires 'load' once the referenced resource is ready
603
+ imageEl.addEventListener('load', measure)
604
+ // in case the image is already loaded/cached by the time this effect runs
605
+ measure()
606
+
607
+ return () => {
608
+ active = false
609
+ imageEl.removeEventListener('load', measure)
610
+ }
598
611
  }, [image, canvasSize])
599
612
 
600
613
  useEffect(() => {
package/src/Sia.tsx CHANGED
@@ -399,11 +399,15 @@ const Sia = ({
399
399
  line: true,
400
400
  junk: true,
401
401
  polygon: true,
402
+ imageLabel: true,
403
+ delete: true,
402
404
  }
403
405
 
404
406
  if (propAllowedTools === undefined) return setAllowedTools(defaultAllowedTools)
405
407
 
406
- setAllowedTools(propAllowedTools)
408
+ // merge so consumers on the old (pre-imageLabel/delete) shape keep those
409
+ // tools enabled by default instead of silently losing them
410
+ setAllowedTools({ ...defaultAllowedTools, ...propAllowedTools })
407
411
  }, [propAllowedTools])
408
412
 
409
413
  // useEffect(() => {
@@ -46,6 +46,7 @@ const Toolbar = ({
46
46
  <ImageTools
47
47
  canJunk={allowedTools.junk}
48
48
  isImageJunk={isImageJunk}
49
+ canLabel={allowedTools.imageLabel}
49
50
  imageLabelIds={imageLabelIds}
50
51
  isDisabled={isDisabled}
51
52
  isFullscreen={isFullscreen}
@@ -79,15 +79,17 @@ const AnnoToolSelector = ({
79
79
  />
80
80
  )}
81
81
 
82
- <IconButton
83
- color="primary"
84
- icon={faTrash}
85
- isOutline={true}
86
- disabled={isDisabled}
87
- onClick={onShouldDeleteSelectedAnnotation}
88
- tooltip="Delete selected annotation"
89
- margin={0}
90
- />
82
+ {allowedTools.delete && (
83
+ <IconButton
84
+ color="primary"
85
+ icon={faTrash}
86
+ isOutline={true}
87
+ disabled={isDisabled}
88
+ onClick={onShouldDeleteSelectedAnnotation}
89
+ tooltip="Delete selected annotation"
90
+ margin={0}
91
+ />
92
+ )}
91
93
  </CButtonGroup>
92
94
  )
93
95
  }
@@ -7,6 +7,7 @@ import IconButton from '../../IconButton'
7
7
 
8
8
  type ImageToolsProps = {
9
9
  canJunk: boolean
10
+ canLabel?: boolean
10
11
  imageLabelIds?: number[]
11
12
  isDisabled?: boolean
12
13
  isFullscreen?: boolean
@@ -18,6 +19,7 @@ type ImageToolsProps = {
18
19
 
19
20
  const ImageTools = ({
20
21
  canJunk,
22
+ canLabel = true,
21
23
  isDisabled = false,
22
24
  isFullscreen = false,
23
25
  isImageJunk = false,
@@ -37,7 +39,7 @@ const ImageTools = ({
37
39
 
38
40
  return (
39
41
  <CButtonGroup role="group" aria-label="Image Tools">
40
- {possibleLabels && (
42
+ {possibleLabels && canLabel && (
41
43
  <ImageLabelInput
42
44
  isDisabled={isDisabled}
43
45
  isMultilabel={true}
@@ -1,36 +1,3 @@
1
- const canvasConfig = {
2
- tools: {
3
- point: true,
4
- line: true,
5
- polygon: true,
6
- bbox: true,
7
- junk: true,
8
- },
9
- annos: {
10
- minArea: 250,
11
- multilabels: false,
12
- actions: {
13
- draw: true,
14
- label: true,
15
- edit: true,
16
- },
17
- maxAnnos: null,
18
- },
19
- img: {
20
- multilabels: false,
21
- actions: {
22
- label: true,
23
- },
24
- },
25
- autoSaveInterval: undefined,
26
- allowedToMarkExample: true,
27
- }
28
-
29
- const filter = {
30
- rotate: { active: false, angle: 0 },
31
- clahe: { active: false, clipLimit: 3 },
32
- }
33
-
34
1
  const uiConfig = {
35
2
  nodeRadius: 4,
36
3
  strokeWidth: 4,
@@ -69,24 +36,6 @@ const imgMeta = {
69
36
  description: null,
70
37
  }
71
38
 
72
- const toolbarEnabled = {
73
- imgLabel: true,
74
- nextPrev: true,
75
- toolSelection: true,
76
- fullscreen: true,
77
- junk: true,
78
- deleteAll: true,
79
- settings: {
80
- infoBoxes: true,
81
- annoStyle: true,
82
- },
83
- filter: {
84
- rotate: true,
85
- clahe: true,
86
- },
87
- help: true,
88
- }
89
-
90
39
  export const noAnnos = { bBoxes: [], lines: [], points: [], polygons: [] }
91
40
  const annos = {
92
41
  bBoxes: [
@@ -29,6 +29,8 @@ const allowedTools: AllowedTools = {
29
29
  point: false,
30
30
  polygon: false,
31
31
  junk: true,
32
+ imageLabel: true,
33
+ delete: true,
32
34
  }
33
35
 
34
36
  export const BBoxOnly: Story = {
@@ -63,12 +65,14 @@ export const BBoxOnly: Story = {
63
65
  },
64
66
  }
65
67
 
66
- const noJunkTools = {
68
+ const noJunkTools: AllowedTools = {
67
69
  bbox: true,
68
70
  line: true,
69
71
  point: true,
70
72
  polygon: true,
71
73
  junk: false,
74
+ imageLabel: true,
75
+ delete: true,
72
76
  }
73
77
 
74
78
  export const NoJunk: Story = {
@@ -35,6 +35,8 @@ const allowedTools: AllowedTools = {
35
35
  line: true,
36
36
  point: true,
37
37
  polygon: true,
38
+ imageLabel: true,
39
+ delete: true,
38
40
  }
39
41
 
40
42
  const limitedAllowedTools: AllowedTools = {
@@ -43,6 +45,8 @@ const limitedAllowedTools: AllowedTools = {
43
45
  line: false,
44
46
  point: false,
45
47
  polygon: false,
48
+ imageLabel: true,
49
+ delete: true,
46
50
  }
47
51
 
48
52
  const annotationSettings: AnnotationSettings = {
package/src/types.ts CHANGED
@@ -8,6 +8,8 @@ export type AllowedTools = {
8
8
  line: boolean
9
9
  point: boolean
10
10
  polygon: boolean
11
+ imageLabel?: boolean
12
+ delete?: boolean
11
13
  }
12
14
 
13
15
  export type AnnotationSettings = {
@@ -1,126 +0,0 @@
1
- import React, { Component } from 'react'
2
-
3
- import * as tbe from './types/toolbarEvents'
4
- import { faCog } from '@fortawesome/free-solid-svg-icons'
5
- import { CFormSwitch, CPopover } from '@coreui/react'
6
- import ToolbarItem from './ToolbarItem'
7
- class SIASettingButton extends Component {
8
- constructor(props) {
9
- super(props)
10
- this.state = {}
11
- }
12
-
13
- triggerEvent(e, data) {
14
- if (this.props.onSettingEvent) {
15
- this.props.onSettingEvent(e, data)
16
- }
17
- }
18
- toggleAnnoDetails() {
19
- this.triggerEvent(tbe.SHOW_ANNO_DETAILS)
20
- }
21
-
22
- toggleLabelInfo() {
23
- this.triggerEvent(tbe.SHOW_LABEL_INFO)
24
- }
25
-
26
- toggleAnnoStats() {
27
- this.triggerEvent(tbe.SHOW_ANNO_STATS)
28
- }
29
-
30
- handleStrokeWidthChange(e) {
31
- this.triggerEvent(tbe.EDIT_STROKE_WIDTH, parseInt(e.target.value))
32
- }
33
-
34
- handleNodeRadiusChange(e) {
35
- this.triggerEvent(tbe.EDIT_NODE_RADIUS, parseInt(e.target.value))
36
- }
37
-
38
- renderInfoBoxContent() {
39
- return (
40
- <div>
41
- <h4>Info Boxes</h4>
42
- <CFormSwitch
43
- checked={this.props.uiConfig.annoDetails.visible}
44
- label="Annotation Details"
45
- size="xl"
46
- onClick={() => this.toggleAnnoDetails()}
47
- />
48
- <CFormSwitch
49
- checked={this.props.uiConfig.labelInfo.visible}
50
- label="Label Info"
51
- size="xl"
52
- onClick={() => this.toggleLabelInfo()}
53
- />
54
- <CFormSwitch
55
- checked={this.props.uiConfig.annoStats.visible}
56
- label="Anno Stats"
57
- size="xl"
58
- onClick={() => this.toggleAnnoStats()}
59
- />
60
- </div>
61
- )
62
- }
63
- renderInfoBoxes() {
64
- if (!this.props.enabled) return null
65
- if (this.props.enabled === true) {
66
- return this.renderInfoBoxContent()
67
- } else {
68
- if (this.props.enabled.infoBoxes) {
69
- return this.renderInfoBoxContent()
70
- }
71
- }
72
- }
73
-
74
- renderAnnoStyle() {
75
- if (!this.props.enabled) return null
76
- if (this.props.enabled === true) {
77
- return this.renderAnnoStyleContent()
78
- } else {
79
- if (this.props.enabled.annoStyle) {
80
- return this.renderAnnoStyleContent()
81
- }
82
- }
83
- }
84
- renderAnnoStyleContent() {
85
- return (
86
- <div>
87
- <h4 style={{ marginTop: 25 }}>Anno Appearance</h4>
88
- <div>Stroke width: {this.props.uiConfig.strokeWidth}</div>
89
- <input
90
- type="range"
91
- min={1}
92
- max={10}
93
- value={this.props.uiConfig.strokeWidth}
94
- onChange={(e) => this.handleStrokeWidthChange(e)}
95
- />
96
- <div>Node radius: {this.props.uiConfig.nodeRadius}</div>
97
- <input
98
- type="range"
99
- min={1}
100
- max={10}
101
- value={this.props.uiConfig.nodeRadius}
102
- onChange={(e) => this.handleNodeRadiusChange(e)}
103
- />
104
- </div>
105
- )
106
- }
107
-
108
- render() {
109
- if (!this.props.uiConfig) return null
110
- const popupContent = (
111
- <div>
112
- {this.renderInfoBoxes()}
113
- {this.renderAnnoStyle()}
114
- </div>
115
- )
116
- return (
117
- <CPopover content={popupContent} placement="right">
118
- <span>
119
- <ToolbarItem faIcon={faCog} />
120
- </span>
121
- </CPopover>
122
- )
123
- }
124
- }
125
-
126
- export default SIASettingButton