@@ -4853,6 +4853,198 @@ int PSL_plotpolygon (struct PSL_CTRL *PSL, double *x, double *y, int n) {
48534853 return (PSL_NO_ERROR );
48544854}
48554855
4856+ int PSL_plotgradienttriangle (struct PSL_CTRL * PSL , double * x , double * y , double * rgb , int steps ) {
4857+ /* Draw triangle with smooth vertex color gradient using barycentric interpolation
4858+ * x, y: arrays of 3 coordinates (in inches, will be converted to points)
4859+ * rgb: array of 9 values [r1 g1 b1 r2 g2 b2 r3 g3 b3], each 0.0-1.0
4860+ * steps: number of subdivisions (higher = smoother, slower)
4861+ *
4862+ * Writen by Claude.ai
4863+ */
4864+
4865+ int i , j ;
4866+ double s , t ;
4867+ double s1 , t1 , s2 , t2 , s3 , t3 , s4 , t4 ;
4868+ double u1 , v1 , w1 , u2 , v2 , w2 , u3 , v3 , w3 , u4 , v4 , w4 ;
4869+ double px1 , py1 , px2 , py2 , px3 , py3 , px4 , py4 ;
4870+ double pr1 , pg1 , pb1 , pr2 , pg2 , pb2 , pr3 , pg3 , pb3 , pr4 , pg4 , pb4 ;
4871+ double step_inv ;
4872+ double x_pts [3 ], y_pts [3 ];
4873+
4874+ if (steps <= 0 ) steps = 50 ; /* Default quality */
4875+ if (steps > 200 ) steps = 200 ; /* Limit maximum */
4876+
4877+ /* Convert inches to points */
4878+ for (i = 0 ; i < 3 ; i ++ ) {
4879+ x_pts [i ] = x [i ] * PSL -> internal .dpu ;
4880+ y_pts [i ] = y [i ] * PSL -> internal .dpu ;
4881+ }
4882+
4883+ step_inv = 1.0 / steps ;
4884+
4885+ PSL_comment (PSL , "Begin gradient triangle\n" );
4886+ PSL_command (PSL , "gsave\n" );
4887+
4888+ /* Subdivide triangle into micro-triangles using barycentric coordinates */
4889+ for (i = 0 ; i < steps ; i ++ ) {
4890+ t = i * step_inv ;
4891+ for (j = 0 ; j < steps ; j ++ ) {
4892+ s = j * step_inv ;
4893+
4894+ /* Barycentric coordinates for 4 corners of sub-quad */
4895+ s1 = s ; t1 = t ;
4896+ s2 = s + step_inv ; t2 = t ;
4897+ s3 = s + step_inv ; t3 = t + step_inv ;
4898+ s4 = s ; t4 = t + step_inv ;
4899+
4900+ /* Check if first corner is inside triangle */
4901+ if (s1 + t1 <= 1.0 ) {
4902+ u1 = 1.0 - s1 - t1 ;
4903+ v1 = s1 ;
4904+ w1 = t1 ;
4905+
4906+ /* Interpolate position */
4907+ px1 = u1 * x_pts [0 ] + v1 * x_pts [1 ] + w1 * x_pts [2 ];
4908+ py1 = u1 * y_pts [0 ] + v1 * y_pts [1 ] + w1 * y_pts [2 ];
4909+
4910+ /* Interpolate color */
4911+ pr1 = u1 * rgb [0 ] + v1 * rgb [3 ] + w1 * rgb [6 ];
4912+ pg1 = u1 * rgb [1 ] + v1 * rgb [4 ] + w1 * rgb [7 ];
4913+ pb1 = u1 * rgb [2 ] + v1 * rgb [5 ] + w1 * rgb [8 ];
4914+
4915+ /* Check second corner */
4916+ if (s2 + t2 <= 1.0 ) {
4917+ u2 = 1.0 - s2 - t2 ;
4918+ v2 = s2 ;
4919+ w2 = t2 ;
4920+
4921+ px2 = u2 * x_pts [0 ] + v2 * x_pts [1 ] + w2 * x_pts [2 ];
4922+ py2 = u2 * y_pts [0 ] + v2 * y_pts [1 ] + w2 * y_pts [2 ];
4923+ pr2 = u2 * rgb [0 ] + v2 * rgb [3 ] + w2 * rgb [6 ];
4924+ pg2 = u2 * rgb [1 ] + v2 * rgb [4 ] + w2 * rgb [7 ];
4925+ pb2 = u2 * rgb [2 ] + v2 * rgb [5 ] + w2 * rgb [8 ];
4926+
4927+ /* Check fourth corner */
4928+ if (s4 + t4 <= 1.0 ) {
4929+ u4 = 1.0 - s4 - t4 ;
4930+ v4 = s4 ;
4931+ w4 = t4 ;
4932+
4933+ px4 = u4 * x_pts [0 ] + v4 * x_pts [1 ] + w4 * x_pts [2 ];
4934+ py4 = u4 * y_pts [0 ] + v4 * y_pts [1 ] + w4 * y_pts [2 ];
4935+ pr4 = u4 * rgb [0 ] + v4 * rgb [3 ] + w4 * rgb [6 ];
4936+ pg4 = u4 * rgb [1 ] + v4 * rgb [4 ] + w4 * rgb [7 ];
4937+ pb4 = u4 * rgb [2 ] + v4 * rgb [5 ] + w4 * rgb [8 ];
4938+
4939+ /* Draw first micro-triangle: p1-p2-p4 */
4940+ PSL_command (PSL , "%.6f %.6f %.6f C\n" ,
4941+ (pr1 + pr2 + pr4 ) / 3.0 ,
4942+ (pg1 + pg2 + pg4 ) / 3.0 ,
4943+ (pb1 + pb2 + pb4 ) / 3.0 );
4944+ PSL_command (PSL , "N %.4f %.4f M %.4f %.4f L %.4f %.4f L P F\n" , px1 , py1 , px2 , py2 , px4 , py4 );
4945+ }
4946+
4947+ /* Check third corner for second micro-triangle */
4948+ if (s3 + t3 <= 1.0 ) {
4949+ u3 = 1.0 - s3 - t3 ;
4950+ v3 = s3 ;
4951+ w3 = t3 ;
4952+
4953+ px3 = u3 * x_pts [0 ] + v3 * x_pts [1 ] + w3 * x_pts [2 ];
4954+ py3 = u3 * y_pts [0 ] + v3 * y_pts [1 ] + w3 * y_pts [2 ];
4955+ pr3 = u3 * rgb [0 ] + v3 * rgb [3 ] + w3 * rgb [6 ];
4956+ pg3 = u3 * rgb [1 ] + v3 * rgb [4 ] + w3 * rgb [7 ];
4957+ pb3 = u3 * rgb [2 ] + v3 * rgb [5 ] + w3 * rgb [8 ];
4958+
4959+ if (s4 + t4 <= 1.0 ) { /* Already computed p4 */
4960+ /* Draw second micro-triangle: p2-p3-p4 */
4961+ PSL_command (PSL , "%.6f %.6f %.6f C\n" ,
4962+ (pr2 + pr3 + pr4 ) / 3.0 ,
4963+ (pg2 + pg3 + pg4 ) / 3.0 ,
4964+ (pb2 + pb3 + pb4 ) / 3.0 );
4965+ PSL_command (PSL , "N %.4f %.4f M %.4f %.4f L %.4f %.4f L P F\n" , px2 , py2 , px3 , py3 , px4 , py4 );
4966+ }
4967+ }
4968+ }
4969+ }
4970+ }
4971+ }
4972+
4973+ PSL_command (PSL , "grestore\n" );
4974+ PSL_comment (PSL , "End gradient triangle\n" );
4975+
4976+ return (PSL_NO_ERROR );
4977+ }
4978+
4979+ int PSL_plotgradienttriangle_gouraud (struct PSL_CTRL * PSL , double * x , double * y , double * rgb ) {
4980+ /* Draw triangle with smooth Gouraud shading using PostScript Type 4 shading
4981+ * x, y: arrays of 3 coordinates (in inches, will be converted to points)
4982+ * rgb: array of 9 values [r1 g1 b1 r2 g2 b2 r3 g3 b3], each 0.0-1.0
4983+ * This uses PostScript Level 2/3 shading for perfectly smooth gradients
4984+ *
4985+ * Writen by Claude.ai
4986+ */
4987+
4988+ int i ;
4989+ double x_pts [3 ], y_pts [3 ];
4990+
4991+ /* Convert inches to points */
4992+ for (i = 0 ; i < 3 ; i ++ ) {
4993+ x_pts [i ] = x [i ] * PSL -> internal .dpu ;
4994+ y_pts [i ] = y [i ] * PSL -> internal .dpu ;
4995+ }
4996+
4997+ PSL_comment (PSL , "Begin Gouraud shaded triangle\n" );
4998+ PSL_command (PSL , "gsave\n" );
4999+ /* Set clipping path to triangle to constrain shfill and establish bounding box */
5000+ PSL_command (PSL , "N %.4f %.4f M %.4f %.4f L %.4f %.4f L closepath clip" ,
5001+ x_pts [0 ], y_pts [0 ], x_pts [1 ], y_pts [1 ], x_pts [2 ], y_pts [2 ]);
5002+
5003+
5004+ /* Create Type 4 (Free-Form Gouraud) shading dictionary */
5005+ PSL_command (PSL , "<<\n" );
5006+ PSL_command (PSL , " /ShadingType 4\n" );
5007+ PSL_command (PSL , " /ColorSpace /DeviceRGB\n" );
5008+ PSL_command (PSL , " /BitsPerCoordinate 16\n" );
5009+ PSL_command (PSL , " /BitsPerComponent 8\n" );
5010+ PSL_command (PSL , " /BitsPerFlag 8\n" );
5011+
5012+ /* Decode arrays map from integer coordinates to actual coordinate ranges */
5013+ /* Find bounding box */
5014+ double xmin = x_pts [0 ], xmax = x_pts [0 ], ymin = y_pts [0 ], ymax = y_pts [0 ];
5015+ for (i = 1 ; i < 3 ; i ++ ) {
5016+ if (x_pts [i ] < xmin ) xmin = x_pts [i ];
5017+ if (x_pts [i ] > xmax ) xmax = x_pts [i ];
5018+ if (y_pts [i ] < ymin ) ymin = y_pts [i ];
5019+ if (y_pts [i ] > ymax ) ymax = y_pts [i ];
5020+ }
5021+
5022+ PSL_command (PSL , " /Decode [%.4f %.4f %.4f %.4f 0 1 0 1 0 1]\n" , xmin , xmax , ymin , ymax );
5023+ PSL_command (PSL , " /DataSource <\n" );
5024+
5025+ /* Write triangle data as hex string */
5026+ /* Flag 0 = start new triangle, then coordinates (x,y) scaled to 0-65535, then RGB scaled to 0-255 */
5027+ for (i = 0 ; i < 3 ; i ++ ) {
5028+ unsigned int flag = 0 ; /* Start new triangle with vertex 0 */
5029+ unsigned int x_scaled = (unsigned int )((x_pts [i ] - xmin ) / (xmax - xmin ) * 65535.0 + 0.5 );
5030+ unsigned int y_scaled = (unsigned int )((y_pts [i ] - ymin ) / (ymax - ymin ) * 65535.0 + 0.5 );
5031+ unsigned int r = (unsigned int )(rgb [i * 3 + 0 ] * 255.0 + 0.5 );
5032+ unsigned int g = (unsigned int )(rgb [i * 3 + 1 ] * 255.0 + 0.5 );
5033+ unsigned int b = (unsigned int )(rgb [i * 3 + 2 ] * 255.0 + 0.5 );
5034+
5035+ PSL_command (PSL , " %02X %04X %04X %02X%02X%02X\n" , flag , x_scaled , y_scaled , r , g , b );
5036+ }
5037+
5038+ PSL_command (PSL , " >\n" );
5039+ PSL_command (PSL , ">>\n" );
5040+ PSL_command (PSL , "shfill\n" );
5041+
5042+ PSL_command (PSL , "grestore\n" );
5043+ PSL_comment (PSL , "End Gouraud shaded triangle\n" );
5044+
5045+ return (PSL_NO_ERROR );
5046+ }
5047+
48565048int PSL_setexec (struct PSL_CTRL * PSL , int action ) {
48575049 /* Enables of disables the execution of a PSL_plot_completion function at start of a PSL_plotinit overlay */
48585050 PSL -> current .complete = (action ) ? 1 : 0 ;
0 commit comments