## Segment Painting Feature

> **Project Overview**: This module is applicable to Wi-Fi + BLE protocol RGBIC string light devices, supporting segment selection and independent control of LED beads for precise lighting effect customization.

### 🖼️ Preview:
<Image src='/images/extended/lampSting/string-paint-en.png' style={{ width: '120px', height: '224px' }} />

The segment painting feature allows users to select specific LED bead segments through a visual interface and independently control the color of selected segments. This feature supports both color light and white light modes, enabling full segment control, single segment control, and segment painting operations. It is an important tool for users to personalize lighting effects.

### Feature List

1. **LED Bead Visualization**: Displays all LED beads in grid format, supports click selection
2. **Select All/Deselect All**: One-click selection or deselection of all LED beads
3. **Segment Selection**: Supports clicking single or multiple LED beads for selection
4. **Segment Painting Control**: Independent color control for selected LED bead segments
5. **Full Segment Painting Control**: Unified color control for all LED beads
6. **Real-time Preview**: Real-time update of light strip color display during slider adjustment
7. **Data Synchronization**: LED bead color data syncs to cloud storage and device

### Module Implementation Details

#### Implementation Overview

The segment painting feature uses Redux to manage selection state and sends data through the `paint_colour_1` DP point. Core implementation approach:

- **State Management**: Uses Redux's `commonSlice` to manage selected LED bead list (`checkedIdList`) and select all state (`stripCheckAll`)
- **Data Flow**: User selection → Update Redux state → Color adjustment → Build DP data → Send to device
- **Painting Modes**: Supports paint bucket mode (full segment) and single point painting mode (segment)
- **Data Persistence**: LED bead color data saved to cloud storage, supports cross-session recovery

#### Core Components

- `src/components/light-strip/index.tsx`: Light strip visualization component, responsible for LED bead display and selection interaction
- `src/components/light/index.tsx`: Main light control component, integrates segment painting and color control logic
- `src/redux/modules/commonSlice.ts`: Redux state management, stores selected LED bead list and select all state
- `src/hooks/useDrawToolDataList.ts`: Painting tool data management Hook, handles LED bead data updates
- `src/devices/protocols/paintColour1.ts`: `paint_colour_1` DP point encoder/decoder
- `src/hooks/useStorageData.ts`: Cloud storage data management Hook

**Component Responsibilities**:

- **LightStrip Component**: Responsible for LED bead visualization rendering and click interaction, maintains UI feedback for selection state
- **Light Component**: Integrates color control and segment painting logic, handles color changes and DP sending
- **commonSlice**: Centrally manages selection state, provides state update methods
- **useDrawToolDataList Hook**: Encapsulates LED bead data update logic, handles both full segment and segment modes

#### Data Flow

```mermaid
sequenceDiagram
    participant User as User
    participant LightStrip as Light Strip Component
    participant Redux as Redux Store
    participant Light as Light Component
    participant Hook as Data Hook
    participant Protocol as Protocol Parser
    participant Device as Device
    participant Cloud as Cloud Storage

    User->>LightStrip: Click to select LED bead
    LightStrip->>Redux: Update selected list
    Redux-->>LightStrip: Update UI state
    User->>Light: Adjust color
    Light->>Hook: Call update function
    Hook->>Hook: Build LED bead data array
    Hook->>Protocol: Encode DP data
    Protocol->>Device: Send paint_colour_1
    Device-->>User: Display effect
    Hook->>Cloud: Save LED bead color data
```

#### Mermaid Flowcharts

**Main Feature Flow**:

```mermaid
flowchart TD
    A[Enter light control page] --> B[Load LED bead data]
    B --> C[Display light strip visualization]
    C --> D{User action}
    D -->|Click LED bead| E[Update selection state]
    D -->|Click select all| F[Toggle select all state]
    D -->|Adjust color| G[Get selected LED beads]
    E --> H[Update Redux state]
    F --> H
    G --> I{Is all selected}
    I -->|Yes| J[Full segment painting mode]
    I -->|No| K[Segment painting mode]
    J --> L[Build full segment DP data]
    K --> M[Build segment DP data]
    L --> N[Encode DP data]
    M --> N
    N --> O[Send to device]
    O --> P[Update cloud storage]
    P --> Q[Real-time preview effect]
```

**Data Flow Diagram**:

