在画布中使用十进制值绘制图像

时间:2013-05-27 17:31:18

标签: delphi drawing rounding

我正在尝试绘制一个源自X,Y值列表的图像,这些值代表一条线的起点和终点。它们以英寸为单位,因此它们目前被格式化为小数。

我遇到的问题是图纸。 MoveToLineTo命令需要整数而不是double。如果我使用Round(float)数学运算,您会看到下面的输出。舍入产生相同的起点和终点,因此不会绘制任何内容。

如何从十进制X,Y点列表中绘制我的形状?

输入值的调试代码(小数):

LineStartVal: -88.988857, 36.265838
LineEndVal: -89.094923, 36.371904
LineStartVal: -89.094923, 36.371904
LineEndVal: -95.000423, 36.371904
LineStartVal: -95.000423, 36.371904
LineEndVal: -95.000423, 32.828604
LineStartVal: -95.000423, 32.828604
LineEndVal: -99.134273, 32.828604

舍入后输出点的调试代码:

MoveTo: -89, 36
LineTo: -89, 36
MoveTo: -89, 36
LineTo: -95, 36
MoveTo: -95, 36
LineTo: -95, 33
MoveTo: -95, 33
LineTo: -99, 33

绘图代码段

//Function used to to get start and stop points
LSNLineObj.GetEndPoints(X1,Y1,X2,Y2);


//OutputMemo.Text := OutputMemo.Text + #13#10 + 'LineStartVal: ' + FloatToStrF(X1, ffGeneral, 8, 4) + ', ' + FloatToStrF(Y1, ffGeneral, 8, 4);
//OutputMemo.Text := OutputMemo.Text + #13#10 + 'LineEndVal: ' + FloatToStrF(X2, ffGeneral, 8, 4) + ', ' + FloatToStrF(Y2, ffGeneral, 8, 4);

X1int := Round(X1); X2int := Round(X2);
Y1int := Round(Y1); Y2int := Round(Y2);

PartImage.Canvas.MoveTo(X1int,X2int);

OutputMemo.Text := OutputMemo.Text + #13#10 + 'MoveTo: ' + IntToStr(X1int) + ', ' + IntToStr(Y1int);

PartImage.Canvas.LineTo(X2int,Y2int);

OutputMemo.Text := OutputMemo.Text + #13#10 + 'LineTo: ' + IntToStr(X2int) + ', ' + IntToStr(Y2int);

3 个答案:

答案 0 :(得分:7)

你有两个坐标系:首先,你的'逻辑'系统的坐标如-88.988857, 36.265838。其次,你有屏幕。你需要在这两者之间进行转换。你应该写函数

function LogToScreen(LogPoint: TRealVector): TPoint;
function ScreenToLog(Point: TPoint): TRealVector;

其中TRealVector是包含两个双打的记录。写这两个函数只需要小学数学。

例如,您可以让屏幕上的矩形0..8000..600对应逻辑值-110..-8030..40

提示:使用上述值

function LogToScreen(LogPoint: TRealVector): TPoint;
begin
  result.X := round(800 * (LogPoint.X - (-110)) / ((-80) - (-110)));
  result.Y := round(600 * (LogPoint.Y - 30) / (40 - 30)); // or rev. orientation
end;

答案 1 :(得分:0)

这个问题有几个部分。 1)如何将世界坐标缩放到屏幕坐标。 2)如何加载形状数据,3)如何绘制数据 到了屏幕。

