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.
- package/dist/Canvas/Canvas.js +1 -1
- package/dist/Sia.js +1 -1
- package/dist/Toolbar/Toolbar.js +1 -1
- package/dist/Toolbar/ToolbarItems/AnnoToolSelector.js +1 -1
- package/dist/Toolbar/ToolbarItems/ImageTools.d.ts +2 -1
- package/dist/Toolbar/ToolbarItems/ImageTools.js +1 -1
- package/dist/types.d.ts +2 -0
- package/package.json +1 -1
- package/src/Canvas/Canvas.tsx +29 -16
- package/src/Sia.tsx +5 -1
- package/src/Toolbar/Toolbar.tsx +1 -0
- package/src/Toolbar/ToolbarItems/AnnoToolSelector.tsx +11 -9
- package/src/Toolbar/ToolbarItems/ImageTools.tsx +3 -1
- package/src/siaDummyData.js +0 -51
- package/src/stories/AnnotationTools.stories.tsx +5 -1
- package/src/stories/Toolbar/Toolbar.stories.tsx +4 -0
- package/src/types.ts +2 -0
- package/src/SIASettingButton.jsx +0 -126
package/dist/Canvas/Canvas.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{jsxs as
|
|
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
|
|
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};
|
package/dist/Toolbar/Toolbar.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{jsxs as
|
|
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
|
|
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
|
|
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
package/package.json
CHANGED
package/src/Canvas/Canvas.tsx
CHANGED
|
@@ -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
|
-
|
|
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 !==
|
|
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
|
-
|
|
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
|
-
|
|
588
|
+
const imageEl = imageRef.current
|
|
589
|
+
if (imageEl === null) return
|
|
595
590
|
|
|
596
|
-
|
|
597
|
-
|
|
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
|
-
|
|
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(() => {
|
package/src/Toolbar/Toolbar.tsx
CHANGED
|
@@ -79,15 +79,17 @@ const AnnoToolSelector = ({
|
|
|
79
79
|
/>
|
|
80
80
|
)}
|
|
81
81
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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}
|
package/src/siaDummyData.js
CHANGED
|
@@ -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
package/src/SIASettingButton.jsx
DELETED
|
@@ -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
|