9090 }));
9191 }
9292
93+ function getRobotDisplayXY(xy : BasePoint , headingDeg : number ): BasePoint {
94+ const headingRad = (headingDeg * Math .PI ) / 180 ;
95+ const offsetX = x (settings .rOffsetX ) - x (0 );
96+ const offsetY = y (settings .rOffsetY ) - y (0 );
97+ const offsetXRot =
98+ offsetX * Math .cos (headingRad ) - offsetY * Math .sin (headingRad );
99+ const offsetYRot =
100+ offsetX * Math .sin (headingRad ) + offsetY * Math .cos (headingRad );
101+
102+ return {
103+ x: xy .x + offsetXRot ,
104+ y: xy .y + offsetYRot ,
105+ };
106+ }
107+
93108 // Canvas state
94109 let two: Two ;
95110 let twoElement: HTMLDivElement ;
10381053 settings .rWidth ,
10391054 settings .rHeight ,
10401055 50 ,
1056+ settings .rOffsetX ,
1057+ settings .rOffsetY ,
10411058 );
10421059
10431060 if (ghostPoints .length >= 3 ) {
11121129 settings .rWidth ,
11131130 settings .rHeight ,
11141131 50 ,
1132+ settings .rOffsetX ,
1133+ settings .rOffsetY ,
11151134 );
11161135
11171136 if (ghostPoints .length >= 3 ) {
11841203 settings .rWidth ,
11851204 settings .rHeight ,
11861205 50 ,
1206+ settings .rOffsetX ,
1207+ settings .rOffsetY ,
11871208 );
11881209
11891210 if (ghostPoints .length >= 3 ) {
12571278 settings .rWidth ,
12581279 settings .rHeight ,
12591280 spacing ,
1281+ settings .rOffsetX ,
1282+ settings .rOffsetY ,
12601283 );
12611284
12621285 // If user requested onion layers only for the next point, filter to the relevant line
13691392 settings .rWidth ,
13701393 settings .rHeight ,
13711394 spacing ,
1395+ settings .rOffsetX ,
1396+ settings .rOffsetY ,
13721397 );
13731398
13741399 // If user requested onion layers only for the next point, filter to the relevant line
16401665 ctx .globalAlpha = opacity ;
16411666 ctx .translate (xy .x * scale , xy .y * scale );
16421667 ctx .rotate ((headingDeg * Math .PI ) / 180 );
1668+ // Apply robot center offset (rotated with the robot)
1669+ ctx .translate (
1670+ settings .rOffsetX * scale ,
1671+ settings .rOffsetY * scale
1672+ );
16431673 ctx .drawImage (
16441674 robotImage ,
16451675 (- robotPixelWidth * scale ) / 2 ,
30943124 <MathTools {x } {y } {twoElement } {robotXY } />
30953125 <!-- Main robot: only show in normal mode -->
30963126 {#if $activePaths .length === 0 }
3127+ {@const mainRobotDisplayXY = getRobotDisplayXY (robotXY , robotHeading )}
30973128 <img
30983129 src ={settings .robotImage || " /robot.png" }
30993130 alt =" Robot"
3100- style ={` position: absolute; top: ${robotXY .y }px;
3101- left: ${robotXY .x }px; transform: translate(-50%, -50%) rotate(${robotHeading }deg); z-index: 20; width: ${x (robotWidth )}px; height: ${x (robotHeight )}px;user-select: none; -webkit-user-select: none; -moz-user-select: none;-ms-user-select: none;
3131+ style ={` position: absolute; top: ${mainRobotDisplayXY .y }px;
3132+ left: ${mainRobotDisplayXY .x }px; transform: translate(-50%, -50%) rotate(${robotHeading }deg); z-index: 20; width: ${x (robotWidth )}px; height: ${x (robotHeight )}px;user-select: none; -webkit-user-select: none; -moz-user-select: none;-ms-user-select: none;
31023133pointer-events: none; ` }
31033134 draggable =" false"
31043135 on:error ={(e ) => {
@@ -3111,7 +3142,7 @@ pointer-events: none;`}
31113142 <!-- Heading arrow for main robot -->
31123143 {#if settings .showHeadingArrow }
31133144 <svg
3114- style ={` position: absolute; top: ${robotXY .y }px; left: ${robotXY .x }px; z-index: 21; pointer-events: none; overflow: visible; ` }
3145+ style ={` position: absolute; top: ${mainRobotDisplayXY .y }px; left: ${mainRobotDisplayXY .x }px; z-index: 21; pointer-events: none; overflow: visible; ` }
31153146 width =" 1"
31163147 height =" 1"
31173148 >
@@ -3144,11 +3175,12 @@ pointer-events: none;`}
31443175 {/if }
31453176 <!-- Second robot: only show in dual path mode (not multi-path mode) -->
31463177 {#if $activePaths .length === 0 && $dualPathMode }
3178+ {@const secondRobotDisplayXY = getRobotDisplayXY (secondRobotXY , secondRobotHeading )}
31473179 <img
31483180 src ={settings .robotImage || " /robot.png" }
31493181 alt =" Robot 2"
3150- style ={` position: absolute; top: ${secondRobotXY .y }px;
3151- left: ${secondRobotXY .x }px; transform: translate(-50%, -50%) rotate(${secondRobotHeading }deg); z-index: 19; width: ${x (robotWidth )}px; height: ${x (robotHeight )}px;user-select: none; -webkit-user-select: none; -moz-user-select: none;-ms-user-select: none;
3182+ style ={` position: absolute; top: ${secondRobotDisplayXY .y }px;
3183+ left: ${secondRobotDisplayXY .x }px; transform: translate(-50%, -50%) rotate(${secondRobotHeading }deg); z-index: 19; width: ${x (robotWidth )}px; height: ${x (robotHeight )}px;user-select: none; -webkit-user-select: none; -moz-user-select: none;-ms-user-select: none;
31523184pointer-events: none; opacity: 0.8; ` }
31533185 draggable =" false"
31543186 on:error ={(e ) => {
@@ -3161,7 +3193,7 @@ pointer-events: none; opacity: 0.8;`}
31613193 <!-- Heading arrow for second robot -->
31623194 {#if settings .showHeadingArrow }
31633195 <svg
3164- style ={` position: absolute; top: ${secondRobotXY .y }px; left: ${secondRobotXY .x }px; z-index: 19; pointer-events: none; overflow: visible; opacity: 0.8; ` }
3196+ style ={` position: absolute; top: ${secondRobotDisplayXY .y }px; left: ${secondRobotDisplayXY .x }px; z-index: 19; pointer-events: none; overflow: visible; opacity: 0.8; ` }
31653197 width =" 1"
31663198 height =" 1"
31673199 >
@@ -3195,11 +3227,12 @@ pointer-events: none; opacity: 0.8;`}
31953227 <!-- Additional robots: only show in multi-path mode -->
31963228 {#if $activePaths .length > 0 }
31973229 {#each additionalRobotStates as robotState , idx }
3230+ {@const displayXY = getRobotDisplayXY (robotState .xy , robotState .heading )}
31983231 <img
31993232 src ={settings .robotImage || " /robot.png" }
32003233 alt ="Robot {idx + 1 }"
3201- style ={` position: absolute; top: ${robotState . xy .y }px;
3202- left: ${robotState . xy .x }px; transform: translate(-50%, -50%) rotate(${robotState .heading }deg); z-index: ${20 - idx }; width: ${x (robotWidth )}px; height: ${x (robotHeight )}px;user-select: none; -webkit-user-select: none; -moz-user-select: none;-ms-user-select: none;
3234+ style ={` position: absolute; top: ${displayXY .y }px;
3235+ left: ${displayXY .x }px; transform: translate(-50%, -50%) rotate(${robotState .heading }deg); z-index: ${20 - idx }; width: ${x (robotWidth )}px; height: ${x (robotHeight )}px;user-select: none; -webkit-user-select: none; -moz-user-select: none;-ms-user-select: none;
32033236pointer-events: none; opacity: ${1.0 - idx * 0.15 }; ` }
32043237 draggable =" false"
32053238 on:error ={(e ) => {
@@ -3212,7 +3245,7 @@ pointer-events: none; opacity: ${1.0 - idx * 0.15};`}
32123245 <!-- Heading arrow for additional robots -->
32133246 {#if settings .showHeadingArrow }
32143247 <svg
3215- style ={` position: absolute; top: ${robotState . xy . y }px; left: ${robotState . xy .x }px; z-index: ${20 - idx }; pointer-events: none; overflow: visible; opacity: ${1.0 - idx * 0.15 }; ` }
3248+ style ={` position: absolute; top: ${displayXY . y }px; left: ${displayXY .x }px; z-index: ${20 - idx }; pointer-events: none; overflow: visible; opacity: ${1.0 - idx * 0.15 }; ` }
32163249 width =" 1"
32173250 height =" 1"
32183251 >
0 commit comments