1- export default function getEllipsePixels ( x0 : number , y0 : number , x1 : number , y1 : number ) {
2- let a = Math . abs ( x1 - x0 ) , b = Math . abs ( y1 - y0 ) , b1 = b & 1 ; /* values of diameter */
3- let dx = 4 * ( 1 - a ) * b * b , dy = 4 * ( b1 + 1 ) * a * a ; /* error increment */
4- let err = dx + dy + b1 * a * a , e2 ; /* error of 1.step */
5-
6- if ( x0 > x1 ) { x0 = x1 ; x1 += a ; } /* if called with swapped points */
7- if ( y0 > y1 ) y0 = y1 ; /* .. exchange them */
8- y0 += ( b + 1 ) / 2 ; y1 = y0 - b1 ; /* starting pixel */
9- a *= 8 * a ; b1 = 8 * b * b ;
1+ export function distance ( x : number , y : number , ratio : number ) : number {
2+ return Math . sqrt ( ( Math . pow ( y * ratio , 2 ) ) + Math . pow ( x , 2 ) ) ;
3+ }
4+
5+ function filled ( x : number , y : number , radius : number , ratio : number ) : boolean {
6+ return distance ( x , y , ratio ) <= radius ;
7+ }
8+
9+ function thinfilled ( x : number , y : number , radius : number , ratio : number ) : boolean {
10+ return filled ( x , y , radius , ratio ) && ! (
11+ filled ( x + 1 , y , radius , ratio ) &&
12+ filled ( x - 1 , y , radius , ratio ) &&
13+ filled ( x , y + 1 , radius , ratio ) &&
14+ filled ( x , y - 1 , radius , ratio )
15+ ) ;
16+ }
17+
18+ function isFilled ( x : number , y : number , width : number , height : number ) : boolean {
19+ const bounds = {
20+ minX : 0 ,
21+ maxX : width ,
22+ minY : 0 ,
23+ maxY : height ,
24+ } ;
25+
26+ x = - .5 * ( bounds . maxX - 2 * ( x + .5 ) ) ;
27+ y = - .5 * ( bounds . maxY - 2 * ( y + .5 ) ) ;
28+
29+ return thinfilled ( x , y , ( bounds . maxX / 2 ) , bounds . maxX / bounds . maxY ) ;
30+ }
31+
32+
33+ function betterCircle ( x0 : number , y0 : number , x1 : number , y1 : number ) {
34+ const width = Math . abs ( x0 - x1 ) ;
35+ const height = Math . abs ( y0 - y1 ) ;
36+ const minX = Math . min ( x0 , x1 ) ;
37+ const minY = Math . min ( y0 , y1 ) ;
38+
1039 const pixels : { x : number , y : number } [ ] = [ ] ;
40+
41+ // Loop through bounding box
42+ for ( let y = 0 ; y < height ; y ++ ) {
43+ for ( let x = 0 ; x < width ; x ++ ) {
44+ // Center coordinates relative to ellipse
45+ const cx = - .5 * ( width - 2 * ( x + 0.5 ) ) ;
46+ const cy = - .5 * ( height - 2 * ( y + 0.5 ) ) ;
47+
48+ // Use filled() instead of thinfilled() to fill the ellipse
49+ if ( filled ( cx , cy , width / 2 , width / height ) ) {
50+ pixels . push ( { x : x + minX , y : y + minY } ) ;
51+ }
52+ }
53+ }
54+
55+ return pixels ;
56+ }
57+
58+
59+ function ellipse ( x0 : number , y0 : number , x1 : number , y1 : number ) {
60+ const pixels : { x : number , y : number } [ ] = [ ] ;
61+
62+ let a = Math . abs ( x1 - x0 ) ,
63+ b = Math . abs ( y1 - y0 ) ,
64+ b1 = b & 1 ;
65+
66+ let dx = 4 * ( 1.0 - a ) * b * b ,
67+ dy = 4 * ( b1 + 1 ) * a * a ;
68+
69+ let err = dx + dy + b1 * a * a ,
70+ e2 ;
71+
72+ if ( x0 > x1 ) {
73+ x0 = x1 ;
74+ x1 += a ;
75+ }
76+ if ( y0 > y1 ) y0 = y1 ;
77+ y0 += ( b + 1 ) >> 1 ;
78+ y1 = y0 - b1 ;
79+
80+ a = 8 * a * a ;
81+ b1 = 8 * b * b ;
82+
83+ // Outline drawing
1184 do {
12- pixels . push ( { x : x1 , y : y0 } ) ; /* I. Quadrant */
13- pixels . push ( { x : x0 , y : y0 } ) ; /* II. Quadrant */
14- pixels . push ( { x : x0 , y : y1 } ) ; /* III. Quadrant */
15- pixels . push ( { x : x1 , y : y1 } ) ; /* IV. Quadrant */
85+ pixels . push ( { x : x1 , y : y0 } ) ; // I Quadrant
86+ pixels . push ( { x : x0 , y : y0 } ) ; // II Quadrant
87+ pixels . push ( { x : x0 , y : y1 } ) ; // III Quadrant
88+ pixels . push ( { x : x1 , y : y1 } ) ; // IV Quadrant
89+
1690 e2 = 2 * err ;
17- if ( e2 <= dy ) { y0 ++ ; y1 -- ; err += dy += a ; } /* y step */
18- if ( e2 >= dx || 2 * err > dy ) { x0 ++ ; x1 -- ; err += dx += b1 ; } /* x step */
91+ if ( e2 <= dy ) {
92+ y0 ++ ;
93+ y1 -- ;
94+ err += dy += a ;
95+ }
96+ if ( e2 >= dx || 2 * err > dy ) {
97+ x0 ++ ;
98+ x1 -- ;
99+ err += dx += b1 ;
100+ }
19101 } while ( x0 <= x1 ) ;
20102
21- while ( y0 - y1 < b ) { /* too early stop of flat ellipses a=1 */
22- pixels . push ( { x : x0 - 1 , y : y0 } ) ; /* -> finish tip of ellipse */
103+ while ( y0 - y1 <= b ) {
104+ pixels . push ( { x : x0 - 1 , y : y0 } ) ;
23105 pixels . push ( { x : x1 + 1 , y : y0 ++ } ) ;
24106 pixels . push ( { x : x0 - 1 , y : y1 } ) ;
25107 pixels . push ( { x : x1 + 1 , y : y1 -- } ) ;
26108 }
27- return pixels ;
28- }
109+
110+ // --- Fill interior ---
111+ // Group pixels by row (y), then fill between minX and maxX
112+ const rows : Record < number , { minX : number ; maxX : number } > = { } ;
113+ for ( const p of pixels ) {
114+ if ( ! ( p . y in rows ) ) {
115+ rows [ p . y ] = { minX : p . x , maxX : p . x } ;
116+ } else {
117+ rows [ p . y ] . minX = Math . min ( rows [ p . y ] . minX , p . x ) ;
118+ rows [ p . y ] . maxX = Math . max ( rows [ p . y ] . maxX , p . x ) ;
119+ }
120+ }
121+
122+ const filledPixels : { x : number ; y : number } [ ] = [ ...pixels ] ;
123+ for ( const y in rows ) {
124+ const { minX, maxX } = rows [ y ] ;
125+ for ( let x = minX ; x <= maxX ; x ++ ) {
126+ filledPixels . push ( { x, y : parseInt ( y ) } ) ;
127+ }
128+ }
129+
130+ return filledPixels ;
131+ }
132+
133+
134+ export default function getEllipsePixels ( x0 : number , y0 : number , x1 : number , y1 : number ) {
135+ if ( Math . abs ( x0 - x1 ) === Math . abs ( y0 - y1 ) && Math . abs ( x0 - x1 ) ) {
136+ console . log ( 'circle' , Math . abs ( x0 - x1 ) , Math . abs ( y0 - y1 ) )
137+ return betterCircle ( x0 , y0 , x1 + 1 , y1 + 1 ) ;
138+ }
139+ return ellipse ( x0 , y0 , x1 , y1 ) ;
140+ }
0 commit comments