```mermaid
sequenceDiagram
    participant User as User Action
    participant UI as UI Component
    participant State as Redux State
    participant Logic as Business Logic
    participant DP as DP Protocol
    participant Device as Device
    participant Storage as Cloud Storage

    User->>UI: Select LED bead
    UI->>State: dispatch(updateCheckedIdList)
    State-->>UI: Update selection state
    User->>UI: Adjust color
    UI->>Logic: Get selected LED bead list
    Logic->>Logic: Build color data array
    Logic->>DP: Encode paint_colour_1 data
    DP->>Device: Send DP command
    Device-->>User: Display lighting effect
    Logic->>Storage: Save LED bead color data
```

**Component Interaction Diagram**:

```mermaid
graph TB
    A[Light Component] -->|Use| B[LightStrip Component]
    A -->|Use| C[ColorLight Component]
    A -->|Use| D[WhiteLight Component]
    B -->|dispatch| E[Redux Store]
    C -->|Call| F[useDrawToolDataList]
    D -->|Call| F
    F -->|Use| G[useStorageData]
    F -->|Encode| H[paintColour1 Protocol]
    H -->|Send| I[Device]
    G -->|Save| J[Cloud Storage]
```

**State Transition Diagram**:

```mermaid
stateDiagram-v2
    [*] --> InitialState: Page load
    InitialState --> SelectLED: User click
    SelectLED --> AdjustColor: Selection complete
    AdjustColor --> SliderAdjust: Start sliding
    SliderAdjust --> RealtimePreview: Color change
    RealtimePreview --> ReleaseConfirm: Stop sliding
    ReleaseConfirm --> SendDP: Build data
    SendDP --> UpdateStorage: Success callback
    UpdateStorage --> SelectLED: Continue operation
    UpdateStorage --> [*]: Complete
```

#### Key Code Snippets

**LED Bead Selection Handling**:

```tsx
// File: src/components/light-strip/index.tsx
const onClick = useCallback(
  (checked: boolean, index: number) => {
    if (!checked) {
      checkedSet.delete(index);
    } else {
      checkedSet.add(index);
    }
    const newCheckedSet = new Set(checkedSet);
    dispatch(actions.common.updateCheckedIdList(Array.from(newCheckedSet)));
  },
  [checkedSet]
);
```

**Select All/Deselect All**:

```tsx
// File: src/components/light-strip/index.tsx
const handleCheckAll = () => {
  // Select all
  if (!checkAllBool) {
    const newSet = new Set<number>();
    new Array(lightNum).fill(0).map((_, i) => newSet.add(i));
    dispatch(actions.common.updateCheckedIdList(Array.from(newSet)));
  } else {
    const newArr = [];
    dispatch(actions.common.updateCheckedIdList(newArr));
  }
};
```

**Segment Painting Data Update**:

```tsx
// File: src/hooks/useDrawToolDataList.ts
const updateStripDataListAll = ({ isMoving, color, isSectionAll }: UpdateStripDataListAllOps) => {
  if (!color) {
    return;
  }
  if (!isSectionAll && checkedSet.size === 0) {
    ty.showToast({
      title: Strings.getLang('checkLightTip'),
      icon: 'none',
    });
  }
  if (isMoving) {
    // Real-time update light strip color during sliding
    updateStripDataList(color);
    return;
  }
  // Send DP and update color for full segment control
  if (isSectionAll) {
    const res: DiySceneData = {
      ...(diy_scene || {}),
      ...(color || {}),
      effect: 1,
      daubType: 'all',
      // Update all segments to this color
      segments: new Array(ledNumber).fill(0).map(i => ({
        index: i,
        ...color,
      })),
    };

    console.log('[diy_scene] set:', res);
    cutDrawDiySceneSet(res, {
      success() {
        const newLightStripDataList = updateStripDataList(color, isSectionAll);
        preUpdateCloudDataRef.current = newLightStripDataList;
      },
      fail(e) {
        console.error('fail', e);
      },
    });
    return;
  }
  // Send DP data on release
  // If external color is provided and there are selected LED beads, update selected LED bead colors
  if (checkedSet.size > 0) {
    const indexs = Array.from(checkedSet);
    const curSegments = getArray(diy_scene?.segments);
    const res: DiySceneData = {
      ...(diy_scene || {}),
      daubType: 'single',
      segments: new Array(ledNumber)
        .fill(0)
        .map((_, index) => {
          const item = curSegments[index] || {
            isWhite: false,
            hue: 0,
            saturation: 1000,
            value: 1000,
            brightness: 0,
            temperature: 0,
          };
          // Edit selected colors
          if (indexs.includes(index)) {
            item.brightness = color.brightness;
            item.hue = color.hue;
            item.isWhite = color.isWhite;
            item.saturation = color.saturation;
            item.value = color.value;
            item.temperature = color.temperature;
          }
          return item;
        })
        .slice(0, ledNumber),
    };
    console.log('[diy_scene] set:', res);
    cutDrawDiySceneSet(res, {
      success() {
        const newLightStripDataList = updateStripDataList(color, false);
        preUpdateCloudDataRef.current = newLightStripDataList;
      },
      fail(params) {
        console.error('__checkedSet fail', params);
      },
    });
  }
};
```

