@viamrobotics/motion-tools 1.15.7 → 1.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/buf/common/v1/common_pb.d.ts +38 -0
- package/dist/buf/common/v1/common_pb.js +64 -0
- package/dist/buf/draw/v1/scene_pb.d.ts +6 -2
- package/dist/buf/draw/v1/scene_pb.js +9 -3
- package/dist/components/Entities/Geometry.svelte +1 -1
- package/dist/components/PCD.svelte +4 -1
- package/dist/components/PCD.svelte.d.ts +1 -0
- package/dist/components/SceneProviders.svelte +0 -2
- package/dist/components/{Lasso → Selection}/Debug.svelte +8 -8
- package/dist/components/{Lasso → Selection}/Debug.svelte.d.ts +2 -2
- package/dist/components/Selection/Ellipse.svelte +293 -0
- package/dist/components/Selection/Ellipse.svelte.d.ts +7 -0
- package/dist/components/{Lasso → Selection}/Lasso.svelte +31 -61
- package/dist/components/{Lasso → Selection}/Lasso.svelte.d.ts +1 -0
- package/dist/components/{Lasso → Selection}/Tool.svelte +50 -15
- package/dist/components/{Lasso → Selection}/traits.d.ts +11 -2
- package/dist/components/{Lasso → Selection}/traits.js +7 -2
- package/dist/components/Selection/utils.d.ts +5 -0
- package/dist/components/Selection/utils.js +38 -0
- package/dist/components/Snapshot.svelte +8 -0
- package/dist/components/overlay/RefreshRate.svelte +7 -6
- package/dist/components/overlay/RefreshRate.svelte.d.ts +1 -1
- package/dist/components/overlay/settings/Settings.svelte +16 -8
- package/dist/components/xr/OriginMarker.svelte +94 -17
- package/dist/hooks/useControls.svelte.d.ts +1 -0
- package/dist/hooks/useControls.svelte.js +4 -0
- package/dist/hooks/useGeometries.svelte.js +7 -9
- package/dist/hooks/usePointcloudObjects.svelte.js +7 -6
- package/dist/hooks/usePointclouds.svelte.js +7 -6
- package/dist/hooks/usePose.svelte.js +3 -3
- package/dist/hooks/useSettings.svelte.d.ts +13 -1
- package/dist/hooks/useSettings.svelte.js +13 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/hooks/useMachineSettings.svelte.d.ts +0 -13
- package/dist/hooks/useMachineSettings.svelte.js +0 -58
- /package/dist/components/{Lasso → Selection}/Tool.svelte.d.ts +0 -0
|
@@ -668,6 +668,44 @@ export declare class DoCommandResponse extends Message<DoCommandResponse> {
|
|
|
668
668
|
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): DoCommandResponse;
|
|
669
669
|
static equals(a: DoCommandResponse | PlainMessage<DoCommandResponse> | undefined, b: DoCommandResponse | PlainMessage<DoCommandResponse> | undefined): boolean;
|
|
670
670
|
}
|
|
671
|
+
/**
|
|
672
|
+
* GetStatusRequest represents a generic GetStatus input
|
|
673
|
+
*
|
|
674
|
+
* @generated from message viam.common.v1.GetStatusRequest
|
|
675
|
+
*/
|
|
676
|
+
export declare class GetStatusRequest extends Message<GetStatusRequest> {
|
|
677
|
+
/**
|
|
678
|
+
* @generated from field: string name = 1;
|
|
679
|
+
*/
|
|
680
|
+
name: string;
|
|
681
|
+
constructor(data?: PartialMessage<GetStatusRequest>);
|
|
682
|
+
static readonly runtime: typeof proto3;
|
|
683
|
+
static readonly typeName = "viam.common.v1.GetStatusRequest";
|
|
684
|
+
static readonly fields: FieldList;
|
|
685
|
+
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): GetStatusRequest;
|
|
686
|
+
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): GetStatusRequest;
|
|
687
|
+
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): GetStatusRequest;
|
|
688
|
+
static equals(a: GetStatusRequest | PlainMessage<GetStatusRequest> | undefined, b: GetStatusRequest | PlainMessage<GetStatusRequest> | undefined): boolean;
|
|
689
|
+
}
|
|
690
|
+
/**
|
|
691
|
+
* GetStatusResponse represents a generic GetStatus output
|
|
692
|
+
*
|
|
693
|
+
* @generated from message viam.common.v1.GetStatusResponse
|
|
694
|
+
*/
|
|
695
|
+
export declare class GetStatusResponse extends Message<GetStatusResponse> {
|
|
696
|
+
/**
|
|
697
|
+
* @generated from field: google.protobuf.Struct result = 1;
|
|
698
|
+
*/
|
|
699
|
+
result?: Struct;
|
|
700
|
+
constructor(data?: PartialMessage<GetStatusResponse>);
|
|
701
|
+
static readonly runtime: typeof proto3;
|
|
702
|
+
static readonly typeName = "viam.common.v1.GetStatusResponse";
|
|
703
|
+
static readonly fields: FieldList;
|
|
704
|
+
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): GetStatusResponse;
|
|
705
|
+
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): GetStatusResponse;
|
|
706
|
+
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): GetStatusResponse;
|
|
707
|
+
static equals(a: GetStatusResponse | PlainMessage<GetStatusResponse> | undefined, b: GetStatusResponse | PlainMessage<GetStatusResponse> | undefined): boolean;
|
|
708
|
+
}
|
|
671
709
|
/**
|
|
672
710
|
* @generated from message viam.common.v1.GetKinematicsRequest
|
|
673
711
|
*/
|
|
@@ -968,6 +968,70 @@ export class DoCommandResponse extends Message {
|
|
|
968
968
|
return proto3.util.equals(DoCommandResponse, a, b);
|
|
969
969
|
}
|
|
970
970
|
}
|
|
971
|
+
/**
|
|
972
|
+
* GetStatusRequest represents a generic GetStatus input
|
|
973
|
+
*
|
|
974
|
+
* @generated from message viam.common.v1.GetStatusRequest
|
|
975
|
+
*/
|
|
976
|
+
export class GetStatusRequest extends Message {
|
|
977
|
+
/**
|
|
978
|
+
* @generated from field: string name = 1;
|
|
979
|
+
*/
|
|
980
|
+
name = "";
|
|
981
|
+
constructor(data) {
|
|
982
|
+
super();
|
|
983
|
+
proto3.util.initPartial(data, this);
|
|
984
|
+
}
|
|
985
|
+
static runtime = proto3;
|
|
986
|
+
static typeName = "viam.common.v1.GetStatusRequest";
|
|
987
|
+
static fields = proto3.util.newFieldList(() => [
|
|
988
|
+
{ no: 1, name: "name", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
|
989
|
+
]);
|
|
990
|
+
static fromBinary(bytes, options) {
|
|
991
|
+
return new GetStatusRequest().fromBinary(bytes, options);
|
|
992
|
+
}
|
|
993
|
+
static fromJson(jsonValue, options) {
|
|
994
|
+
return new GetStatusRequest().fromJson(jsonValue, options);
|
|
995
|
+
}
|
|
996
|
+
static fromJsonString(jsonString, options) {
|
|
997
|
+
return new GetStatusRequest().fromJsonString(jsonString, options);
|
|
998
|
+
}
|
|
999
|
+
static equals(a, b) {
|
|
1000
|
+
return proto3.util.equals(GetStatusRequest, a, b);
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
/**
|
|
1004
|
+
* GetStatusResponse represents a generic GetStatus output
|
|
1005
|
+
*
|
|
1006
|
+
* @generated from message viam.common.v1.GetStatusResponse
|
|
1007
|
+
*/
|
|
1008
|
+
export class GetStatusResponse extends Message {
|
|
1009
|
+
/**
|
|
1010
|
+
* @generated from field: google.protobuf.Struct result = 1;
|
|
1011
|
+
*/
|
|
1012
|
+
result;
|
|
1013
|
+
constructor(data) {
|
|
1014
|
+
super();
|
|
1015
|
+
proto3.util.initPartial(data, this);
|
|
1016
|
+
}
|
|
1017
|
+
static runtime = proto3;
|
|
1018
|
+
static typeName = "viam.common.v1.GetStatusResponse";
|
|
1019
|
+
static fields = proto3.util.newFieldList(() => [
|
|
1020
|
+
{ no: 1, name: "result", kind: "message", T: Struct },
|
|
1021
|
+
]);
|
|
1022
|
+
static fromBinary(bytes, options) {
|
|
1023
|
+
return new GetStatusResponse().fromBinary(bytes, options);
|
|
1024
|
+
}
|
|
1025
|
+
static fromJson(jsonValue, options) {
|
|
1026
|
+
return new GetStatusResponse().fromJson(jsonValue, options);
|
|
1027
|
+
}
|
|
1028
|
+
static fromJsonString(jsonString, options) {
|
|
1029
|
+
return new GetStatusResponse().fromJsonString(jsonString, options);
|
|
1030
|
+
}
|
|
1031
|
+
static equals(a, b) {
|
|
1032
|
+
return proto3.util.equals(GetStatusResponse, a, b);
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
971
1035
|
/**
|
|
972
1036
|
* @generated from message viam.common.v1.GetKinematicsRequest
|
|
973
1037
|
*/
|
|
@@ -52,11 +52,15 @@ export declare enum RenderShapes {
|
|
|
52
52
|
NURBS = 5
|
|
53
53
|
}
|
|
54
54
|
/**
|
|
55
|
-
* for orthographic camera props
|
|
56
|
-
*
|
|
57
55
|
* @generated from message draw.v1.OrthographicCamera
|
|
58
56
|
*/
|
|
59
57
|
export declare class OrthographicCamera extends Message<OrthographicCamera> {
|
|
58
|
+
/**
|
|
59
|
+
* for orthographic camera props
|
|
60
|
+
*
|
|
61
|
+
* @generated from field: optional float zoom = 1;
|
|
62
|
+
*/
|
|
63
|
+
zoom?: number;
|
|
60
64
|
constructor(data?: PartialMessage<OrthographicCamera>);
|
|
61
65
|
static readonly runtime: typeof proto3;
|
|
62
66
|
static readonly typeName = "draw.v1.OrthographicCamera";
|
|
@@ -73,18 +73,24 @@ proto3.util.setEnumType(RenderShapes, "draw.v1.RenderShapes", [
|
|
|
73
73
|
{ no: 5, name: "RENDER_SHAPES_NURBS" },
|
|
74
74
|
]);
|
|
75
75
|
/**
|
|
76
|
-
* for orthographic camera props
|
|
77
|
-
*
|
|
78
76
|
* @generated from message draw.v1.OrthographicCamera
|
|
79
77
|
*/
|
|
80
78
|
export class OrthographicCamera extends Message {
|
|
79
|
+
/**
|
|
80
|
+
* for orthographic camera props
|
|
81
|
+
*
|
|
82
|
+
* @generated from field: optional float zoom = 1;
|
|
83
|
+
*/
|
|
84
|
+
zoom;
|
|
81
85
|
constructor(data) {
|
|
82
86
|
super();
|
|
83
87
|
proto3.util.initPartial(data, this);
|
|
84
88
|
}
|
|
85
89
|
static runtime = proto3;
|
|
86
90
|
static typeName = "draw.v1.OrthographicCamera";
|
|
87
|
-
static fields = proto3.util.newFieldList(() => [
|
|
91
|
+
static fields = proto3.util.newFieldList(() => [
|
|
92
|
+
{ no: 1, name: "zoom", kind: "scalar", T: 2 /* ScalarType.FLOAT */, opt: true },
|
|
93
|
+
]);
|
|
88
94
|
static fromBinary(bytes, options) {
|
|
89
95
|
return new OrthographicCamera().fromBinary(bytes, options);
|
|
90
96
|
}
|
|
@@ -9,9 +9,10 @@
|
|
|
9
9
|
data: Uint8Array
|
|
10
10
|
name?: string
|
|
11
11
|
renderOrder?: number
|
|
12
|
+
oncreate?: (positions: Float32Array, colors: Uint8Array | null) => void
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
let { data, name, renderOrder }: Props = $props()
|
|
15
|
+
let { data, name, renderOrder, oncreate }: Props = $props()
|
|
15
16
|
|
|
16
17
|
const world = useWorld()
|
|
17
18
|
|
|
@@ -32,6 +33,8 @@
|
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
entity = world.spawn(...entityTraits)
|
|
36
|
+
|
|
37
|
+
oncreate?.(positions, colors)
|
|
35
38
|
})
|
|
36
39
|
|
|
37
40
|
return () => {
|
|
@@ -16,7 +16,6 @@
|
|
|
16
16
|
import { provideGeometries } from '../hooks/useGeometries.svelte'
|
|
17
17
|
import { provideLinkedEntities } from '../hooks/useLinked.svelte'
|
|
18
18
|
import { provideLogs } from '../hooks/useLogs.svelte'
|
|
19
|
-
import { provideMachineSettings } from '../hooks/useMachineSettings.svelte'
|
|
20
19
|
import { usePartID } from '../hooks/usePartID.svelte'
|
|
21
20
|
import { providePointcloudObjects } from '../hooks/usePointcloudObjects.svelte'
|
|
22
21
|
import { providePointclouds } from '../hooks/usePointclouds.svelte'
|
|
@@ -37,7 +36,6 @@
|
|
|
37
36
|
|
|
38
37
|
provideCameraControls(() => cameraPose)
|
|
39
38
|
provideTransformControls()
|
|
40
|
-
provideMachineSettings()
|
|
41
39
|
provideLogs()
|
|
42
40
|
|
|
43
41
|
provideOrigin()
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<!--
|
|
2
2
|
@component
|
|
3
3
|
|
|
4
|
-
Shows all steps for querying points within a
|
|
4
|
+
Shows all steps for querying points within a selection
|
|
5
5
|
-->
|
|
6
6
|
<script lang="ts">
|
|
7
7
|
import type { Entity } from 'koota'
|
|
@@ -11,22 +11,22 @@ Shows all steps for querying points within a lasso selection
|
|
|
11
11
|
|
|
12
12
|
import { traits, useTrait } from '../../ecs'
|
|
13
13
|
|
|
14
|
-
import * as
|
|
14
|
+
import * as selectionTraits from './traits'
|
|
15
15
|
|
|
16
16
|
const box3 = new Box3()
|
|
17
17
|
const min = new Vector3()
|
|
18
18
|
const max = new Vector3()
|
|
19
19
|
|
|
20
20
|
interface Props {
|
|
21
|
-
|
|
21
|
+
selection: Entity
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
let {
|
|
24
|
+
let { selection }: Props = $props()
|
|
25
25
|
|
|
26
|
-
const indices = useTrait(() =>
|
|
27
|
-
const positions = useTrait(() =>
|
|
28
|
-
const box = useTrait(() =>
|
|
29
|
-
const boxes = useTrait(() =>
|
|
26
|
+
const indices = useTrait(() => selection, selectionTraits.Indices)
|
|
27
|
+
const positions = useTrait(() => selection, traits.LinePositions)
|
|
28
|
+
const box = useTrait(() => selection, selectionTraits.Box)
|
|
29
|
+
const boxes = useTrait(() => selection, selectionTraits.Boxes)
|
|
30
30
|
|
|
31
31
|
const geometry = new BufferGeometry()
|
|
32
32
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { Entity } from 'koota';
|
|
2
2
|
interface Props {
|
|
3
|
-
|
|
3
|
+
selection: Entity;
|
|
4
4
|
}
|
|
5
|
-
/** Shows all steps for querying points within a
|
|
5
|
+
/** Shows all steps for querying points within a selection */
|
|
6
6
|
declare const Debug: import("svelte").Component<Props, {}, "">;
|
|
7
7
|
type Debug = ReturnType<typeof Debug>;
|
|
8
8
|
export default Debug;
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { ShapecastCallbacks } from 'three-mesh-bvh'
|
|
3
|
+
|
|
4
|
+
import { useThrelte } from '@threlte/core'
|
|
5
|
+
import earcut from 'earcut'
|
|
6
|
+
import { Not } from 'koota'
|
|
7
|
+
import { Box3, Triangle, Vector3 } from 'three'
|
|
8
|
+
|
|
9
|
+
import { createBufferGeometry } from '../../attribute'
|
|
10
|
+
import { traits, useQuery, useWorld } from '../../ecs'
|
|
11
|
+
import { useCameraControls } from '../../hooks/useControls.svelte'
|
|
12
|
+
|
|
13
|
+
import Debug from './Debug.svelte'
|
|
14
|
+
import * as selectionTraits from './traits'
|
|
15
|
+
import { getTriangleBoxesFromIndices, getTriangleFromIndex, raycast } from './utils'
|
|
16
|
+
|
|
17
|
+
interface Props {
|
|
18
|
+
active?: boolean
|
|
19
|
+
debug?: boolean
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let { active = false, debug = false }: Props = $props()
|
|
23
|
+
|
|
24
|
+
const world = useWorld()
|
|
25
|
+
const controls = useCameraControls()
|
|
26
|
+
const { scene, dom, camera } = useThrelte()
|
|
27
|
+
|
|
28
|
+
const box3 = new Box3()
|
|
29
|
+
const min = new Vector3()
|
|
30
|
+
const max = new Vector3()
|
|
31
|
+
|
|
32
|
+
const triangle = new Triangle()
|
|
33
|
+
const triangleBox = new Box3()
|
|
34
|
+
|
|
35
|
+
let frameScheduled = false
|
|
36
|
+
let drawing = false
|
|
37
|
+
|
|
38
|
+
const onpointerdown = (event: PointerEvent) => {
|
|
39
|
+
if (!event.shiftKey || !active) return
|
|
40
|
+
|
|
41
|
+
const { x, y } = raycast(event, camera.current)
|
|
42
|
+
|
|
43
|
+
drawing = true
|
|
44
|
+
|
|
45
|
+
world.spawn(
|
|
46
|
+
traits.LinePositions(new Float32Array([x, y, 0])),
|
|
47
|
+
selectionTraits.StartPoint({ x, y }),
|
|
48
|
+
traits.LineWidth(1.5),
|
|
49
|
+
traits.RenderOrder(999),
|
|
50
|
+
traits.Material({ depthTest: false }),
|
|
51
|
+
traits.Color({ r: 1, g: 0, b: 0 }),
|
|
52
|
+
selectionTraits.Box({ minX: x, minY: y, maxX: x, maxY: y }),
|
|
53
|
+
selectionTraits.Ellipse
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
if (controls.current) {
|
|
57
|
+
controls.current.enabled = false
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const onpointermove = (event: PointerEvent) => {
|
|
62
|
+
if (!drawing || !active) return
|
|
63
|
+
|
|
64
|
+
let ellipse = world.query(selectionTraits.Ellipse).at(-1)
|
|
65
|
+
|
|
66
|
+
if (!ellipse) return
|
|
67
|
+
|
|
68
|
+
if (frameScheduled) return
|
|
69
|
+
|
|
70
|
+
frameScheduled = true
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* pointermove can execute at a rate much higher than screen
|
|
74
|
+
* refresh, creating huge polygon vertex counts, so we cap it.
|
|
75
|
+
*/
|
|
76
|
+
requestAnimationFrame(() => {
|
|
77
|
+
frameScheduled = false
|
|
78
|
+
|
|
79
|
+
const { x, y } = raycast(event, camera.current)
|
|
80
|
+
const positions = ellipse.get(traits.LinePositions)
|
|
81
|
+
const startPoint = ellipse.get(selectionTraits.StartPoint)
|
|
82
|
+
const box = ellipse.get(selectionTraits.Box)
|
|
83
|
+
|
|
84
|
+
if (!positions || !box || !startPoint) return
|
|
85
|
+
|
|
86
|
+
let minX = startPoint.x
|
|
87
|
+
let minY = startPoint.y
|
|
88
|
+
let maxX = startPoint.x
|
|
89
|
+
let maxY = startPoint.y
|
|
90
|
+
|
|
91
|
+
if (x < minX) minX = x
|
|
92
|
+
else if (x > maxX) maxX = x
|
|
93
|
+
|
|
94
|
+
if (y < minY) minY = y
|
|
95
|
+
else if (y > maxY) maxY = y
|
|
96
|
+
|
|
97
|
+
const nextPositions = ellipsePoints(minX, maxX, minY, maxY, 100)
|
|
98
|
+
|
|
99
|
+
ellipse.set(traits.LinePositions, new Float32Array(nextPositions))
|
|
100
|
+
ellipse.set(selectionTraits.Box, { minX, minY, maxX, maxY })
|
|
101
|
+
})
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const ellipsePoints = (
|
|
105
|
+
minX: number,
|
|
106
|
+
maxX: number,
|
|
107
|
+
minY: number,
|
|
108
|
+
maxY: number,
|
|
109
|
+
numPoints: number
|
|
110
|
+
): Float32Array => {
|
|
111
|
+
const cx = (minX + maxX) / 2
|
|
112
|
+
const cy = (minY + maxY) / 2
|
|
113
|
+
const rx = (maxX - minX) / 2
|
|
114
|
+
const ry = (maxY - minY) / 2
|
|
115
|
+
|
|
116
|
+
const points = new Float32Array(numPoints * 3)
|
|
117
|
+
|
|
118
|
+
for (let i = 0; i < numPoints; i++) {
|
|
119
|
+
const t = (i / numPoints) * 2 * Math.PI
|
|
120
|
+
|
|
121
|
+
points[i * 3] = cx + rx * Math.cos(t)
|
|
122
|
+
points[i * 3 + 1] = cy + ry * Math.sin(t)
|
|
123
|
+
points[i * 3 + 2] = 0
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return points
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const onpointerleave = () => {
|
|
130
|
+
if (!drawing || !active) return
|
|
131
|
+
|
|
132
|
+
onpointerup()
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const onpointerup = () => {
|
|
136
|
+
if (!drawing || !active) return
|
|
137
|
+
|
|
138
|
+
drawing = false
|
|
139
|
+
|
|
140
|
+
let ellipse = world.query(selectionTraits.Ellipse).at(-1)
|
|
141
|
+
|
|
142
|
+
if (!ellipse) return
|
|
143
|
+
|
|
144
|
+
let positions = ellipse.get(traits.LinePositions)
|
|
145
|
+
|
|
146
|
+
if (!positions) return
|
|
147
|
+
|
|
148
|
+
if (controls.current) {
|
|
149
|
+
controls.current.enabled = true
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const indices = earcut(positions, undefined, 3)
|
|
153
|
+
if (debug) {
|
|
154
|
+
ellipse.add(selectionTraits.Indices(new Uint16Array(indices)))
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const boxes: selectionTraits.AABB[] = getTriangleBoxesFromIndices(indices, positions)
|
|
158
|
+
if (debug) {
|
|
159
|
+
ellipse.add(selectionTraits.Boxes(boxes))
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const ellipseBox = ellipse.get(selectionTraits.Box)
|
|
163
|
+
|
|
164
|
+
if (!ellipseBox) return
|
|
165
|
+
|
|
166
|
+
min.set(ellipseBox.minX, ellipseBox.minY, Number.NEGATIVE_INFINITY)
|
|
167
|
+
max.set(ellipseBox.maxX, ellipseBox.maxY, Number.POSITIVE_INFINITY)
|
|
168
|
+
box3.set(min, max)
|
|
169
|
+
|
|
170
|
+
const enclosedPoints: number[] = []
|
|
171
|
+
|
|
172
|
+
for (const pointsEntity of world.query(
|
|
173
|
+
traits.Points,
|
|
174
|
+
Not(selectionTraits.SelectionEnclosedPoints)
|
|
175
|
+
)) {
|
|
176
|
+
const geometry = pointsEntity.get(traits.BufferGeometry)
|
|
177
|
+
|
|
178
|
+
if (!geometry) return
|
|
179
|
+
|
|
180
|
+
const points = scene.getObjectByName(pointsEntity as unknown as string)
|
|
181
|
+
|
|
182
|
+
if (!points) {
|
|
183
|
+
return
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
geometry.boundsTree?.shapecast({
|
|
187
|
+
intersectsBounds: (box) => {
|
|
188
|
+
return box.intersectsBox(box3)
|
|
189
|
+
},
|
|
190
|
+
|
|
191
|
+
intersectsPoint: (point: Vector3) => {
|
|
192
|
+
for (let i = 0, j = 0, l = indices.length; i < l; i += 3, j += 1) {
|
|
193
|
+
const { minX, minY, maxX, maxY } = boxes[j]
|
|
194
|
+
|
|
195
|
+
min.set(minX, minY, Number.NEGATIVE_INFINITY)
|
|
196
|
+
max.set(maxX, maxY, Number.POSITIVE_INFINITY)
|
|
197
|
+
triangleBox.set(min, max)
|
|
198
|
+
|
|
199
|
+
if (triangleBox.containsPoint(point)) {
|
|
200
|
+
getTriangleFromIndex(i, indices, positions, triangle)
|
|
201
|
+
|
|
202
|
+
if (triangle.containsPoint(point)) {
|
|
203
|
+
enclosedPoints.push(point.x, point.y, point.z)
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
// intersectsPoint is not yet in typedef, this can be removed when it is added
|
|
209
|
+
} as ShapecastCallbacks)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const ellipseResultGeometry = createBufferGeometry(new Float32Array(enclosedPoints))
|
|
213
|
+
|
|
214
|
+
world.spawn(
|
|
215
|
+
traits.Name('Ellipse result'),
|
|
216
|
+
traits.BufferGeometry(ellipseResultGeometry),
|
|
217
|
+
traits.Color({ r: 1, g: 0, b: 0 }),
|
|
218
|
+
traits.RenderOrder(999),
|
|
219
|
+
traits.Material({ depthTest: false }),
|
|
220
|
+
traits.Points,
|
|
221
|
+
traits.Removable,
|
|
222
|
+
selectionTraits.SelectionEnclosedPoints,
|
|
223
|
+
selectionTraits.PointsCapturedBy(ellipse)
|
|
224
|
+
)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const onkeydown = (event: KeyboardEvent) => {
|
|
228
|
+
if (event.key === 'Shift') {
|
|
229
|
+
dom.style.cursor = 'crosshair'
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const onkeyup = (event: KeyboardEvent) => {
|
|
234
|
+
if (event.key === 'Shift') {
|
|
235
|
+
dom.style.removeProperty('cursor')
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
$effect(() => {
|
|
240
|
+
globalThis.addEventListener('keydown', onkeydown)
|
|
241
|
+
globalThis.addEventListener('keyup', onkeyup)
|
|
242
|
+
dom.addEventListener('pointerdown', onpointerdown)
|
|
243
|
+
dom.addEventListener('pointermove', onpointermove)
|
|
244
|
+
dom.addEventListener('pointerup', onpointerup)
|
|
245
|
+
dom.addEventListener('pointerleave', onpointerleave)
|
|
246
|
+
|
|
247
|
+
return () => {
|
|
248
|
+
globalThis.removeEventListener('keydown', onkeydown)
|
|
249
|
+
globalThis.removeEventListener('keyup', onkeyup)
|
|
250
|
+
dom.removeEventListener('pointerdown', onpointerdown)
|
|
251
|
+
dom.removeEventListener('pointermove', onpointermove)
|
|
252
|
+
dom.removeEventListener('pointerup', onpointerup)
|
|
253
|
+
dom.removeEventListener('pointerleave', onpointerleave)
|
|
254
|
+
}
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
const ellipses = useQuery(selectionTraits.Ellipse)
|
|
258
|
+
|
|
259
|
+
$effect(() => {
|
|
260
|
+
if (!controls.current) return
|
|
261
|
+
|
|
262
|
+
const currentControls = controls.current
|
|
263
|
+
|
|
264
|
+
const { minPolarAngle, maxPolarAngle } = currentControls
|
|
265
|
+
|
|
266
|
+
// Locks the camera to top down while this component is mounted
|
|
267
|
+
currentControls.polarAngle = 0
|
|
268
|
+
currentControls.minPolarAngle = 0
|
|
269
|
+
currentControls.maxPolarAngle = 0
|
|
270
|
+
|
|
271
|
+
return () => {
|
|
272
|
+
currentControls.minPolarAngle = minPolarAngle
|
|
273
|
+
currentControls.maxPolarAngle = maxPolarAngle
|
|
274
|
+
}
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
// On unmount, destroy all lasso related entities
|
|
278
|
+
$effect(() => {
|
|
279
|
+
return () => {
|
|
280
|
+
for (const entity of world.query(selectionTraits.SelectionEnclosedPoints)) {
|
|
281
|
+
if (world.has(entity)) {
|
|
282
|
+
entity.destroy()
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
})
|
|
287
|
+
</script>
|
|
288
|
+
|
|
289
|
+
{#if debug}
|
|
290
|
+
{#each ellipses.current as ellipse (ellipse)}
|
|
291
|
+
<Debug selection={ellipse} />
|
|
292
|
+
{/each}
|
|
293
|
+
{/if}
|