Page 1 of 1

Drawing a line in 3D across a Surface Plot

Posted: Thu Feb 16, 2017 12:10 pm
by 16479009
Hi,

I have been attemptong to draw a line (in 3D) across a surface plot using the MoveTo3D and LineTo3D functions. Intuitively, I tried using the CalcXPOsValue and CalcYPosValue to obtain the start and end positions for x and y but I see not equivalent for the z value. I winder if there is a simpler way to achieve this or whether I am missing something?

Re: Drawing a line in 3D across a Surface Plot

Posted: Fri Feb 17, 2017 9:25 am
by yeray
Hello,

You could use the TSurfaceSeries CalcXPos/CalcYPos/CalcZPos functions. Ie:

Code: Select all

uses TeeSurfa, TeeTools, TeCanvas;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Chart1.Chart3DPercent:=100;
  Chart1.Aspect.Orthogonal:=False;
  Chart1.Aspect.Zoom:=80;
  Chart1.Legend.Visible:=False;

  Chart1.AddSeries(TSurfaceSeries).FillSampleValues(10);

  Chart1.Tools.Add(TRotateTool);
end;

procedure TForm1.Chart1AfterDraw(Sender: TObject);

  function Calc3DPos(ValueIndex: Integer; ASeries: TCustom3DSeries): TPoint3D;
  begin
    with ASeries do
      result:=Point3D(CalcXPos(ValueIndex), CalcYPos(ValueIndex), CalcZPos(ValueIndex));
  end;

begin
  with Chart1.Canvas do
  begin
    Pen.Color:=clRed;
    MoveTo3D(Calc3DPos(2, Chart1[0] as TSurfaceSeries));
    LineTo3D(Calc3DPos(45, Chart1[0] as TSurfaceSeries));
  end;
end;

Re: Drawing a line in 3D across a Surface Plot

Posted: Mon Jul 24, 2017 9:17 am
by 16479009
tmp2.png
tmp2.png (107.64 KiB) Viewed 9149 times
tmp1.png
tmp1.png (161.36 KiB) Viewed 9146 times
tmp3.png
tmp3.png (78.46 KiB) Viewed 9148 times
I used this method to draw on the line. I was attempting to use the real values in x, y and z to draw across the chart in each plane. The actual line produced looks perfect but its orientation is wrong. The x and y coordinates appear incorrect in a 3d context. Any advice would be appreciated.

Code: Select all

procedure TKPWSurfaceForm.DrawArbitraryLine;
  function Calc3DPos(valueIndex: Integer): TPoint3D;
  var
    xVal, zVal, yVal: Double;
    xCo, yCo, zCo: Integer;
  begin
    xVal := arbitraryLine.XValue[ValueIndex];
    xCo := Series1.CalcXSizeValue(xVal);
    zVal := arbitraryLine.ZValue[ValueIndex];
    zCo := Series1.CalcPosValue(zVal);
    yVal := arbitraryLine.YValue[ValueIndex];
    yCo := Series1.CalcYPosValue(yVal);

    Memo1.Lines.Add(Format('%d %d %d',[xCo,zCo,yCo]));

    with Series1 do
      Result := Point3D(xCo, zCo, yCo);
  end;
var
  i: Integer;
  pointA, pointB: TPoint3D;
begin
  PopulateArbitraryLine;

  if arbitraryLine.Count > 1 then
    with MainChart.Canvas do
    begin
      Pen.Color := RGB(51,255,51);
      Pen.Width := 5;
      for i := 0 to arbitraryLine.Count-2 do // don't connect the final point to the first
      begin
        Memo1.Lines.Add('From');
        pointA := Calc3DPos(i);
        Memo1.Lines.Add('To');
        pointB := Calc3DPos(i+1);
        MoveTo3D(pointA);
        LineTo3D(pointB);
      end;
    end;
end;

Re: Drawing a line in 3D across a Surface Plot

Posted: Wed Jul 26, 2017 10:04 am
by yeray
Hello,

First of all note in your code I see some issues:
- You are calling CalcXSizeValue that gives the distance in pixels from the given axis value to the axis value 0.
- You are getting the XValue/YValue/ZValue of the given ValueIndex and then converting it to pixels using the CalcPosValue and CalcYPosValue. That's correct but you can just use CalcXPos/CalcYPos/CalcZPos to get the pixel coordinates from a ValueIndex.
- You seem to be creating the Point3D giving the parameters in the wrong order: It expects x, y and z and you are giving x, z and y.

If you only want to highlight a part of your TSurfaceSeries you could just use a TPoint3DSeries. Ie:

Code: Select all

var
  surfaceSeries: TSurfaceSeries;
  arbitraryLine: TPoint3DSeries;

procedure TForm1.FormCreate(Sender: TObject);
var i: Integer;
begin
  Chart1.Chart3DPercent:=100;
  Chart1.Aspect.Zoom:=80;
  Chart1.Aspect.Orthogonal:=False;
  Chart1.Legend.Hide;

  surfaceSeries:=Chart1.AddSeries(TSurfaceSeries) as TSurfaceSeries;
  surfaceSeries.FillSampleValues;

  arbitraryLine:=Chart1.AddSeries(TPoint3DSeries) as TPoint3DSeries;
  arbitraryLine.Pointer.Visible:=False;
  arbitraryLine.Pen.Color:=RGB(51,255,51);
  arbitraryLine.Pen.Width:=5;

  for i:=42 to 47 do
    arbitraryLine.AddXYZ(surfaceSeries.XValue[i], surfaceSeries.YValue[i], surfaceSeries.ZValue[i])
end;
Project2_2017-07-26_11-46-46.png
Project2_2017-07-26_11-46-46.png (66.01 KiB) Viewed 9129 times
If you want to draw a line that isn't following consecutive points of your TSurfaceSeries, then you should draw the line manually as you were doing. Correcting the issues at the top of this post this way will probably work better.
If you still find problems with it, please try to arrange a simple example project we can run as-is to reproduce the situation here.