Projekt

Obecné

Profil

Stáhnout (12.6 KB) Statistiky
| Větev: | Tag: | Revize:
1
import { Box, HStack, Pressable, View, Text, Flex, IconButton } from 'native-base';
2
import React, { useEffect, useRef, useState } from 'react';
3
import { StyleSheet, Dimensions, TouchableOpacity, GestureResponderEvent } from 'react-native';
4
import { PanGestureHandler, PinchGestureHandler, State, TapGestureHandler } from 'react-native-gesture-handler';
5
import Animated, {
6
    useAnimatedGestureHandler,
7
    useAnimatedStyle,
8
    useSharedValue,
9
} from 'react-native-reanimated';
10
import { PlanImage, Room } from '../../types/plan';
11
import Svg, { Path, SvgXml, Text as SvgText } from 'react-native-svg';
12

    
13
// @ts-ignore
14
import pointInSvgPolygon from "point-in-svg-polygon"
15
import { ExitfullscreenIcon, FullscreenIcon } from '../general/Icons';
16

    
17
const CastlePlanView = (props: { mapImage: PlanImage, roomList: Room[], selectedRoom?: Room, fullScreenMode?: boolean, height?: number, setSelectedRoom?: (room: Room) => void }) => {
18

    
19
    const DEFAULT_SCALE = 1
20
    const MIN_SCALE = 0.95
21

    
22
    const MAP_VIEW_SIZE = {
23
        height: 500,
24
        width: "100%"
25
    }
26

    
27
    const { mapImage, roomList, selectedRoom, height, setSelectedRoom } = props;
28

    
29
    const [fullScreenMode, setFullScreen] = useState(false);
30

    
31
    useEffect(() => {
32
        if (props.fullScreenMode) {
33
            setFullScreen(props.fullScreenMode)
34
        }
35
      }, [props.fullScreenMode])
36

    
37

    
38
    const panRef = useRef<React.ReactElement>();
39
    const pinchRef = useRef<React.ReactElement>();
40

    
41
    const svgRef = useRef<React.ReactElement>();
42

    
43
    const translateX = useSharedValue(0);
44
    const translateY = useSharedValue(0);
45
    const scale = useSharedValue(DEFAULT_SCALE);
46

    
47
    console.log(mapImage.svg.substring(0, 500))
48

    
49
    const onPanEvent = useAnimatedGestureHandler({
50
        // handle one finger -> drag / move
51
        onStart: (event, ctx: any) => {
52
            if (event.numberOfPointers === 1) {
53
                ctx.startX = translateX.value;
54
                ctx.startY = translateY.value;
55
            }
56
        },
57
        onActive: (event, ctx: any) => {
58
            if (event.numberOfPointers === 1) {
59
                translateX.value = ctx.startX + event.translationX;
60
                translateY.value = ctx.startY + event.translationY;
61
            }
62
        },
63
    });
64

    
65
    const onGestureEvent = useAnimatedGestureHandler({
66
        onStart: (_, ctx: any) => {
67
            ctx.startScale = scale.value;
68
        },
69
        onActive: (event: any, ctx: any) => {
70
            // handle two fingers -> zoom + -
71
            if (event.numberOfPointers === 2) {
72
                scale.value = ctx.startScale * event.scale;
73
            }
74
        },
75
        onEnd: (event) => {
76
            // handle two fingers -> zoom + -
77
            if (event.numberOfPointers === 2) {
78
                if (scale.value < MIN_SCALE) {
79
                    scale.value = MIN_SCALE;
80
                }
81
                // You may add additional logic for maximum scale if needed
82
            }
83
        },
84
    });
85

    
86
    const animatedStyle = useAnimatedStyle(() => {
87
        return {
88
            transform: [
89
                { translateX: translateX.value },
90
                { translateY: translateY.value },
91
                { scale: scale.value },
92
            ],
93
        };
94
    });
95

    
96

    
97
    function calculateStraightLineDistance(x1: number, y1: number, planX: number, planY: number, log?: boolean) {
98
        // Apply scale and translations to both points
99

    
100
        const x2 = planX
101
        const y2 = planY
102

    
103

    
104
        const x_distance = Math.abs(x1 - x2);
105
        const y_distance = Math.abs(y1 - y2);
106

    
107
        if (log) {
108
            console.log("Original X: ", x1, "Original Y: ", y1);
109
            console.log("Clicked X: ", planX, "Clicked Y: ", planY);
110
            console.log("Transformed X: ", x2, "Transformed Y: ", y2);
111
            console.log("Scale: ", scale.value, "TranslateX: ", translateX.value, "TranslateY: ", translateY.value);
112
            console.log("X Distance: ", x_distance, "Y Distance: ", y_distance);
113
        }
114
        return x_distance + y_distance;
115
    }
116

    
117
    const zoomOut = () => {
118
        translateX.value = 0;
119
        translateY.value = 0;
120
        scale.value = DEFAULT_SCALE;
121
    }
122

    
123
    const zoomToRoom = (room: Room) => {
124
        // Todo
125
        translateX.value = room.kx;
126
        translateY.value = room.ky;
127
        scale.value = DEFAULT_SCALE;
128
    }
129

    
130
    const handleFullScreenPressed = () => {
131
        console.log("neco")
132
        if (scale.value != DEFAULT_SCALE && translateX.value != 0 && translateY.value != 0) {
133
            console.log("todo zoom out")
134
            zoomOut()
135
        }
136
        else {
137
            console.log("Toggling fullscreen")
138
            setFullScreen && setFullScreen(!fullScreenMode)
139
        }
140
    }
141

    
142
    const handleSvgPress = (event: GestureResponderEvent) => {
143

    
144
        // Get the raw event coordinates
145
        const rawLocationX = event.nativeEvent.locationX;
146
        const rawLocationY = event.nativeEvent.locationY;
147

    
148
        // Log raw coordinates
149
        console.log("Raw Location X: ", rawLocationX, "Raw Location Y: ", rawLocationY);
150

    
151
        // If needed, you can get the SVG element's dimensions to normalize the coordinates
152
        const svgWidth = Dimensions.get('window').width
153
        const svgHeight = height ? height : (!fullScreenMode ? MAP_VIEW_SIZE.height : Dimensions.get('window').height - 62.5)
154

    
155
        // Normalize the coordinates based on the SVG dimensions
156
        // @ts-ignore
157
        const locationX = (rawLocationX / svgWidth) * 100;
158
        const locationY = (rawLocationY / svgHeight) * 100;
159

    
160

    
161
        // Check if the touch event is within the bounds of a room
162
        let clickedRoom: Room | undefined = undefined
163

    
164
        // TODO set accordingaly
165
        const maxNumberDistance = 100
166

    
167
        let minDistance = Number.MAX_VALUE
168

    
169
        for (let i = 0; i < roomList.length; i++) {
170
            const room: Room = roomList[i]
171
            if (room.in_plan) {
172

    
173
                // TODO
174
                console.log()
175
                console.log("Room + " + room.id)
176

    
177
                const currentDistance = calculateStraightLineDistance(room.number_x, room.number_y, locationX, locationY, true)
178

    
179
                if (currentDistance < minDistance) {
180
                    minDistance = currentDistance
181
                    clickedRoom = room
182
                }
183
            }
184
        };
185

    
186
        console.log('Room clicked with id:', clickedRoom?.id);
187
        console.log(minDistance)
188
        if (clickedRoom && minDistance < maxNumberDistance) {
189
            // TODO        
190

    
191
            // Perform any actions you need with the clicked room
192

    
193
            // TODO fix -> point recognition
194
            setSelectedRoom && setSelectedRoom(clickedRoom)
195
        } else {
196
            console.log("no room found")
197
        }
198
    };
199

    
200
    console.log(Dimensions.get('window').height)
201

    
202
    return (
203
        // container
204
        <View
205
            width={!fullScreenMode ? MAP_VIEW_SIZE.width : Dimensions.get('window').width - 5
206
            }
207
            height={height ? height : (!fullScreenMode ? MAP_VIEW_SIZE.height : Dimensions.get('window').height - 62.5)}
208
            // @ts-ignore
209
            style={
210
                fullScreenMode ? {
211

    
212
                    position: "absolute",
213
                    top: -230,
214
                    left: 0,
215
                    zIndex: 1000,
216
                    backgroundColor:"white",
217

    
218
                    height: Dimensions.get('window').height - 120,
219
                    width: Dimensions.get('window').width - 20,
220

    
221

    
222
                }
223
                    :
224
                    {
225
                        overflow: "hidden",
226
                    }
227
            }
228
            borderColor={"light.300"}
229
            borderRadius={10}
230
            borderWidth={fullScreenMode ? 0 : 1}
231
        >
232
            {/* control panel */}
233
            <Flex direction="row" alignItems="center" justify="flex-end" style={{ height: 50, width: 100, top: 5, right: 10, position: "absolute", zIndex: 5 }}>
234
                <Pressable padding={1.5} backgroundColor={"#654B07"} borderRadius={5} marginRight={selectedRoom ? 1 : -2} onPress={handleFullScreenPressed}>
235
                    <FullscreenIcon color="white" />
236
                </Pressable>
237
                {
238
                    selectedRoom &&
239
                    <Pressable padding={1.5} backgroundColor={"#654B07"} borderRadius={5} marginRight={-2}  onPress={() => { zoomToRoom(selectedRoom) }}>
240
                        <ExitfullscreenIcon color="white" />
241
                    </Pressable>
242
                }
243

    
244
            </Flex>
245

    
246
            <PinchGestureHandler
247
                ref={pinchRef}
248
                // @ts-ignore
249
                onGestureEvent={onGestureEvent}
250
                simultaneousHandlers={[svgRef, panRef]}>
251
                <Animated.View style={{ flex: 1 }}>
252
                    <PanGestureHandler
253
                        ref={panRef}
254
                        simultaneousHandlers={[svgRef, pinchRef]}
255
                        onGestureEvent={onPanEvent}
256
                        onHandlerStateChange={(nativeEvent: any) => {
257
                            if (nativeEvent.state === State.END) {
258
                                console.log(nativeEvent)
259
                            }
260
                        }}
261
                    >
262
                        <Animated.View style={[{
263
                            flex: 1,
264
                        }, animatedStyle]}>
265
                            <Svg
266
                                id="svgMap"
267
                                ref={(ref: any) => (svgRef.current = ref)}
268
                                onPress={handleSvgPress}
269
                            >
270
                                {mapImage && mapImage.viewBox &&
271
                                    // background image                    
272
                                    <SvgXml
273
                                        xml={mapImage.svg}
274
                                        width={"100%"}
275
                                        height={"100%"}
276
                                    />
277
                                }
278

    
279
                                {mapImage && roomList && roomList.length > 0 &&
280

    
281
                                    roomList.map((room, index) => {
282
                                        if (!room.in_plan) {
283
                                            return
284
                                        }
285
                                        return (
286
                                            <Path
287
                                                id={"roomList_" + index.toString()}
288
                                                key={'room_' + room.id}
289
                                                d={room.svg_path}  // The path data defining the shape of the room
290
                                                fill={selectedRoom && room.id == selectedRoom.id ? "#E6F7FF" : "white"}        // Fill the room shape with no color
291
                                                stroke="#66B2FF"       // Outline color
292
                                                strokeWidth={0.3}     // Outline width
293
                                                fillOpacity={selectedRoom && room.id == selectedRoom.id ? 1 : 0.2}
294
                                            />
295
                                        )
296
                                    })}
297

    
298
                                {mapImage && roomList && roomList.length > 0 &&
299

    
300
                                    roomList.map((room) => {
301
                                        if (!room.in_plan) {
302
                                            return
303
                                        }
304
                                        return (
305
                                            < SvgText
306
                                                x={room.number_x}     // Adjust the x-coordinate based on your room data
307
                                                y={room.number_y}     // Adjust the y-coordinate based on your room data
308
                                                fill="red"      // Text color
309
                                                fontSize={8}       // Font size
310
                                                textAnchor="middle"  // Center the text horizontally
311
                                                fontStyle='italic'
312
                                                vectorEffect='non-scaling-stroke' // Helps prevent stroke from scaling
313
                                            >
314
                                                {room.id}
315
                                            </SvgText>
316
                                        )
317
                                    })}
318
                            </Svg>
319
                        </Animated.View>
320
                    </PanGestureHandler>
321
                </Animated.View>
322
            </PinchGestureHandler >
323
        </View >
324
    );
325
};
326

    
327
export default CastlePlanView;
328

    
    (1-1/1)