0

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

我遇到的问题是绘图。MoveToand命令需要一个整数而LineTo不是双精度数。如果我使用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);
4

3 回答 3

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;
于 2013-05-27T17:40:20.477 回答
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部分

当 World 坐标为十进制值时,坐标由坐标为 xLowvalue、xHighValue 和 yLowVaue 和 yHighValue 的框包围,屏幕窗口可以定义为 tlx..brx、tly..bry,其中坐标为整数值。

以下函数在世界坐标和屏幕坐标之间进行转换。Andreas 的转换也有效。我喜欢它们,它们同时转换坐标对更有效。我更喜欢下面的转换来描述这个过程。

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

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

函数 mapW2SyLin(yf:double):integer; 开始结果:= round(bry - (yf - ylow) * (bry - tly) / (yhigh - ylow)); 结尾;

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

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

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

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

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

第 3 部分

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

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

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

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

var xLow,xHigh,yLow,yHigh:double; 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;

将图像、油漆盒和按钮添加到窗体。然后在 Button 和 Form OnCreate 处理程序中使用以下代码。还要添加转换函数和全局变量 (xLow,xHigh,yLow,yHigh:double; tlx,brx,tly,bry:integer; xy: array of Point;) 并记得添加 Tpoint 类型:( type TMyPolygon = array of T点;)

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;

做对了,结果是这样的:

在此处输入图像描述

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

于 2013-05-28T15:52:26.900 回答
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 中构建。

于 2015-01-16T10:17:52.427 回答