Projekt

Obecné

Profil

Stáhnout (13.9 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, TouchableWithoutFeedback } from 'react-native-gesture-handler';
5

    
6
import { findNodeHandle, UIManager } from 'react-native';
7

    
8
import Animated, {
9
    useAnimatedGestureHandler,
10
    useAnimatedStyle,
11
    useSharedValue,
12
} from 'react-native-reanimated';
13
import { PlanImage, Room } from '../../types/plan';
14
import Svg, { Path, SvgXml, Text as SvgText, G, Circle } from 'react-native-svg';
15

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

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

    
22
    const DEFAULT_SCALE = 1
23
    const MIN_SCALE = 0.95
24

    
25
    const { mapImage, roomList, selectedRoom, height, setSelectedRoom } = props;
26

    
27

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

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

    
36

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

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

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

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

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

    
80
                console.log("scale")
81
                console.log(scale.value)
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) {
98
        // Apply scale and translations to both points
99

    
100
        const x2 = planX
101
        const y2 = planY
102

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

    
106
        return x_distance + y_distance;
107
    }
108

    
109
    const zoomOut = () => {
110
        translateX.value = 0;
111
        translateY.value = 0;
112
        scale.value = DEFAULT_SCALE;
113
    }
114

    
115
    const zoomToRoom = (room: Room) => {
116
        // Todo
117

    
118
        console.log(room)
119

    
120
        scale.value = 3;
121

    
122
        translateX.value = room.number_x + 100
123
        translateY.value = room.number_y + 100
124

    
125
    }
126

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

    
138
    const handleSvgPress = (event: GestureResponderEvent) => {
139
        let locationX = event.nativeEvent.locationX
140
        let locationY = event.nativeEvent.locationY
141

    
142
        // console.log("view size:")
143
        // console.log("x " + viewSize.width + " y " + viewSize.height) 
144

    
145
        const viewRatio =  viewSize.width / viewSize.height
146
        // console.log("ratio: " + viewSize.width / viewSize.height)       
147
        // console.log("svg size:")
148
        // console.log("x " + mapImage.viewBox.width + " y " + mapImage.viewBox.height)
149
        const svgRatio = mapImage.viewBox.width / mapImage.viewBox.height
150
        // console.log("ratio: " + mapImage.viewBox.width / mapImage.viewBox.height)    
151

    
152
    
153
        let mapX =  (locationX / viewSize.width) * mapImage.viewBox.width
154
        let mapY =  (locationY / viewSize.height) * mapImage.viewBox.height
155

    
156
        // console.log("clicked: x " + locationX + " y " + locationY)
157

    
158
        if(svgRatio > viewRatio){
159
            // svg is spaced in the middle 
160
            // top down is filled
161
            let xRatio = viewSize.width / mapImage.viewBox.width
162
            let yFilled = xRatio * mapImage.viewBox.height
163
            let offsetY = (viewSize.height - yFilled) / 2
164

    
165
            mapY = ((locationY - offsetY) / yFilled) * mapImage.viewBox.height
166
        }
167
        else if(svgRatio < viewRatio){
168
            // svg is spaced in the center 
169
            // left right is filled
170
            let yRatio = viewSize.height / mapImage.viewBox.height
171
            let xFilled = yRatio * mapImage.viewBox.width
172
            let offsetX = (viewSize.width - xFilled) / 2
173

    
174
            mapX = ((locationX - offsetX) / xFilled) * mapImage.viewBox.width
175
        }
176

    
177
        // Check if the touch event is within the bounds of a room
178
        let clickedRoom: Room | undefined = undefined
179

    
180
        const maxNumberDistance = 100
181

    
182
        let minDistance = Number.MAX_VALUE
183

    
184
        for (let i = 0; i < roomList.length; i++) {
185
            const room: Room = roomList[i]
186
            if (room.in_plan) {
187
                // TODO
188
                // console.log()
189
                console.log("Room + " + room.id + " x: " + room.number_x + " y: " + room.number_y)
190

    
191
                const currentDistance = calculateStraightLineDistance(room.number_x, room.number_y, mapX, mapY)
192

    
193
                if (currentDistance < minDistance) {
194
                    minDistance = currentDistance
195
                    clickedRoom = room
196
                }
197
            }
198
        };
199

    
200
        console.log()
201
        console.log('Room clicked with id:', clickedRoom?.id);
202
        console.log("x " + clickedRoom?.number_x + " y " + clickedRoom?.number_y)
203
        console.log(minDistance)
204
        console.log("translated clicked: x " + mapX + " y " + mapY)
205

    
206
        if (clickedRoom && minDistance < maxNumberDistance) {
207
            // TODO        
208

    
209
            // Perform any actions you need with the clicked room
210

    
211
            // TODO fix -> point recognition
212
            setSelectedRoom && setSelectedRoom(clickedRoom)
213
        } else {
214
            console.log("no room found")
215
        }
216
        console.log("")
217
    };
218

    
219
    const [viewSize, setViewSize] = useState({ height: 0, width: 0 });