**Color Light Segment Control**:

```tsx
// File: src/components/light/index.tsx
const updateColourStripDataListAll = (_hsv = currentHsv, isSectionAll = false, extParams?) => {
  const { checkedSet: _checkedSet } = extParams || {};
  const __checkedSet = _checkedSet || checkedSet || new Set();
  const _currentHsv = _hsv || currentHsv;
  preHsvRef.current = _currentHsv;
  if (!_currentHsv) {
    return;
  }
  if (isMovingRef.current) {
    // Real-time update light strip color during sliding
    updateStripDataList(currentLightStripDataList, __checkedSet, {
      ..._currentHsv,
      mode: DimmerMode.colour,
    });
    return;
  }
  // Send DP and update color for full segment control
  if (isSectionAll) {
    const hsvFull = {
      hue: _currentHsv.h,
      saturation: _currentHsv.s,
      value: _currentHsv.v,
    };
    const indexs = new Set() as any;
    const dimmerMode = DimmerMode.colour;
    const smearMode = SmearMode.all;
    const res = {
      ...paintColorData,
      ...hsvFull,
      indexs,
      ledNumber,
      dimmerMode,
      smearMode,
    };
    paintColorDataRef.current = res;
    timeoutPutDpStatus();
    sActions.paint_colour_1.set(res, {
      success() {
        const newLightStripDataList = updateStripDataList(
          currentLightStripDataList,
          __checkedSet,
          {
            ..._currentHsv,
            mode: DimmerMode.colour,
          },
          isSectionAll
        );
        const _isEqualColor = isEqual(preUpdateCloudDataRef.current, newLightStripDataList);
        console.warn(newLightStripDataList, 'handleUpdate2Cloud1');
        !_isEqualColor && handleUpdate2Cloud(newLightStripDataList);
        preUpdateCloudDataRef.current = newLightStripDataList;
      },
      fail(e) {
        console.error('fail', e);
      },
    });
    return;
  }
  // Send DP data on release
  // If external color is provided and there are selected LED beads, update selected LED bead colors
  if (__checkedSet.size > 0) {
    const hsvFull = {
      hue: _currentHsv.h,
      saturation: _currentHsv.s,
      value: _currentHsv.v,
    };
    const indexs = __checkedSet;
    const dimmerMode = DimmerMode.colour;
    const smearMode = SmearMode.single;
    const res = {
      ...paintColorData,
      ...hsvFull,
      indexs,
      ledNumber,
      dimmerMode,
      smearMode,
    };
    paintColorDataRef.current = res;
    timeoutPutDpStatus();
    sActions.paint_colour_1.set(res, {
      success() {
        const newLightStripDataList = updateStripDataList(currentLightStripDataList, __checkedSet, {
          ..._currentHsv,
          mode: DimmerMode.colour,
        });
        const _isEqualColor = isEqual(preUpdateCloudDataRef.current, newLightStripDataList);
        !_isEqualColor && newLightStripDataList && handleUpdate2Cloud(newLightStripDataList);
        preUpdateCloudDataRef.current = newLightStripDataList;
      },
      fail(params) {
        console.error('__checkedSet fail', params);
      },
    });
  }
};
```

#### Data Structures

**Selection State Data Structure** (Redux):

```typescript
type CommonState = {
  stripCheckAll: boolean; // Whether all LED beads are selected
  checkedIdList: number[]; // List of selected LED bead IDs
};
```

**paint_colour_1 DP Data Structure**:

```typescript
interface SmearDataType {
  version: number; // Version number
  dimmerMode: DimmerMode; // Mode (0: White light, 1: Color light, 2: Color card, 3: Combination)
  effect?: number; // Painting effect (0: None, 1: Gradient)
  ledNumber?: number; // Light strip UI segment count
  smearMode?: SmearMode; // Painting action (0: Paint bucket, 1: Paint, 2: Eraser)
  hue?: number; // Color light hue
  saturation?: number; // Color light saturation
  value?: number; // Color light brightness
  brightness?: number; // White light brightness
  temperature?: number; // White light color temperature
  indexs?: Set<number>; // Set of selected LED bead indices
}
```

