@@ -183,31 +183,132 @@ export class PoseRenderer {
183183 }
184184 }
185185
186- // Keypoints only mode
186+ // Keypoints only mode — large colored dots with labels, no skeleton lines
187187 renderKeypointsMode ( poseData , metadata ) {
188188 const persons = poseData . persons || [ ] ;
189-
189+
190190 persons . forEach ( ( person , index ) => {
191191 if ( person . confidence >= this . config . confidenceThreshold && person . keypoints ) {
192192 this . renderKeypoints ( person . keypoints , person . confidence , true ) ;
193+
194+ // Render bounding box
195+ if ( this . config . showBoundingBox && person . bbox ) {
196+ this . renderBoundingBox ( person . bbox , person . confidence , index ) ;
197+ }
198+ if ( this . config . showConfidence ) {
199+ this . renderConfidenceScore ( person , index ) ;
200+ }
193201 }
194202 } ) ;
203+
204+ if ( this . config . showZones && poseData . zone_summary ) {
205+ this . renderZones ( poseData . zone_summary ) ;
206+ }
195207 }
196208
197- // Heatmap rendering mode
209+ // Heatmap rendering mode — Gaussian blobs around each keypoint
198210 renderHeatmapMode ( poseData , metadata ) {
199- // This would render a heatmap visualization
200- // For now, fall back to skeleton mode
201- this . logger . debug ( 'Heatmap mode not fully implemented, using skeleton mode' ) ;
202- this . renderSkeletonMode ( poseData , metadata ) ;
211+ const persons = poseData . persons || [ ] ;
212+
213+ persons . forEach ( ( person , personIdx ) => {
214+ if ( person . confidence < this . config . confidenceThreshold || ! person . keypoints ) return ;
215+
216+ const hue = ( personIdx * 60 ) % 360 ; // different hue per person
217+
218+ person . keypoints . forEach ( ( kp ) => {
219+ if ( kp . confidence <= this . config . keypointConfidenceThreshold ) return ;
220+
221+ const cx = this . scaleX ( kp . x ) ;
222+ const cy = this . scaleY ( kp . y ) ;
223+ const radius = 30 + kp . confidence * 20 ;
224+
225+ const grad = this . ctx . createRadialGradient ( cx , cy , 0 , cx , cy , radius ) ;
226+ grad . addColorStop ( 0 , `hsla(${ hue } , 100%, 55%, ${ kp . confidence * 0.7 } )` ) ;
227+ grad . addColorStop ( 0.5 , `hsla(${ hue } , 100%, 45%, ${ kp . confidence * 0.3 } )` ) ;
228+ grad . addColorStop ( 1 , `hsla(${ hue } , 100%, 40%, 0)` ) ;
229+
230+ this . ctx . fillStyle = grad ;
231+ this . ctx . fillRect ( cx - radius , cy - radius , radius * 2 , radius * 2 ) ;
232+ } ) ;
233+
234+ // Light skeleton overlay so joints are connected
235+ if ( person . keypoints ) {
236+ this . ctx . globalAlpha = 0.25 ;
237+ this . renderSkeleton ( person . keypoints , person . confidence ) ;
238+ this . ctx . globalAlpha = 1.0 ;
239+ }
240+
241+ if ( this . config . showConfidence ) {
242+ this . renderConfidenceScore ( person , personIdx ) ;
243+ }
244+ } ) ;
245+
246+ if ( this . config . showZones && poseData . zone_summary ) {
247+ this . renderZones ( poseData . zone_summary ) ;
248+ }
203249 }
204250
205- // Dense pose rendering mode
251+ // Dense pose rendering mode — body region segmentation with filled polygons
206252 renderDenseMode ( poseData , metadata ) {
207- // This would render dense pose segmentation
208- // For now, fall back to skeleton mode
209- this . logger . debug ( 'Dense mode not fully implemented, using skeleton mode' ) ;
210- this . renderSkeletonMode ( poseData , metadata ) ;
253+ const persons = poseData . persons || [ ] ;
254+
255+ // Body part groups: [start_kp, end_kp, color]
256+ const bodyParts = [
257+ { name : 'head' , kps : [ 0 , 1 , 2 , 3 , 4 ] , color : 'rgba(255, 100, 100, 0.4)' } ,
258+ { name : 'torso' , kps : [ 5 , 6 , 12 , 11 ] , color : 'rgba(100, 200, 255, 0.4)' } ,
259+ { name : 'left_arm' , kps : [ 5 , 7 , 9 ] , color : 'rgba(100, 255, 150, 0.4)' } ,
260+ { name : 'right_arm' , kps : [ 6 , 8 , 10 ] , color : 'rgba(255, 200, 100, 0.4)' } ,
261+ { name : 'left_leg' , kps : [ 11 , 13 , 15 ] , color : 'rgba(200, 100, 255, 0.4)' } ,
262+ { name : 'right_leg' , kps : [ 12 , 14 , 16 ] , color : 'rgba(255, 255, 100, 0.4)' } ,
263+ ] ;
264+
265+ persons . forEach ( ( person , personIdx ) => {
266+ if ( person . confidence < this . config . confidenceThreshold || ! person . keypoints ) return ;
267+
268+ const kps = person . keypoints ;
269+
270+ bodyParts . forEach ( ( part ) => {
271+ // Collect valid keypoints for this body part
272+ const points = part . kps
273+ . filter ( i => kps [ i ] && kps [ i ] . confidence > this . config . keypointConfidenceThreshold )
274+ . map ( i => ( { x : this . scaleX ( kps [ i ] . x ) , y : this . scaleY ( kps [ i ] . y ) } ) ) ;
275+
276+ if ( points . length < 2 ) return ;
277+
278+ // Draw filled region with padding around joints
279+ this . ctx . fillStyle = part . color ;
280+ this . ctx . strokeStyle = part . color . replace ( '0.4' , '0.7' ) ;
281+ this . ctx . lineWidth = 8 ;
282+ this . ctx . lineJoin = 'round' ;
283+ this . ctx . lineCap = 'round' ;
284+
285+ // Draw thick path as a "region"
286+ this . ctx . beginPath ( ) ;
287+ this . ctx . moveTo ( points [ 0 ] . x , points [ 0 ] . y ) ;
288+ for ( let i = 1 ; i < points . length ; i ++ ) {
289+ this . ctx . lineTo ( points [ i ] . x , points [ i ] . y ) ;
290+ }
291+ this . ctx . stroke ( ) ;
292+
293+ // Draw circles at each joint to widen the region
294+ points . forEach ( p => {
295+ this . ctx . beginPath ( ) ;
296+ this . ctx . arc ( p . x , p . y , 10 , 0 , Math . PI * 2 ) ;
297+ this . ctx . fill ( ) ;
298+ } ) ;
299+ } ) ;
300+
301+ // Subtle keypoint dots on top
302+ this . renderKeypoints ( kps , person . confidence , false ) ;
303+
304+ if ( this . config . showConfidence ) {
305+ this . renderConfidenceScore ( person , personIdx ) ;
306+ }
307+ } ) ;
308+
309+ if ( this . config . showZones && poseData . zone_summary ) {
310+ this . renderZones ( poseData . zone_summary ) ;
311+ }
211312 }
212313
213314 // Render skeleton connections
0 commit comments