220

    
221
    useEffect(() => {
222
        setViewSize({
223
            width: fullScreenMode ?
224
                Dimensions.get('window').width - 20 : // full screen
225
                Dimensions.get('window').width - 20, // not full screen
226
            height: fullScreenMode ?
227
                Dimensions.get('window').height - 122.5 : // full scren
228
                Dimensions.get('window').height -350, // not full screen
229
        })
230
    }, [fullScreenMode])
231

    
232

    
233
    return (
234
        // container
235
        <View
236
            height={viewSize.height}
237
            width={viewSize.width}
238
            // @ts-ignore
239
            style={
240
                fullScreenMode ? {
241
                    position: "absolute",
242
                    top: -230,
243
                    left: 0,
244
                    zIndex: 1000,
245
                    backgroundColor: "white",
246
                }
247
                    :
248
                    {
249
                        overflow: "hidden",
250
                    }
251
            }
252
            borderColor={"light.300"}
253
            borderRadius={10}
254
            borderWidth={fullScreenMode ? 0 : 1}
255
        >
256
            {/* control panel */}
257
            <Flex direction="row" alignItems="center" justify="flex-end" style={{ height: 50, width: 100, top: fullScreenMode? 10 : 0, right: fullScreenMode ? 20 : 15, position: "absolute", zIndex: 5 }}>
258
                <Pressable padding={1.5} backgroundColor={"#654B07"} borderRadius={5} marginRight={selectedRoom ? 1 : -2} onPress={handleFullScreenPressed}>
259
                    <FullscreenIcon color="white" />
260
                </Pressable>
261
                {
262
                    selectedRoom &&
263
                    <Pressable padding={1.5} backgroundColor={"#654B07"} borderRadius={5} marginRight={-2} onPress={() => { zoomToRoom(selectedRoom) }}>
264
                        <ExitfullscreenIcon color="white" />
265
                    </Pressable>
266
                }
267

    
268
            </Flex>
269

    
270
            <PinchGestureHandler
271
                ref={pinchRef}
272
                // @ts-ignore
273
                onGestureEvent={onGestureEvent}
274
                simultaneousHandlers={[svgRef, panRef]}>
275
                <Animated.View style={[{ flex: 1 }, animatedStyle]}>
276
                    <PanGestureHandler
277
                        ref={panRef}
278
                        simultaneousHandlers={[svgRef, pinchRef]}
279
                        onGestureEvent={onPanEvent}
280
                        onHandlerStateChange={(nativeEvent: any) => {
281
                            if (nativeEvent.state === State.END) {
282
                                console.log(nativeEvent)
283
                            }
284
                        }}
285
                    >
286
                        <Animated.View style={[{
287
                            flex: 1,
288
                        }]}>
289
                            <Svg
290
                                id="svgMap"
291
                                onPress={handleSvgPress}
292
                            >
293
                                {mapImage && mapImage.viewBox &&
294
                                    // background image                    
295
                                    <SvgXml
296
                                        xml={mapImage.svg}
297
                                        width={"100%"}
298
                                        height={"100%"}
299
                                        viewBox={`${0} ${0} ${mapImage.viewBox.width} ${mapImage.viewBox.height}`}
300
                                        // onLayout={(event) => {
301
                                        //     setSvgDimensions({
302
                                        //         width: event.nativeEvent.layout.width,
303
                                        //         height: event.nativeEvent.layout.height,
304
                                        //     });
305
                                        // }}
306
                                    >
307
                                    </SvgXml>
308
                                }
309

    
310
                                {mapImage && roomList && roomList.length > 0 &&
311

    
312
                                    roomList.map((room, index) => {
313
                                        if (!room.in_plan) {
314
                                            return
315
                                        }
316
                                        return (
317
                                            <Path
318
                                                id={"roomList_" + index.toString()}
319
                                                d={room.svg_path}  // The path data defining the shape of the room
320
                                                fill={selectedRoom && room.id == selectedRoom.id ? "#E6F7FF" : "white"}        // Fill the room shape with no color
321
                                                stroke="#66B2FF"       // Outline color
322
                                                strokeWidth={0.3}     // Outline width
323
                                                fillOpacity={selectedRoom && room.id == selectedRoom.id ? 1 : 0.2}
324
                                            />
325
                                        )
326
                                    })}
327

    
328

    
329

    
330
                                {mapImage && roomList && roomList.length > 0 &&
331

    
332
                                    roomList.map((room) => {
333
                                        if (!room.in_plan) {
334
                                            return
335
                                        }
336
                                        return (
337
                                            < SvgText
338
                                                x={room.number_x}     // Adjust the x-coordinate based on your room data
339
                                                y={room.number_y}     // Adjust the y-coordinate based on your room data
340
                                                fill="red"      // Text color
341
                                                fontSize={8}       // Font size
342
                                                textAnchor="middle"  // Center the text horizontally
343
                                                fontStyle='italic'
344
                                                vectorEffect='non-scaling-stroke' // Helps prevent stroke from scaling
345
                                            >
346
                                                {room.id}
347
                                            </SvgText>
348
                                        )
349
                                    })}
350

    
351
                            </Svg>
352
                        </Animated.View>
353
                    </PanGestureHandler>
354
                </Animated.View>
355
            </PinchGestureHandler >
356
        </View >
357
    );
358
};
359

    
360
export default CastlePlanView;
361

    
    (1-1/1)