Bin
2025-12-17 1d710f844b65d9bfdf986a71a3b924cd70598a41
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import chroma from "chroma-js";
import { observe } from "mobx";
import { useContext, useEffect, useMemo, useState } from "react";
import { ImageViewContext } from "../components/ImageView/ImageViewContext";
import Constants, { defaultStyle } from "../core/Constants";
import { isDefined } from "../utils/utilities";
 
const defaultStyles = {
  defaultOpacity: defaultStyle.opacity,
  defaultFillColor: defaultStyle.fillcolor,
  defaultStrokeColor: defaultStyle.strokecolor,
  defaultStrokeColorHighlighted: Constants.HIGHLIGHTED_STROKE_COLOR,
  defaultStrokeWidth: defaultStyle.strokewidth,
  defaultStrokeWidthHighlighted: Constants.HIGHLIGHTED_STROKE_WIDTH,
  defaultSuggestionWidth: Constants.SUGGESTION_STROKE_WIDTH,
};
 
type StyleOptions = typeof defaultStyles & {
  region: any;
  highlighted?: boolean;
  shouldFill?: boolean;
  suggestion?: boolean;
  includeFill?: boolean;
  useStrokeAsFill?: boolean;
  sameStrokeWidthForSelected?: boolean;
};
 
export const getRegionStyles = ({
  region,
  highlighted = false,
  shouldFill = false,
  useStrokeAsFill = false,
  sameStrokeWidthForSelected = false,
  suggestion = false,
  defaultOpacity = defaultStyle.opacity,
  defaultFillColor = defaultStyle.fillcolor,
  defaultStrokeColor = defaultStyle.strokecolor,
  defaultStrokeColorHighlighted = Constants.HIGHLIGHTED_STROKE_COLOR,
  defaultStrokeWidth = defaultStyle.strokewidth,
  defaultStrokeWidthHighlighted = Constants.HIGHLIGHTED_STROKE_WIDTH,
  defaultSuggestionWidth = Constants.SUGGESTION_STROKE_WIDTH,
}: StyleOptions) => {
  const style = region.style || region.tag;
 
  const selected = region.inSelection || highlighted;
 
  const fillopacity = style?.fillopacity;
  const opacity = isDefined(fillopacity) ? fillopacity : style?.opacity;
 
  const fillColor = shouldFill
    ? chroma((useStrokeAsFill ? style?.strokecolor : style?.fillcolor) ?? defaultFillColor)
        .darken(0.3)
        .alpha(+(opacity ?? defaultOpacity ?? 0.5))
        .css()
    : null;
 
  const strokeColor = selected ? defaultStrokeColorHighlighted : chroma(style?.strokecolor ?? defaultStrokeColor).css();
 
  const strokeWidth = (() => {
    if (suggestion) {
      return defaultSuggestionWidth;
    }
    if (selected && !sameStrokeWidthForSelected) {
      return defaultStrokeWidthHighlighted;
    }
    return +(style?.strokewidth ?? defaultStrokeWidth);
  })();
 
  return {
    strokeColor,
    fillColor,
    strokeWidth,
  };
};
 
export const useRegionStyles = (region: any, options: Partial<StyleOptions> = {}) => {
  const { suggestion } = useContext(ImageViewContext) ?? {};
  const [highlighted, setHighlighted] = useState(region.highlighted);
  const [shouldFill, setShouldFill] = useState(region.fill ?? (options.useStrokeAsFill || options.includeFill));
 
  const styles = useMemo(() => {
    return getRegionStyles({
      ...defaultStyles,
      ...(options ?? {}),
      highlighted,
      shouldFill,
      region,
      suggestion,
    });
  }, [region, suggestion, options, highlighted, shouldFill]);
 
  useEffect(() => {
    const disposeObserver = ["highlighted", "fill"].map((prop) => {
      try {
        return observe(
          region,
          prop,
          ({ newValue }) => {
            switch (prop) {
              case "highlighted":
                return setHighlighted(newValue);
              case "fill":
                return setShouldFill(newValue);
            }
          },
          true,
        );
      } catch (e) {
        return () => {};
      }
    });
 
    return () => {
      disposeObserver.forEach((dispose) => dispose());
    };
  }, [region]);
 
  return styles;
};