@ -84,30 +84,34 @@
return area >= MIN _SHAPE _AREA ;
return area >= MIN _SHAPE _AREA ;
}
}
function fitPoints ( shapeType , points , maxX , maxY ) {
function rotatePoint ( x , y , angle , cx = 0 , cy = 0 ) {
const fittedPoints = [ ] ;
const sin = Math . sin ( ( angle * Math . PI ) / 180 ) ;
const cos = Math . cos ( ( angle * Math . PI ) / 180 ) ;
for ( let i = 0 ; i < points . length - 1 ; i += 2 ) {
const rotX = ( x - cx ) * cos - ( y - cy ) * sin + cx ;
const x = points [ i ] ;
const rotY = ( y - cy ) * cos + ( x - cx ) * sin + cy ;
const y = points [ i + 1 ] ;
return [ rotX , rotY ] ;
}
checkObjectType ( 'coordinate' , x , 'number' , null ) ;
function fitPoints ( shapeType , points , rotation , maxX , maxY ) {
checkObjectType ( 'coordinate' , y , 'number' , null ) ;
checkObjectType ( 'rotation' , rotation , 'number' , null ) ;
points . forEach ( ( coordinate ) => checkObjectType ( 'coordinate' , coordinate , 'number' , null ) ) ;
fittedPoints . push ( Math . clamp ( x , 0 , maxX ) , Math . clamp ( y , 0 , maxY ) ) ;
if ( shapeType === ObjectShape . CUBOID || ! ! rotation ) {
// cuboids and rotated bounding boxes cannot be fitted
return points ;
}
}
return shapeType === ObjectShape . CUBOID ? points : fittedPoints ;
const fittedPoints = [ ] ;
}
function checkOutside ( points , width , height ) {
let inside = false ;
for ( let i = 0 ; i < points . length - 1 ; i += 2 ) {
for ( let i = 0 ; i < points . length - 1 ; i += 2 ) {
const [ x , y ] = points . slice ( i ) ;
const x = points [ i ] ;
inside = inside || ( x >= 0 && x <= width && y >= 0 && y <= height ) ;
const y = points [ i + 1 ] ;
const clampedX = Math . clamp ( x , 0 , maxX ) ;
const clampedY = Math . clamp ( y , 0 , maxY ) ;
fittedPoints . push ( clampedX , clampedY ) ;
}
}
return ! inside ;
return fittedPo int s;
}
}
function validateAttributeValue ( value , attr ) {
function validateAttributeValue ( value , attr ) {
@ -345,13 +349,13 @@
checkNumberOfPoints ( this . shapeType , data . points ) ;
checkNumberOfPoints ( this . shapeType , data . points ) ;
// cut points
// cut points
const { width , height , filename } = this . frameMeta [ frame ] ;
const { width , height , filename } = this . frameMeta [ frame ] ;
fittedPoints = fitPoints ( this . shapeType , data . points , width, height ) ;
fittedPoints = fitPoints ( this . shapeType , data . points , data. rotation , width, height ) ;
let check = true ;
let check = true ;
if ( filename && filename . slice ( filename . length - 3 ) === 'pcd' ) {
if ( filename && filename . slice ( filename . length - 3 ) === 'pcd' ) {
check = false ;
check = false ;
}
}
if ( check ) {
if ( check ) {
if ( ! checkShapeArea ( this . shapeType , fittedPoints ) || checkOutside ( fittedPoints , width , height ) ) {
if ( ! checkShapeArea ( this . shapeType , fittedPoints ) ) {
fittedPoints = [ ] ;
fittedPoints = [ ] ;
}
}
}
}
@ -492,6 +496,7 @@
constructor ( data , clientID , color , injection ) {
constructor ( data , clientID , color , injection ) {
super ( data , clientID , color , injection ) ;
super ( data , clientID , color , injection ) ;
this . points = data . points ;
this . points = data . points ;
this . rotation = data . rotation || 0 ;
this . occluded = data . occluded ;
this . occluded = data . occluded ;
this . zOrder = data . z _order ;
this . zOrder = data . z _order ;
}
}
@ -504,6 +509,7 @@
occluded : this . occluded ,
occluded : this . occluded ,
z _order : this . zOrder ,
z _order : this . zOrder ,
points : [ ... this . points ] ,
points : [ ... this . points ] ,
rotation : this . rotation ,
attributes : Object . keys ( this . attributes ) . reduce ( ( attributeAccumulator , attrId ) => {
attributes : Object . keys ( this . attributes ) . reduce ( ( attributeAccumulator , attrId ) => {
attributeAccumulator . push ( {
attributeAccumulator . push ( {
spec _id : attrId ,
spec _id : attrId ,
@ -535,6 +541,7 @@
lock : this . lock ,
lock : this . lock ,
zOrder : this . zOrder ,
zOrder : this . zOrder ,
points : [ ... this . points ] ,
points : [ ... this . points ] ,
rotation : this . rotation ,
attributes : { ... this . attributes } ,
attributes : { ... this . attributes } ,
descriptions : [ ... this . descriptions ] ,
descriptions : [ ... this . descriptions ] ,
label : this . label ,
label : this . label ,
@ -548,9 +555,11 @@
} ;
} ;
}
}
_savePoints ( points , frame) {
_savePoints ( points , rotation, frame) {
const undoPoints = this . points ;
const undoPoints = this . points ;
const undoRotation = this . rotation ;
const redoPoints = points ;
const redoPoints = points ;
const redoRotation = rotation ;
const undoSource = this . source ;
const undoSource = this . source ;
const redoSource = Source . MANUAL ;
const redoSource = Source . MANUAL ;
@ -559,11 +568,13 @@
( ) => {
( ) => {
this . points = undoPoints ;
this . points = undoPoints ;
this . source = undoSource ;
this . source = undoSource ;
this . rotation = undoRotation ;
this . updated = Date . now ( ) ;
this . updated = Date . now ( ) ;
} ,
} ,
( ) => {
( ) => {
this . points = redoPoints ;
this . points = redoPoints ;
this . source = redoSource ;
this . source = redoSource ;
this . rotation = redoRotation ;
this . updated = Date . now ( ) ;
this . updated = Date . now ( ) ;
} ,
} ,
[ this . clientID ] ,
[ this . clientID ] ,
@ -572,6 +583,7 @@
this . source = Source . MANUAL ;
this . source = Source . MANUAL ;
this . points = points ;
this . points = points ;
this . rotation = rotation ;
}
}
_saveOccluded ( occluded , frame ) {
_saveOccluded ( occluded , frame ) {
@ -637,6 +649,7 @@
const updated = data . updateFlags ;
const updated = data . updateFlags ;
const fittedPoints = this . _validateStateBeforeSave ( frame , data , updated ) ;
const fittedPoints = this . _validateStateBeforeSave ( frame , data , updated ) ;
const { rotation } = data ;
// Now when all fields are validated, we can apply them
// Now when all fields are validated, we can apply them
if ( updated . label ) {
if ( updated . label ) {
@ -652,7 +665,7 @@
}
}
if ( updated . points && fittedPoints . length ) {
if ( updated . points && fittedPoints . length ) {
this . _savePoints ( fittedPoints , frame) ;
this . _savePoints ( fittedPoints , rotation, frame) ;
}
}
if ( updated . occluded ) {
if ( updated . occluded ) {
@ -696,6 +709,7 @@
zOrder : value . z _order ,
zOrder : value . z _order ,
points : value . points ,
points : value . points ,
outside : value . outside ,
outside : value . outside ,
rotation : value . rotation || 0 ,
attributes : value . attributes . reduce ( ( attributeAccumulator , attr ) => {
attributes : value . attributes . reduce ( ( attributeAccumulator , attr ) => {
attributeAccumulator [ attr . spec _id ] = attr . value ;
attributeAccumulator [ attr . spec _id ] = attr . value ;
return attributeAccumulator ;
return attributeAccumulator ;
@ -736,6 +750,7 @@
occluded : this . shapes [ frame ] . occluded ,
occluded : this . shapes [ frame ] . occluded ,
z _order : this . shapes [ frame ] . zOrder ,
z _order : this . shapes [ frame ] . zOrder ,
points : [ ... this . shapes [ frame ] . points ] ,
points : [ ... this . shapes [ frame ] . points ] ,
rotation : this . shapes [ frame ] . rotation ,
outside : this . shapes [ frame ] . outside ,
outside : this . shapes [ frame ] . outside ,
attributes : Object . keys ( this . shapes [ frame ] . attributes ) . reduce (
attributes : Object . keys ( this . shapes [ frame ] . attributes ) . reduce (
( attributeAccumulator , attrId ) => {
( attributeAccumulator , attrId ) => {
@ -1009,22 +1024,21 @@
) ;
) ;
}
}
_savePoints ( points , frame) {
_savePoints ( points , rotation, frame) {
const current = this . get ( frame ) ;
const current = this . get ( frame ) ;
const wasKeyframe = frame in this . shapes ;
const wasKeyframe = frame in this . shapes ;
const undoSource = this . source ;
const undoSource = this . source ;
const redoSource = Source . MANUAL ;
const redoSource = Source . MANUAL ;
const undoShape = wasKeyframe ? this . shapes [ frame ] : undefined ;
const undoShape = wasKeyframe ? this . shapes [ frame ] : undefined ;
const redoShape = wasKeyframe ?
const redoShape = wasKeyframe ? { ... this . shapes [ frame ] , points , rotation } : {
{ ... this . shapes [ frame ] , points } :
frame ,
{
points ,
frame ,
rotation ,
points ,
zOrder : current . zOrder ,
zOrder : current . zOrder ,
outside : current . outside ,
outside : current . outside ,
occluded : current . occluded ,
occluded : current . occluded ,
attributes : { } ,
attributes : { } ,
} ;
} ;
this . shapes [ frame ] = redoShape ;
this . shapes [ frame ] = redoShape ;
this . source = Source . MANUAL ;
this . source = Source . MANUAL ;
@ -1049,6 +1063,7 @@
{
{
frame ,
frame ,
outside ,
outside ,
rotation : current . rotation ,
zOrder : current . zOrder ,
zOrder : current . zOrder ,
points : current . points ,
points : current . points ,
occluded : current . occluded ,
occluded : current . occluded ,
@ -1078,6 +1093,7 @@
{
{
frame ,
frame ,
occluded ,
occluded ,
rotation : current . rotation ,
zOrder : current . zOrder ,
zOrder : current . zOrder ,
points : current . points ,
points : current . points ,
outside : current . outside ,
outside : current . outside ,
@ -1107,6 +1123,7 @@
{
{
frame ,
frame ,
zOrder ,
zOrder ,
rotation : current . rotation ,
occluded : current . occluded ,
occluded : current . occluded ,
points : current . points ,
points : current . points ,
outside : current . outside ,
outside : current . outside ,
@ -1139,6 +1156,7 @@
const redoShape = keyframe ?
const redoShape = keyframe ?
{
{
frame ,
frame ,
rotation : current . rotation ,
zOrder : current . zOrder ,
zOrder : current . zOrder ,
points : current . points ,
points : current . points ,
outside : current . outside ,
outside : current . outside ,
@ -1172,6 +1190,7 @@
const updated = data . updateFlags ;
const updated = data . updateFlags ;
const fittedPoints = this . _validateStateBeforeSave ( frame , data , updated ) ;
const fittedPoints = this . _validateStateBeforeSave ( frame , data , updated ) ;
const { rotation } = data ;
if ( updated . label ) {
if ( updated . label ) {
this . _saveLabel ( data . label , frame ) ;
this . _saveLabel ( data . label , frame ) ;
@ -1194,7 +1213,7 @@
}
}
if ( updated . points && fittedPoints . length ) {
if ( updated . points && fittedPoints . length ) {
this . _savePoints ( fittedPoints , frame) ;
this . _savePoints ( fittedPoints , rotation, frame) ;
}
}
if ( updated . outside ) {
if ( updated . outside ) {
@ -1246,6 +1265,7 @@
if ( leftPosition ) {
if ( leftPosition ) {
return {
return {
points : [ ... leftPosition . points ] ,
points : [ ... leftPosition . points ] ,
rotation : leftPosition . rotation ,
occluded : leftPosition . occluded ,
occluded : leftPosition . occluded ,
outside : leftPosition . outside ,
outside : leftPosition . outside ,
zOrder : leftPosition . zOrder ,
zOrder : leftPosition . zOrder ,
@ -1256,10 +1276,11 @@
if ( rightPosition ) {
if ( rightPosition ) {
return {
return {
points : [ ... rightPosition . points ] ,
points : [ ... rightPosition . points ] ,
rotation : rightPosition . rotation ,
occluded : rightPosition . occluded ,
occluded : rightPosition . occluded ,
outside : true ,
zOrder : rightPosition . zOrder ,
zOrder : rightPosition . zOrder ,
keyframe : targetFrame in this . shapes ,
keyframe : targetFrame in this . shapes ,
outside : true ,
} ;
} ;
}
}
@ -1356,20 +1377,28 @@
checkNumberOfPoints ( this . shapeType , this . points ) ;
checkNumberOfPoints ( this . shapeType , this . points ) ;
}
}
static distance ( points , x , y ) {
static distance ( points , x , y , angle ) {
const [ xtl , ytl , xbr , ybr ] = points ;
const [ xtl , ytl , xbr , ybr ] = points ;
const cx = xtl + ( xbr - xtl ) / 2 ;
const cy = ytl + ( ybr - ytl ) / 2 ;
const [ rotX , rotY ] = rotatePoint ( x , y , - angle , cx , cy ) ;
if ( ! ( x >= xtl && x <= xbr && y >= ytl && y <= ybr ) ) {
if ( ! ( rotX >= xtl && rotX <= xbr && rotY >= ytl && rotY <= ybr ) ) {
// Cursor is outside of a box
// Cursor is outside of a box
return null ;
return null ;
}
}
// The shortest distance from point to an edge
// The shortest distance from point to an edge
return Math . min . apply ( null , [ x - xtl , y - ytl , xbr - x , ybr - y ] ) ;
return Math . min . apply ( null , [ rotX - xtl , rotY - ytl , xbr - rotX , ybr - rotY ] ) ;
}
}
}
}
class PolyShape extends Shape { }
class PolyShape extends Shape {
constructor ( data , clientID , color , injection ) {
super ( data , clientID , color , injection ) ;
this . rotation = 0 ; // is not supported
}
}
class PolygonShape extends PolyShape {
class PolygonShape extends PolyShape {
constructor ( data , clientID , color , injection ) {
constructor ( data , clientID , color , injection ) {
@ -1509,6 +1538,7 @@
class CuboidShape extends Shape {
class CuboidShape extends Shape {
constructor ( data , clientID , color , injection ) {
constructor ( data , clientID , color , injection ) {
super ( data , clientID , color , injection ) ;
super ( data , clientID , color , injection ) ;
this . rotation = 0 ;
this . shapeType = ObjectShape . CUBOID ;
this . shapeType = ObjectShape . CUBOID ;
this . pinned = false ;
this . pinned = false ;
checkNumberOfPoints ( this . shapeType , this . points ) ;
checkNumberOfPoints ( this . shapeType , this . points ) ;
@ -1638,10 +1668,25 @@
}
}
interpolatePosition ( leftPosition , rightPosition , offset ) {
interpolatePosition ( leftPosition , rightPosition , offset ) {
function findAngleDiff ( rightAngle , leftAngle ) {
let angleDiff = rightAngle - leftAngle ;
angleDiff = ( ( angleDiff + 180 ) % 360 ) - 180 ;
if ( Math . abs ( angleDiff ) >= 180 ) {
// if the main arc is bigger than 180, go another arc
// to find it, just substract absolute value from 360 and inverse sign
angleDiff = 360 - Math . abs ( angleDiff ) * Math . sign ( angleDiff ) * - 1 ;
}
return angleDiff ;
}
const positionOffset = leftPosition . points . map ( ( point , index ) => rightPosition . points [ index ] - point ) ;
const positionOffset = leftPosition . points . map ( ( point , index ) => rightPosition . points [ index ] - point ) ;
return {
return {
points : leftPosition . points . map ( ( point , index ) => point + positionOffset [ index ] * offset ) ,
points : leftPosition . points . map ( ( point , index ) => point + positionOffset [ index ] * offset ) ,
rotation :
( leftPosition . rotation + findAngleDiff (
rightPosition . rotation , leftPosition . rotation ,
) * offset + 360 ) % 360 ,
occluded : leftPosition . occluded ,
occluded : leftPosition . occluded ,
outside : leftPosition . outside ,
outside : leftPosition . outside ,
zOrder : leftPosition . zOrder ,
zOrder : leftPosition . zOrder ,
@ -1650,10 +1695,18 @@
}
}
class PolyTrack extends Track {
class PolyTrack extends Track {
constructor ( data , clientID , color , injection ) {
super ( data , clientID , color , injection ) ;
for ( const shape of Object . values ( this . shapes ) ) {
shape . rotation = 0 ; // is not supported
}
}
interpolatePosition ( leftPosition , rightPosition , offset ) {
interpolatePosition ( leftPosition , rightPosition , offset ) {
if ( offset === 0 ) {
if ( offset === 0 ) {
return {
return {
points : [ ... leftPosition . points ] ,
points : [ ... leftPosition . points ] ,
rotation : leftPosition . rotation ,
occluded : leftPosition . occluded ,
occluded : leftPosition . occluded ,
outside : leftPosition . outside ,
outside : leftPosition . outside ,
zOrder : leftPosition . zOrder ,
zOrder : leftPosition . zOrder ,
@ -1900,6 +1953,7 @@
return {
return {
points : toArray ( reducedPoints ) ,
points : toArray ( reducedPoints ) ,
rotation : leftPosition . rotation ,
occluded : leftPosition . occluded ,
occluded : leftPosition . occluded ,
outside : leftPosition . outside ,
outside : leftPosition . outside ,
zOrder : leftPosition . zOrder ,
zOrder : leftPosition . zOrder ,
@ -1962,6 +2016,7 @@
points : leftPosition . points . map (
points : leftPosition . points . map (
( value , index ) => value + ( rightPosition . points [ index ] - value ) * offset ,
( value , index ) => value + ( rightPosition . points [ index ] - value ) * offset ,
) ,
) ,
rotation : leftPosition . rotation ,
occluded : leftPosition . occluded ,
occluded : leftPosition . occluded ,
outside : leftPosition . outside ,
outside : leftPosition . outside ,
zOrder : leftPosition . zOrder ,
zOrder : leftPosition . zOrder ,
@ -1970,6 +2025,7 @@
return {
return {
points : [ ... leftPosition . points ] ,
points : [ ... leftPosition . points ] ,
rotation : leftPosition . rotation ,
occluded : leftPosition . occluded ,
occluded : leftPosition . occluded ,
outside : leftPosition . outside ,
outside : leftPosition . outside ,
zOrder : leftPosition . zOrder ,
zOrder : leftPosition . zOrder ,
@ -1984,6 +2040,7 @@
this . pinned = false ;
this . pinned = false ;
for ( const shape of Object . values ( this . shapes ) ) {
for ( const shape of Object . values ( this . shapes ) ) {
checkNumberOfPoints ( this . shapeType , shape . points ) ;
checkNumberOfPoints ( this . shapeType , shape . points ) ;
shape . rotation = 0 ; // is not supported
}
}
}
}
@ -1992,6 +2049,7 @@
return {
return {
points : leftPosition . points . map ( ( point , index ) => point + positionOffset [ index ] * offset ) ,
points : leftPosition . points . map ( ( point , index ) => point + positionOffset [ index ] * offset ) ,
rotation : leftPosition . rotation ,
occluded : leftPosition . occluded ,
occluded : leftPosition . occluded ,
outside : leftPosition . outside ,
outside : leftPosition . outside ,
zOrder : leftPosition . zOrder ,
zOrder : leftPosition . zOrder ,