@lumikmz/kmz 0.6.2 → 0.6.4
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/index.d.mts +13 -1
- package/dist/index.mjs +65 -26
- package/package.json +1 -1
- package/src/plan/plan-mapping2d.ts +81 -34
- package/src/plan/plan.test.ts +32 -0
- package/src/types/action.ts +13 -0
package/dist/index.d.mts
CHANGED
|
@@ -575,8 +575,20 @@ interface MegaphoneParam {
|
|
|
575
575
|
}
|
|
576
576
|
type MegaphoneAction = ActionBase<"megaphone", MegaphoneParam>;
|
|
577
577
|
/** M4D/M4TD only — starts gimbal swing in smart-oblique mode. */
|
|
578
|
+
interface SmartObliquePoint {
|
|
579
|
+
/** seconds before this pose starts; DJI exports currently use 0. */
|
|
580
|
+
smartObliqueRunningTime: number;
|
|
581
|
+
/** seconds to hold this pose for smart-oblique shooting. */
|
|
582
|
+
smartObliqueStayTime: number;
|
|
583
|
+
smartObliqueEulerPitch: Degrees;
|
|
584
|
+
smartObliqueEulerRoll: Degrees;
|
|
585
|
+
smartObliqueEulerYaw: Degrees;
|
|
586
|
+
}
|
|
578
587
|
interface StartSmartObliqueParam {
|
|
588
|
+
/** Observed in DJI exports; older generated files may omit it. */
|
|
589
|
+
smartObliqueCycleMode?: "unlimited";
|
|
579
590
|
payloadPositionIndex: number;
|
|
591
|
+
smartObliquePoint?: SmartObliquePoint[];
|
|
580
592
|
}
|
|
581
593
|
type StartSmartObliqueAction = ActionBase<"startSmartOblique", StartSmartObliqueParam>;
|
|
582
594
|
/** M4D/M4TD only — stops gimbal swing in smart-oblique mode. */
|
|
@@ -931,4 +943,4 @@ declare function validate(doc: Template | Waylines): Issue[];
|
|
|
931
943
|
/** Great-circle distance between two WGS-84 coordinates, in meters. */
|
|
932
944
|
declare function haversineMeters(a: LngLat, b: LngLat): number;
|
|
933
945
|
//#endregion
|
|
934
|
-
export { AccurateShootAction, AccurateShootParam, Action, ActionActuatorFunc, ActionGroup, ActionGroupMode, ActionTrigger, ActionTriggerType, AircraftPathMode, AutoRerouteInfo, CoordinateMode, CustomDirNameAction, CustomDirNameParam, Degrees, DroneEnum, DroneInfo, ExecuteHeightMode, ExecuteRCLostAction, ExitOnRCLost, FinishAction, FlyToWaylineMode, FocusAction, FocusMode, FocusParam, GimbalAngleLockAction, GimbalAngleLockParam, GimbalAngleUnlockAction, GimbalEvenlyRotateAction, GimbalEvenlyRotateParam, GimbalHeadingYawBase, GimbalPitchMode, GimbalRotateAction, GimbalRotateMode, GimbalRotateParam, HoverAction, HoverParam, Issue, KmzError, LensType, LidarReturnMode, LidarScanningMode, LngLat, LngLatHeight, Mapping2dFolder, Mapping2dPlacemark, Mapping2dTemplate, Mapping3dFolder, Mapping3dPlacemark, Mapping3dTemplate, MappingHeadingMode, MappingHeadingParam, MappingStripFolder, MappingStripPlacemark, MappingStripTemplate, MegaphoneAction, MegaphoneParam, MeteringMode, Meters, MetersPerSecond, MissionConfig, OrientedPhotoMode, OrientedShootAction, OrientedShootParam, Overlap, PanoShotAction, PanoShotParam, PanoShotSubMode, PayloadEnum, PayloadInfo, PayloadParam, type PlanOptions, PlannerError, PositioningType, RecordPointCloudAction, RecordPointCloudOperate, RecordPointCloudParam, RotateYawAction, RotateYawParam, SearchlightAction, SearchlightParam, SetFocusTypeAction, SetFocusTypeParam, ShootType, StartActionGroup, StartRecordAction, StartRecordParam, StartSmartObliqueAction, StartSmartObliqueParam, StartTimeLapseAction, StartTimeLapseParam, StopRecordAction, StopRecordParam, StopSmartObliqueAction, StopSmartObliqueParam, StopTimeLapseAction, StopTimeLapseParam, TakePhotoAction, TakePhotoParam, Template, TemplateHeightMode, TemplatePolygon, TemplateType, TemplateWaypointPlacemark, WaylineCoordinateSysParam, Waylines, WaylinesFolder, WaylinesPlacemark, WaypointFolder, WaypointHeadingMode, WaypointHeadingParam, WaypointHeadingPathMode, WaypointTemplate, WaypointTurnMode, WaypointTurnParam, ZoomAction, ZoomParam, degrees, haversineMeters, meters, metersPerSecond, plan, validate };
|
|
946
|
+
export { AccurateShootAction, AccurateShootParam, Action, ActionActuatorFunc, ActionGroup, ActionGroupMode, ActionTrigger, ActionTriggerType, AircraftPathMode, AutoRerouteInfo, CoordinateMode, CustomDirNameAction, CustomDirNameParam, Degrees, DroneEnum, DroneInfo, ExecuteHeightMode, ExecuteRCLostAction, ExitOnRCLost, FinishAction, FlyToWaylineMode, FocusAction, FocusMode, FocusParam, GimbalAngleLockAction, GimbalAngleLockParam, GimbalAngleUnlockAction, GimbalEvenlyRotateAction, GimbalEvenlyRotateParam, GimbalHeadingYawBase, GimbalPitchMode, GimbalRotateAction, GimbalRotateMode, GimbalRotateParam, HoverAction, HoverParam, Issue, KmzError, LensType, LidarReturnMode, LidarScanningMode, LngLat, LngLatHeight, Mapping2dFolder, Mapping2dPlacemark, Mapping2dTemplate, Mapping3dFolder, Mapping3dPlacemark, Mapping3dTemplate, MappingHeadingMode, MappingHeadingParam, MappingStripFolder, MappingStripPlacemark, MappingStripTemplate, MegaphoneAction, MegaphoneParam, MeteringMode, Meters, MetersPerSecond, MissionConfig, OrientedPhotoMode, OrientedShootAction, OrientedShootParam, Overlap, PanoShotAction, PanoShotParam, PanoShotSubMode, PayloadEnum, PayloadInfo, PayloadParam, type PlanOptions, PlannerError, PositioningType, RecordPointCloudAction, RecordPointCloudOperate, RecordPointCloudParam, RotateYawAction, RotateYawParam, SearchlightAction, SearchlightParam, SetFocusTypeAction, SetFocusTypeParam, ShootType, SmartObliquePoint, StartActionGroup, StartRecordAction, StartRecordParam, StartSmartObliqueAction, StartSmartObliqueParam, StartTimeLapseAction, StartTimeLapseParam, StopRecordAction, StopRecordParam, StopSmartObliqueAction, StopSmartObliqueParam, StopTimeLapseAction, StopTimeLapseParam, TakePhotoAction, TakePhotoParam, Template, TemplateHeightMode, TemplatePolygon, TemplateType, TemplateWaypointPlacemark, WaylineCoordinateSysParam, Waylines, WaylinesFolder, WaylinesPlacemark, WaypointFolder, WaypointHeadingMode, WaypointHeadingParam, WaypointHeadingPathMode, WaypointTemplate, WaypointTurnMode, WaypointTurnParam, ZoomAction, ZoomParam, degrees, haversineMeters, meters, metersPerSecond, plan, validate };
|
package/dist/index.mjs
CHANGED
|
@@ -734,9 +734,11 @@ function emitOrtho(folder, origin, enuWps, shootInterval, options) {
|
|
|
734
734
|
* Single-pass smart-oblique / quick-ortho (smartObliqueEnable=true):
|
|
735
735
|
* The aircraft flies one grid while the gimbal swings (startSmartOblique/stopSmartOblique).
|
|
736
736
|
*/
|
|
737
|
-
function emitSmartOblique(folder, origin, enuWps, options) {
|
|
737
|
+
function emitSmartOblique(folder, origin, enuWps, shootInterval, options) {
|
|
738
738
|
const lastIndex = enuWps.length - 1;
|
|
739
739
|
const pitch = folder.Placemark.smartObliqueGimbalPitch ?? degrees(-45);
|
|
740
|
+
const smartObliquePoints = buildSmartObliquePoints(Number(pitch), shootInterval);
|
|
741
|
+
let nextGroupId = 0;
|
|
740
742
|
const placemarks = enuWps.map((enu, index) => {
|
|
741
743
|
const coord = enuToLngLat(enu, origin);
|
|
742
744
|
const isLast = index === lastIndex;
|
|
@@ -753,30 +755,8 @@ function emitSmartOblique(folder, origin, enuWps, options) {
|
|
|
753
755
|
},
|
|
754
756
|
...COMMON_WP_EXTRAS
|
|
755
757
|
};
|
|
756
|
-
if (index === 0) placemark.actionGroup = [
|
|
757
|
-
|
|
758
|
-
actionGroupStartIndex: 0,
|
|
759
|
-
actionGroupEndIndex: lastIndex,
|
|
760
|
-
actionGroupMode: "sequence",
|
|
761
|
-
actionTrigger: { actionTriggerType: "reachPoint" },
|
|
762
|
-
action: [{
|
|
763
|
-
actionId: 0,
|
|
764
|
-
actionActuatorFunc: "startSmartOblique",
|
|
765
|
-
actionActuatorFuncParam: { payloadPositionIndex: 0 }
|
|
766
|
-
}]
|
|
767
|
-
}];
|
|
768
|
-
else if (isLast) placemark.actionGroup = [{
|
|
769
|
-
actionGroupId: 1,
|
|
770
|
-
actionGroupStartIndex: lastIndex,
|
|
771
|
-
actionGroupEndIndex: lastIndex,
|
|
772
|
-
actionGroupMode: "sequence",
|
|
773
|
-
actionTrigger: { actionTriggerType: "reachPoint" },
|
|
774
|
-
action: [{
|
|
775
|
-
actionId: 0,
|
|
776
|
-
actionActuatorFunc: "stopSmartOblique",
|
|
777
|
-
actionActuatorFuncParam: { payloadPositionIndex: 0 }
|
|
778
|
-
}]
|
|
779
|
-
}];
|
|
758
|
+
if (index % 2 === 0 && index + 1 <= lastIndex) placemark.actionGroup = [smartObliqueStartGroup(index, index + 1, nextGroupId++, smartObliquePoints)];
|
|
759
|
+
else if (index % 2 === 1) placemark.actionGroup = [smartObliqueStopGroup(index, nextGroupId++)];
|
|
780
760
|
return placemark;
|
|
781
761
|
});
|
|
782
762
|
return {
|
|
@@ -788,6 +768,65 @@ function emitSmartOblique(folder, origin, enuWps, options) {
|
|
|
788
768
|
Placemark: placemarks
|
|
789
769
|
};
|
|
790
770
|
}
|
|
771
|
+
function smartObliqueStartGroup(startIndex, endIndex, groupId, smartObliquePoints) {
|
|
772
|
+
return {
|
|
773
|
+
actionGroupId: groupId,
|
|
774
|
+
actionGroupStartIndex: startIndex,
|
|
775
|
+
actionGroupEndIndex: endIndex,
|
|
776
|
+
actionGroupMode: "sequence",
|
|
777
|
+
actionTrigger: { actionTriggerType: "betweenAdjacentPoints" },
|
|
778
|
+
action: [{
|
|
779
|
+
actionId: 0,
|
|
780
|
+
actionActuatorFunc: "gimbalAngleLock",
|
|
781
|
+
actionActuatorFuncParam: { payloadPositionIndex: 0 }
|
|
782
|
+
}, {
|
|
783
|
+
actionId: 1,
|
|
784
|
+
actionActuatorFunc: "startSmartOblique",
|
|
785
|
+
actionActuatorFuncParam: {
|
|
786
|
+
smartObliqueCycleMode: "unlimited",
|
|
787
|
+
payloadPositionIndex: 0,
|
|
788
|
+
smartObliquePoint: smartObliquePoints
|
|
789
|
+
}
|
|
790
|
+
}]
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
function smartObliqueStopGroup(index, groupId) {
|
|
794
|
+
return {
|
|
795
|
+
actionGroupId: groupId,
|
|
796
|
+
actionGroupStartIndex: index,
|
|
797
|
+
actionGroupEndIndex: index,
|
|
798
|
+
actionGroupMode: "sequence",
|
|
799
|
+
actionTrigger: { actionTriggerType: "reachPoint" },
|
|
800
|
+
action: [{
|
|
801
|
+
actionId: 0,
|
|
802
|
+
actionActuatorFunc: "stopSmartOblique",
|
|
803
|
+
actionActuatorFuncParam: { payloadPositionIndex: 0 }
|
|
804
|
+
}, {
|
|
805
|
+
actionId: 1,
|
|
806
|
+
actionActuatorFunc: "gimbalAngleUnlock"
|
|
807
|
+
}]
|
|
808
|
+
};
|
|
809
|
+
}
|
|
810
|
+
function buildSmartObliquePoints(pitchDeg, shootInterval) {
|
|
811
|
+
const stayTime = Math.max(.1, shootInterval);
|
|
812
|
+
const obliquePitch = -Math.abs(pitchDeg || 45);
|
|
813
|
+
return [
|
|
814
|
+
smartObliquePoint(-90, 0, 0),
|
|
815
|
+
smartObliquePoint(obliquePitch, 45, stayTime),
|
|
816
|
+
smartObliquePoint(obliquePitch, -45, stayTime),
|
|
817
|
+
smartObliquePoint(-120, 50, stayTime),
|
|
818
|
+
smartObliquePoint(-120, -50, stayTime)
|
|
819
|
+
];
|
|
820
|
+
}
|
|
821
|
+
function smartObliquePoint(pitchDeg, yawDeg, stayTime) {
|
|
822
|
+
return {
|
|
823
|
+
smartObliqueRunningTime: 0,
|
|
824
|
+
smartObliqueStayTime: stayTime,
|
|
825
|
+
smartObliqueEulerPitch: degrees(pitchDeg),
|
|
826
|
+
smartObliqueEulerRoll: degrees(0),
|
|
827
|
+
smartObliqueEulerYaw: degrees(yawDeg)
|
|
828
|
+
};
|
|
829
|
+
}
|
|
791
830
|
function planMapping2d(template, options) {
|
|
792
831
|
if (options?.lineSpacing == null) throw new PlannerError("mapping2d requires options.lineSpacing — calculate 2·h·tan(HFOV/2)·(1−sideOverlap) in the demo and pass it in.");
|
|
793
832
|
const standoff = template.Folder.Placemark.smartObliqueEnable === true ? smartObliqueStandoff(template.Folder) : 0;
|
|
@@ -795,7 +834,7 @@ function planMapping2d(template, options) {
|
|
|
795
834
|
const shootInterval = options.shootInterval ?? 2;
|
|
796
835
|
if (folder.Placemark.smartObliqueEnable === true) return {
|
|
797
836
|
missionConfig: template.missionConfig,
|
|
798
|
-
Folder: [emitSmartOblique(folder, origin, enuWps, options)]
|
|
837
|
+
Folder: [emitSmartOblique(folder, origin, enuWps, shootInterval, options)]
|
|
799
838
|
};
|
|
800
839
|
return {
|
|
801
840
|
missionConfig: template.missionConfig,
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { degrees, meters, type Meters } from "../types/branded.js";
|
|
2
|
-
import type { Action } from "../types/action.js";
|
|
2
|
+
import type { Action, SmartObliquePoint } from "../types/action.js";
|
|
3
3
|
import type { ExecuteHeightMode } from "../types/enums.js";
|
|
4
4
|
import type { Mapping2dFolder, Mapping2dTemplate } from "../types/template.js";
|
|
5
5
|
import type { StartActionGroup } from "../types/waylines.js";
|
|
@@ -466,10 +466,13 @@ function emitSmartOblique(
|
|
|
466
466
|
folder: Mapping2dFolder,
|
|
467
467
|
origin: { lng: number; lat: number },
|
|
468
468
|
enuWps: readonly EnuPoint[],
|
|
469
|
+
shootInterval: number,
|
|
469
470
|
options?: PlanOptions,
|
|
470
471
|
): WaylinesFolder {
|
|
471
472
|
const lastIndex = enuWps.length - 1;
|
|
472
473
|
const pitch = folder.Placemark.smartObliqueGimbalPitch ?? degrees(-45);
|
|
474
|
+
const smartObliquePoints = buildSmartObliquePoints(Number(pitch), shootInterval);
|
|
475
|
+
let nextGroupId = 0;
|
|
473
476
|
|
|
474
477
|
const placemarks: WaylinesPlacemark[] = enuWps.map((enu, index) => {
|
|
475
478
|
const coord = enuToLngLat(enu, origin);
|
|
@@ -490,40 +493,12 @@ function emitSmartOblique(
|
|
|
490
493
|
},
|
|
491
494
|
...COMMON_WP_EXTRAS,
|
|
492
495
|
};
|
|
493
|
-
if (index === 0) {
|
|
494
|
-
placemark.actionGroup = [
|
|
495
|
-
{
|
|
496
|
-
actionGroupId: 0,
|
|
497
|
-
actionGroupStartIndex: 0,
|
|
498
|
-
actionGroupEndIndex: lastIndex,
|
|
499
|
-
actionGroupMode: "sequence",
|
|
500
|
-
actionTrigger: { actionTriggerType: "reachPoint" },
|
|
501
|
-
action: [
|
|
502
|
-
{
|
|
503
|
-
actionId: 0,
|
|
504
|
-
actionActuatorFunc: "startSmartOblique",
|
|
505
|
-
actionActuatorFuncParam: { payloadPositionIndex: 0 },
|
|
506
|
-
},
|
|
507
|
-
],
|
|
508
|
-
},
|
|
509
|
-
];
|
|
510
|
-
} else if (isLast) {
|
|
496
|
+
if (index % 2 === 0 && index + 1 <= lastIndex) {
|
|
511
497
|
placemark.actionGroup = [
|
|
512
|
-
|
|
513
|
-
actionGroupId: 1,
|
|
514
|
-
actionGroupStartIndex: lastIndex,
|
|
515
|
-
actionGroupEndIndex: lastIndex,
|
|
516
|
-
actionGroupMode: "sequence",
|
|
517
|
-
actionTrigger: { actionTriggerType: "reachPoint" },
|
|
518
|
-
action: [
|
|
519
|
-
{
|
|
520
|
-
actionId: 0,
|
|
521
|
-
actionActuatorFunc: "stopSmartOblique",
|
|
522
|
-
actionActuatorFuncParam: { payloadPositionIndex: 0 },
|
|
523
|
-
},
|
|
524
|
-
],
|
|
525
|
-
},
|
|
498
|
+
smartObliqueStartGroup(index, index + 1, nextGroupId++, smartObliquePoints),
|
|
526
499
|
];
|
|
500
|
+
} else if (index % 2 === 1) {
|
|
501
|
+
placemark.actionGroup = [smartObliqueStopGroup(index, nextGroupId++)];
|
|
527
502
|
}
|
|
528
503
|
return placemark;
|
|
529
504
|
});
|
|
@@ -538,6 +513,78 @@ function emitSmartOblique(
|
|
|
538
513
|
};
|
|
539
514
|
}
|
|
540
515
|
|
|
516
|
+
function smartObliqueStartGroup(
|
|
517
|
+
startIndex: number,
|
|
518
|
+
endIndex: number,
|
|
519
|
+
groupId: number,
|
|
520
|
+
smartObliquePoints: SmartObliquePoint[],
|
|
521
|
+
) {
|
|
522
|
+
return {
|
|
523
|
+
actionGroupId: groupId,
|
|
524
|
+
actionGroupStartIndex: startIndex,
|
|
525
|
+
actionGroupEndIndex: endIndex,
|
|
526
|
+
actionGroupMode: "sequence" as const,
|
|
527
|
+
actionTrigger: { actionTriggerType: "betweenAdjacentPoints" as const },
|
|
528
|
+
action: [
|
|
529
|
+
{
|
|
530
|
+
actionId: 0,
|
|
531
|
+
actionActuatorFunc: "gimbalAngleLock" as const,
|
|
532
|
+
actionActuatorFuncParam: { payloadPositionIndex: 0 },
|
|
533
|
+
},
|
|
534
|
+
{
|
|
535
|
+
actionId: 1,
|
|
536
|
+
actionActuatorFunc: "startSmartOblique" as const,
|
|
537
|
+
actionActuatorFuncParam: {
|
|
538
|
+
smartObliqueCycleMode: "unlimited" as const,
|
|
539
|
+
payloadPositionIndex: 0,
|
|
540
|
+
smartObliquePoint: smartObliquePoints,
|
|
541
|
+
},
|
|
542
|
+
},
|
|
543
|
+
],
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
function smartObliqueStopGroup(index: number, groupId: number) {
|
|
548
|
+
return {
|
|
549
|
+
actionGroupId: groupId,
|
|
550
|
+
actionGroupStartIndex: index,
|
|
551
|
+
actionGroupEndIndex: index,
|
|
552
|
+
actionGroupMode: "sequence" as const,
|
|
553
|
+
actionTrigger: { actionTriggerType: "reachPoint" as const },
|
|
554
|
+
action: [
|
|
555
|
+
{
|
|
556
|
+
actionId: 0,
|
|
557
|
+
actionActuatorFunc: "stopSmartOblique" as const,
|
|
558
|
+
actionActuatorFuncParam: { payloadPositionIndex: 0 },
|
|
559
|
+
},
|
|
560
|
+
{ actionId: 1, actionActuatorFunc: "gimbalAngleUnlock" as const },
|
|
561
|
+
],
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
function buildSmartObliquePoints(pitchDeg: number, shootInterval: number): SmartObliquePoint[] {
|
|
566
|
+
const stayTime = Math.max(0.1, shootInterval);
|
|
567
|
+
const obliquePitch = -Math.abs(pitchDeg || 45);
|
|
568
|
+
|
|
569
|
+
return [
|
|
570
|
+
smartObliquePoint(-90, 0, 0),
|
|
571
|
+
smartObliquePoint(obliquePitch, 45, stayTime),
|
|
572
|
+
smartObliquePoint(obliquePitch, -45, stayTime),
|
|
573
|
+
smartObliquePoint(-120, 50, stayTime),
|
|
574
|
+
smartObliquePoint(-120, -50, stayTime),
|
|
575
|
+
];
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
function smartObliquePoint(pitchDeg: number, yawDeg: number, stayTime: number): SmartObliquePoint {
|
|
579
|
+
return {
|
|
580
|
+
smartObliqueRunningTime: 0,
|
|
581
|
+
smartObliqueStayTime: stayTime,
|
|
582
|
+
smartObliqueEulerPitch: degrees(pitchDeg),
|
|
583
|
+
smartObliqueEulerRoll: degrees(0),
|
|
584
|
+
smartObliqueEulerYaw: degrees(yawDeg),
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
|
|
541
588
|
export function planMapping2d(template: Mapping2dTemplate, options?: PlanOptions): Waylines {
|
|
542
589
|
if (options?.lineSpacing == null) {
|
|
543
590
|
throw new PlannerError(
|
|
@@ -556,7 +603,7 @@ export function planMapping2d(template: Mapping2dTemplate, options?: PlanOptions
|
|
|
556
603
|
if (folder.Placemark.smartObliqueEnable === true) {
|
|
557
604
|
return {
|
|
558
605
|
missionConfig: template.missionConfig,
|
|
559
|
-
Folder: [emitSmartOblique(folder, origin, enuWps, options)],
|
|
606
|
+
Folder: [emitSmartOblique(folder, origin, enuWps, shootInterval, options)],
|
|
560
607
|
};
|
|
561
608
|
}
|
|
562
609
|
// 正射 and 正射+智能摆拍 share the nadir grid; the centroid detour is driven by
|
package/src/plan/plan.test.ts
CHANGED
|
@@ -370,8 +370,40 @@ describe("plan", () => {
|
|
|
370
370
|
const funcs = wl.Folder[0]!.Placemark.flatMap((p) =>
|
|
371
371
|
(p.actionGroup ?? []).flatMap((g) => g.action.map((a) => a.actionActuatorFunc)),
|
|
372
372
|
);
|
|
373
|
+
const scanlineCount = Math.floor(wl.Folder[0]!.Placemark.length / 2);
|
|
374
|
+
expect(funcs.filter((fn) => fn === "startSmartOblique")).toHaveLength(scanlineCount);
|
|
375
|
+
expect(funcs.filter((fn) => fn === "stopSmartOblique")).toHaveLength(scanlineCount);
|
|
376
|
+
expect(funcs).toContain("gimbalAngleLock");
|
|
373
377
|
expect(funcs).toContain("startSmartOblique");
|
|
374
378
|
expect(funcs).toContain("stopSmartOblique");
|
|
379
|
+
expect(funcs).toContain("gimbalAngleUnlock");
|
|
380
|
+
|
|
381
|
+
const groups = wl.Folder[0]!.Placemark.flatMap((p) => p.actionGroup ?? []);
|
|
382
|
+
const firstStartGroup = groups.find((g) =>
|
|
383
|
+
g.action.some((a) => a.actionActuatorFunc === "startSmartOblique"),
|
|
384
|
+
)!;
|
|
385
|
+
expect(firstStartGroup.actionGroupStartIndex).toBe(0);
|
|
386
|
+
expect(firstStartGroup.actionGroupEndIndex).toBe(1);
|
|
387
|
+
expect(firstStartGroup.actionTrigger.actionTriggerType).toBe("betweenAdjacentPoints");
|
|
388
|
+
|
|
389
|
+
const firstStopGroup = groups.find((g) =>
|
|
390
|
+
g.action.some((a) => a.actionActuatorFunc === "stopSmartOblique"),
|
|
391
|
+
)!;
|
|
392
|
+
expect(firstStopGroup.actionGroupStartIndex).toBe(1);
|
|
393
|
+
expect(firstStopGroup.actionGroupEndIndex).toBe(1);
|
|
394
|
+
expect(firstStopGroup.actionTrigger.actionTriggerType).toBe("reachPoint");
|
|
395
|
+
|
|
396
|
+
const start = firstStartGroup.action.find((a) => a.actionActuatorFunc === "startSmartOblique");
|
|
397
|
+
expect(start?.actionActuatorFunc).toBe("startSmartOblique");
|
|
398
|
+
if (start?.actionActuatorFunc === "startSmartOblique") {
|
|
399
|
+
expect(start.actionActuatorFuncParam.smartObliqueCycleMode).toBe("unlimited");
|
|
400
|
+
expect(start.actionActuatorFuncParam.smartObliquePoint).toHaveLength(5);
|
|
401
|
+
expect(
|
|
402
|
+
start.actionActuatorFuncParam.smartObliquePoint?.map((p) =>
|
|
403
|
+
Number(p.smartObliqueEulerPitch),
|
|
404
|
+
),
|
|
405
|
+
).toEqual([-90, -45, -45, -120, -120]);
|
|
406
|
+
}
|
|
375
407
|
|
|
376
408
|
// The camera swings omnidirectionally, so the polygon is dilated by the
|
|
377
409
|
// stand-off and the grid overflies the boundary on EVERY side. Polygon spans
|
package/src/types/action.ts
CHANGED
|
@@ -253,8 +253,21 @@ export interface MegaphoneParam {
|
|
|
253
253
|
export type MegaphoneAction = ActionBase<"megaphone", MegaphoneParam>;
|
|
254
254
|
|
|
255
255
|
/** M4D/M4TD only — starts gimbal swing in smart-oblique mode. */
|
|
256
|
+
export interface SmartObliquePoint {
|
|
257
|
+
/** seconds before this pose starts; DJI exports currently use 0. */
|
|
258
|
+
smartObliqueRunningTime: number;
|
|
259
|
+
/** seconds to hold this pose for smart-oblique shooting. */
|
|
260
|
+
smartObliqueStayTime: number;
|
|
261
|
+
smartObliqueEulerPitch: Degrees;
|
|
262
|
+
smartObliqueEulerRoll: Degrees;
|
|
263
|
+
smartObliqueEulerYaw: Degrees;
|
|
264
|
+
}
|
|
265
|
+
|
|
256
266
|
export interface StartSmartObliqueParam {
|
|
267
|
+
/** Observed in DJI exports; older generated files may omit it. */
|
|
268
|
+
smartObliqueCycleMode?: "unlimited";
|
|
257
269
|
payloadPositionIndex: number;
|
|
270
|
+
smartObliquePoint?: SmartObliquePoint[];
|
|
258
271
|
}
|
|
259
272
|
export type StartSmartObliqueAction = ActionBase<"startSmartOblique", StartSmartObliqueParam>;
|
|
260
273
|
|