数据转换信息来自Buro Tshaggelar的文章(http://www.ibrtses.com/delphi/dmcs.html)。他讨论了如何进行数据转换。

Tpaintbox或Timage左上角的x,y坐标为0,0。它们必须是整数值。您可以通过相对于偏移值进行缩放,从世界坐标转换为屏幕坐标,或从屏幕坐标转换为世界坐标。

您可以将形状绘制到Tpaintbox或TImage Canvas。如果您绘制到Tpaintbox,结果不会持久。

我建议您使用Tpoint数组来绘制形状,而不是绘制单独的线来创建形状。以下适用于XE2 VCL。

第1部分

当世界坐标是十进制值时,坐标由一个坐标为xLowvalue,xHighValue和yLowVaue以及yHighValue的框限定, 屏幕窗口可以定义为tlx..brx,tly..bry,其中坐标是整数值。

以下功能可在世界和屏幕坐标之间进行转换。安德烈亚斯的转换也有效。我喜欢它们,它们在同时转换坐标对方面更有效率。我更喜欢下面的转换来描述这个过程。

从世界坐标转换为屏幕坐标:

function mapW2SxLin(xf:double):integer;    开始     结果:= round(tlx +(xf - xlow)*(brx - tlx)/(xhigh - xlow));    端;

function mapW2SyLin(yf:double):integer;    开始     结果:=圆(b​​ry - (yf - ylow)*(bry - tly)/(yhigh - ylow));    端;

从屏幕坐标转换为世界坐标:

function mapS2WxLin(xs:integer):double;    开始     结果:= xlow +(xs - tlx)*(xhigh - xlow)/(brx - tlx);    端;

function mapS2WyLin(ys:integer):double;    开始     结果:= yhigh - (ys - tly)*(yhigh - ylow)/(bry - tly);    端;

在ikathygreat提供的示例中,所需的转换似乎是从笛卡尔坐标到屏幕坐标数据(提供的值似乎是纬度和经度位置对,显示为小数纬度和经度值)。

第2部分 这里的数据是硬编码的,而不是加载数据文件。 Goodle for 从文件填充tpoint数组,以获取加载xy动态数组的示例。有代码可以让您从文本文件加载数据。请注意,在加载数据时,必须提供代码以在SetLength中更改数组的大小(6)(当前编码为Setlength(xy,6);)并将数组的大小设置为6。这样做应该是另一个问题。该值将根据形状中的顶点数量而变化。

第3部分

使用Tpoint数组绘制形状。要求但未使用请求者的方法逻辑。绘制单个线条对于绘制形状的代码来说有点尴尬。我相信下面的例子是一个更简单的解决方案并完成工作。

使用以下方法设置绘制框,其边框反映世界坐标的最大限制:

xLow:= -88; //提供最东部的经度    xHigh:= -100; //提供最西端的经度    yLow:= 37; //提供的最高纬度    yHigh:= 32; //提供的最低纬度

我建议将全局变量定义为:

无功   超低,xHigh,yLow,yHigh:双;   TLX,BRX,TLY,BRY:整数;

xy:TPoint数组;

要使用数组绘制形状,您需要定义类型

型   TMyPolygon = TPoint数组; //动态数组

并在OnCreate表单事件处理程序中分配这些值:

BRX:= Paintbox1.Left;   tlx:= Paintbox1.Left + paintbox1.Width;   bry:= Paintbox1.Top;   tly:= Paintbox1.Top + paintbox1.Height;

向表单添加Image,Paintbox和Button。然后在Button和Form OnCreate处理程序中使用以下代码。还要添加转换函数和全局变量(xLow,xHigh,yLow,yHigh:double; tlx,brx,tly,bry:integer; xy:Point数组;)并记住添加Tpoint类型:(类型TMyPolygon = array of TPoint;)

implementation

{$R *.dfm}

  function mapW2SxLin(xf:double):integer;
    begin
      result:=round(tlx+(xf-xlow)*(brx-tlx)/(xhigh-xlow));
    end;

   function mapW2SyLin(yf:double):integer;
     begin
       result:=round(bry-(yf-ylow)*(bry-tly)/(yhigh-ylow));
     end;

   function mapS2WxLin(xs:integer):double;
     begin
       result:=xlow+(xs-tlx)*(xhigh-xlow)/(brx-tlx);
     end;

   function mapS2WyLin(ys:integer):double;
     begin
       result:=yhigh-(ys-tly)*(yhigh-ylow)/(bry-tly);
     end;


procedure TPlotShapeFm.Button1Click(Sender: TObject);
var
  //  xy: array of TPoint;  //probably want to define this globally
  x,y,x1,y1,x2,y2,x3,y3:integer;
  ax,ay,ax2,ay2:integer;
begin

{ Your values
 -88.988857, 36.265838
 -89.094923, 36.371904
 -89.094923, 36.371904
 -95.000423, 36.371904
 -95.000423, 36.371904
 -95.000423, 32.828604
 -95.000423, 32.828604
 -99.134273, 32.828604
 -88.988857, 36.265838    //repeat the first value to close the shape
}

 //convert from World to screen coordinates
 // these values are hard coded for this example
 // there are many  ways to load these from a text file

  x:=   mapW2SxLin(-88.988857);
  y:=   mapW2SyLin( 36.265838 );

  ax:=   mapW2SxLin(-89.094923);
  ay:=   mapW2SyLin(36.371904);

  x1:=   mapW2SxLin(-95.000423);
  y1:=   mapW2SyLin(36.371904);

  x2:=   mapW2SxLin(-95.000423);
  y2:=   mapW2SyLin(32.828604);

  ax2:=   mapW2SxLin(-99.134273);
  ay2:=   mapW2SyLin(32.828604);

  x3:=   mapW2SxLin(-88.988857);   //return to the starting coordinates to finish off the shape
  y3:=   mapW2SyLin(36.265838 );

  // populate the dynamic array
  Setlength(xy, 6);
  xy[0] := point(x,y);
  xy[1] := point(ax,ay);
  xy[2] :=  point(x1,y1);
  xy[3] :=  point(x2,y2);
  xy[4] :=  point(ax2,ay2);
  xy[5] :=  point(x3,y3);

  Paintbox1.Canvas.Brush.Color := Random($FFFFFF);

  //plot the shape
  //canvas.Polygon(xy);  //generic   or plot on the form itself
   // or
  Image1.canvas.polygon(xy);    //to plot on a Timage
  Paintbox1.canvas. polygon(xy);   //to plot on a Tpaintbox


end;

procedure TPlotShapeFm.FormCreate(Sender: TObject);
begin

  //You can set up you paint box using:

 { xLow := 0;
   xHigh:=-180;
   yLow:= 50;
   yHigh:= 30;   //to display part of North America   or


   xLow := 180;
   xHigh:=-180;
   yLow:= 90;
   yHigh:= 00;   //to display the entire World, North of the equator.

 }

 //  but to display the info provided as a large image
  xLow := -88;
  xHigh:=-100;
  yLow:= 37;
  yHigh:= 32;

  // scale the paintbox to World coordinates
  brx:= Paintbox1.Left;
  tlx:= Paintbox1.Left + paintbox1.Width;
  bry:= Paintbox1.Top;
  tly := Paintbox1.Top + paintbox1.Height;

end;

做得对,结果如下:

enter image description here

当我查看提供的数据和生成的形状时,我注意到数据可能没有按照请求者希望使用数组正确绘制形状的顺序列出(注意交叉)。可以通过从初始点开始以顺时针或逆时针顺序列出点来固定交叉,然后在初始点处完成。必须通过在初始点完成绘图来关闭形状。

答案 2 :(得分:0)

  

如何从十进制X,Y点列表中绘制我的形状?

http://graphics32.org/

var
  pts: TArrayOfFixedPoint;
  fr: TFloatRect;
begin
  ...
  pts := MakeArrayOfFixedPoints(fr);   
  SimpleFill(Bitmap, pts, Color32(clBlack), clCornSilk32);

追捕以下内容:GR32_Lines,GR32_Misc,GR32_Misc2,GR32_Text。将文件添加到运行时包。设计时包正确地依赖于r / t包,因此没有额外的步骤。我最近内置了XE5和XE7。