**LED Bead Color Data Array**:

```typescript
// LED bead color data: RGB color string array
// For example: ['rgb(255,0,0)', 'rgb(0,255,0)', ...]
type LightStripDataList = string[];
```

**Painting Mode Enum**:

```typescript
enum SmearMode {
  all = 0, // Paint bucket mode (full segment)
  single = 1, // Single point painting mode (segment)
  clear = 2, // Eraser mode
}
```

#### API Calls

**DP Send Interface**:

- **DP Point**: `paint_colour_1`
- **Call Method**: `structuredActions.paint_colour_1.set(data, options)`
- **Data Format**: Hexadecimal string encoded by `SmearFormater`
- **Call Timing**:
  - Full segment control: User adjusts color when select all state is active
  - Segment control: User adjusts color when there are selected LED beads
  - Slider adjustment: Only send on release, only update UI during sliding

**Cloud Storage Interface**:

- **Storage Key**: `lightStripDataKey` (constant definition)
- **Call Method**: `useSetStorageData(lightStripDataKey)`
- **Data Format**: RGB color string array
- **Call Timing**: After DP send success, update cloud storage data

### Notes

#### Implementation Challenges

1. **Selection State Synchronization**:

   - **Challenge**: Need to synchronize selection state across multiple components, ensure UI and data consistency
   - **Solution**: Use Redux for centralized state management, implement state sharing and updates through `useSelector` and `dispatch`

2. **Real-time Preview Performance**:

   - **Challenge**: Need to update large number of LED bead colors in real-time during slider adjustment, may cause performance issues
   - **Solution**: Use throttle to limit update frequency, only update UI during sliding, send DP on release

3. **Data Alignment**:

   - **Challenge**: LED bead count may change, need to ensure data array length matches device LED bead count
   - **Solution**: Check data length in `useCutDrawDiySceneSet`, pad with default colors when insufficient, truncate when exceeded

4. **Select All State Determination**:
   - **Challenge**: Need to accurately determine whether all LED beads are selected
   - **Solution**: Compare selected set size with total LED bead count, use `useEffect` to monitor changes and update select all state

#### Common Issues

1. **Issue**: Color adjustment has no effect after selecting LED beads

   - **Cause**: No LED beads selected and select all mode not enabled
   - **Solution**: Check `checkedSet.size` and `stripCheckAll` state, prompt user to select LED beads first

2. **Issue**: Color update delay during slider adjustment

   - **Cause**: Throttle time set too long or DP sending too frequent
   - **Solution**: Adjust throttle time (recommended 300ms), only update UI during sliding, send DP on release

3. **Issue**: Data mismatch after device LED bead count changes

   - **Cause**: Cloud storage data length doesn't match device LED bead count
   - **Solution**: Automatically align data during loading, pad or truncate data array

4. **Issue**: Select all state display incorrect
   - **Cause**: Select all state not synchronized in time after selection state update
   - **Solution**: Use `useEffect` to monitor selected list changes, automatically update select all state

#### Performance Optimization

1. **Throttle Processing**:

   - Color adjustment uses throttle to avoid frequent updates
   - Use throttle to update UI during sliding, reduce render count

2. **Data Caching**:

   - Use `useRef` to cache previous LED bead data, avoid repeated updates
   - Use `useMemo` to cache calculation results, reduce repeated calculations

3. **Conditional Rendering**:

   - Dynamically adjust rendering hierarchy based on LED bead count, reduce unnecessary DOM nodes
   - Use `useMemo` to cache LED bead element list

4. **State Update Optimization**:
   - Batch update states, reduce Redux dispatch count
   - Use `useCallback` to cache event handler functions

#### Edge Cases

1. **LED Bead Count is 0**:

   - Handling: Component returns empty view, no rendering
   - Code: `if (!lightNum) return <View />;`

2. **No LED Beads Selected**:

   - Handling: Prompt user to select LED beads first, don't execute color update
   - Code: Check `checkedSet.size === 0 && !isSectionAll`

3. **Device LED Bead Count Changes**:

   - Handling: Automatically align data length, pad or truncate
   - Code: Handle in `useCutDrawDiySceneSet`

4. **DP Send Failed**:

   - Handling: Show error prompt, don't update cloud storage data
   - Code: Handle error in `fail` callback

5. **Multiple Operations Simultaneously**:
   - Handling: Use `isPutDpIngReg` flag to prevent repeated sending
   - Code: Set timeout recovery mechanism (1 second)


