Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 4 additions & 14 deletions src/Annotator/reducers/general-reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,7 @@ export default (state: MainLayoutState, action: Action) => {
}

case "TOGGLE_VISIBILITY": {
console.log("TOGGLE_VISIBILITY", action)
let newState = { ...state }
let newImage = getIn(newState, ["images", currentImageIndex])
let newRegions = getIn(newState, ["images", currentImageIndex, "regions"])
Expand All @@ -443,8 +444,8 @@ export default (state: MainLayoutState, action: Action) => {
newRegions = newRegions.map((region) => {
const isCategoryMatch = region.category === action.category
const isBreakoutMatch =
!selectedBreakoutToggle ||
region.breakout.id === selectedBreakoutToggle
!selectedBreakoutToggle || (region.breakout || {}).id === selectedBreakoutToggle;

const isVisible =
isCategoryMatch && isBreakoutMatch
? !newExcludedCategories.includes(action.category)
Expand All @@ -453,17 +454,6 @@ export default (state: MainLayoutState, action: Action) => {
return { ...region, visible: isVisible }
})

// newRegions = newRegions.map((region) => {
// if (region.category === action.category) {
// // Toggle visibility if the region's category matches the action's category
// return {
// ...region,
// visible: visibility,
// }
// } else {
// return region
// }
// })
newState = setIn(newState, ["excludedCategories"], newExcludedCategories)
newImage = setIn(newImage, ["regions"], newRegions)
newState = setIn(newState, ["images", currentImageIndex], newImage)
Expand Down Expand Up @@ -1860,7 +1850,7 @@ export default (state: MainLayoutState, action: Action) => {
length: 1,
highlighted: true,
editingLabels: false,
color: "#C4A484",
color: "#4f46e5",
cls: "1", // Make sure this is set
id: getRandomId(),
visible: true,
Expand Down
22 changes: 13 additions & 9 deletions src/BreakoutSidebarBox/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -230,18 +230,22 @@ export const BreakoutSidebarBox = ({
selectedBreakoutIdAutoAdd,
selectedBreakoutToggle,
}) => {

const breakoutList = useMemo(() => {
const breakoutRegions = [
...new Set(
breakouts
.filter((obj) => obj.is_breakout === true)
.map((obj) => JSON.stringify(obj))
),
].map((str) => JSON.parse(str))
if (breakoutRegions.length === 0) return null
return breakoutRegions
const seen = new Set()
const breakoutRegions = breakouts
.filter((obj) => obj.is_breakout === true)
.filter((obj) => {
const key = `${obj.name}-${obj.location}` // Customize with relevant keys
if (seen.has(key)) return false
seen.add(key)
return true
})

return breakoutRegions.length === 0 ? null : breakoutRegions
}, [breakouts])


const classes = useStyles()
return (
<SidebarBoxContainer
Expand Down
180 changes: 161 additions & 19 deletions src/RegionShapes/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// @flow

import React, { memo } from "react"
import colorAlpha from "color-alpha"
import React, { memo, useMemo } from "react"

function clamp(num, min, max) {
return num <= min ? min : num >= max ? max : num
Expand All @@ -18,19 +18,44 @@ const RegionComponents = {
/>
</g>
)),
line: memo(({ region, iw, ih }) => (
<g transform={`translate(${region.x1 * iw} ${region.y1 * ih})`}>
<line
strokeWidth={2}
x1={0}
y1={0}
x2={(region.x2 - region.x1) * iw}
y2={(region.y2 - region.y1) * ih}
stroke={colorAlpha(region.color, 0.75)}
fill={colorAlpha(region.color, 0.25)}
/>
</g>
)),
line: memo(({ region, iw, ih }) => {
const x1 = region.x1 * iw
const y1 = region.y1 * ih
const x2 = region.x2 * iw
const y2 = region.y2 * ih

const dx = x2 - x1
const dy = y2 - y1
const angle = Math.atan2(dy, dx) * (180 / Math.PI)
const length = Math.sqrt(dx * dx + dy * dy)

return (
<g transform={`translate(${x1}, ${y1}) rotate(${angle})`}>
<line
strokeWidth={2}
x1={0}
y1={0}
x2={length}
y2={0}
stroke={colorAlpha(region.color, 0.75)}
fill={colorAlpha(region.color, 0.25)}
/>

{/* Length label */}
<text
x={length/2}
y={-10}
textAnchor="middle"
fill={region.color || "#4f46e5"}
fontSize={12}
fontFamily="monospace"
fontWeight="bold"
>
{region.length_ft ? `${region.length_ft.toFixed(2)} ft` : ''}
</text>
</g>
)
}),
scale: memo(({ region, iw, ih }) => (
<g transform={`translate(${region.x1 * iw} ${region.y1 * ih})`}>
<line
Expand All @@ -47,12 +72,12 @@ const RegionComponents = {
box: memo(({ region, iw, ih }) => (
<g transform={`translate(${region.x * iw} ${region.y * ih})`}>
<rect
strokeWidth={region.isOCR?1.5:2}
strokeWidth={region.isOCR ? 1.5 : 2}
x={0}
y={0}
width={Math.max(region.w * iw, 0)}
height={Math.max(region.h * ih, 0)}
stroke={colorAlpha(region.isOCR?"#080808":region.color, region.isOCR?0.75:0.75)}
stroke={colorAlpha(region.isOCR ? "#080808" : region.color, region.isOCR ? 0.75 : 0.75)}
fill={colorAlpha(region.color, 0.5)}
/>
</g>
Expand Down Expand Up @@ -157,9 +182,8 @@ const RegionComponents = {
{points.map(({ x, y, angle }, i) => (
<g
key={i}
transform={`translate(${x * iw} ${y * ih}) rotate(${
(-(angle || 0) * 180) / Math.PI
})`}
transform={`translate(${x * iw} ${y * ih}) rotate(${(-(angle || 0) * 180) / Math.PI
})`}
>
<g>
<rect
Expand Down Expand Up @@ -210,6 +234,109 @@ export const WrappedRegionList = memo(
(n, p) => n.regions === p.regions && n.iw === p.iw && n.ih === p.ih
)

const ScaleLine = memo(({ region, iw, ih }) => {
const scaleColor = region.color || "#4f46e5"

// Memoize core calculations
const { x1, y1, pixelLength, angle } = useMemo(() => {
const x1 = region.x1 * iw
const y1 = region.y1 * ih
const x2 = region.x2 * iw
const y2 = region.y2 * ih

const dx = x2 - x1
const dy = y2 - y1
return {
x1,
y1,
pixelLength: Math.sqrt(dx * dx + dy * dy),
angle: Math.atan2(dy, dx) * (180 / Math.PI)
}
}, [region.x1, region.x2, region.y1, region.y2, iw, ih])

// Pre-calculate tick positions - only 5 fixed positions
const tickMarks = useMemo(() => {
const lengthInFeet = region.length_ft || parseFloat(region.cls)
return [0, 0.25, 0.5, 0.75, 1].map(percent => ({
position: percent * pixelLength,
label: `${(percent * lengthInFeet).toFixed(1)}`
}))
}, [pixelLength, region.length_ft, region.cls])

// Get the display length (prefer length_ft if available)
const displayLength = region.length_ft ?
region.length_ft.toFixed(2) :
parseFloat(region.cls).toFixed(2)

// Render optimized SVG
return (
<g transform={`translate(${x1}, ${y1}) rotate(${angle})`}>
{/* Main line with end caps in single path */}
<path
d={`
M0,0
L${pixelLength},0
M0,-7 L0,7
M${pixelLength},-7 L${pixelLength},7
`}
stroke={scaleColor}
strokeWidth={2}
fill="none"
/>

{/* Combine tick marks into single path */}
<path
d={tickMarks
.map(tick => `M${tick.position},-7 L${tick.position},7`)
.join(" ")}
stroke={scaleColor}
strokeWidth={1}
/>

{/* Text elements */}
{tickMarks.map(({ position, label }, i) => (
<text
key={i}
x={position}
y={12}
textAnchor="middle"
fill={scaleColor}
fontSize={10}
fontFamily="monospace"
>
{label}
</text>
))}

{/* Main label */}
<text
x={pixelLength / 2}
y={-15}
textAnchor="middle"
fill={scaleColor}
fontSize={12}
fontFamily="monospace"
fontWeight="bold"
>
{displayLength} ft
</text>
</g>
)
}, (prev, next) => {
// Custom memoization check
return (
prev.region.x1 === next.region.x1 &&
prev.region.y1 === next.region.y1 &&
prev.region.x2 === next.region.x2 &&
prev.region.y2 === next.region.y2 &&
prev.region.cls === next.region.cls &&
prev.region.length_ft === next.region.length_ft &&
prev.region.color === next.region.color &&
prev.iw === next.iw &&
prev.ih === next.ih
)
})

export const RegionShapes = ({
mat,
imagePosition,
Expand Down Expand Up @@ -242,6 +369,21 @@ export const RegionShapes = ({
keypointDefinitions={keypointDefinitions}
fullSegmentationMode={fullSegmentationMode}
/>
{regions.map((region, i) => {
switch (region.type) {
case "scale":
return (
<ScaleLine
key={i}
region={region}
iw={iw}
ih={ih}
/>
)
default:
return null
}
})}
</svg>
)
}
Expand Down
Loading