Helix 3d toolkit, get the position of a model - c#

I have a helix toolkit project, in WPF, visual studio 2015. Using the example RectSelection I have a 3d viewport in which I can select my objects, which are BoxVisual3D.
What I need to do, is return the 3d position of the selected object. I have:
foreach (var model in models)
{
var geometryModel = model as GeometryModel3D;
if (geometryModel != null)
{
geometryModel.Material = geometryModel.BackMaterial = material;
//do stuff
UserControl1.Point1Position = model.Transform;
UserControl1.returnPoint.X = model.Transform.Value.M14;
UserControl1.returnPoint.Y = geometryModel.Transform.Value.M24;
UserControl1.returnPoint.Z = geometryModel.Transform.Value.M34;
}
}
But the values always return as 0. (I spawn the box myself, so i know they are not 0).
When I step through, there is a selected object, but the transform reads as all zeros. How can i get the position of a BoxVisual3D?
Thanks.

After nearly 3 years maybe not you but someone else could face with this issue. Here is my explanation.
If you created a Model3D with transform attribute you can use OffsetX, OffsetY, OffsetZ. But if you just created your BoxVisual3D with center attributes there will be no transform in it. Hence you just cant reach it. Create your objects with transform attribute. And another issue creating object in the specified Point3D. Here is my code:
my_point = new Point3D(-15,7,5);
var myTransform = new Transform3DGroup();
TranslateTransform3D myTranslate = new TranslateTransform3D(my_point.X, my_point.Y, my_point.Z);
myTransform.Children.Add(myTranslate);
kontrol.Transform = myTransform; //ez
myModel.Children.Add(kontrol);
And here is the getting the Transform back:
Transform3D mytransform = sourceobject.Transform;
Console.WriteLine(mytransform.Value.OffsetX + "," + mytransform.Value.OffsetY+"," + mytransform.Value.OffsetZ);

To get the Position of the Matrix3D you have to use the Offset Properties:
public static Point3D GetPosition(this Matrix3D m)
{
return new Point3D
{
X = m.OffsetX,
Y = m.OffsetY,
Z = m.OffsetZ
};
}

Related

Unity RectTransform.sizeDelta doesn't change size

I am trying to change size of a gameobject that has TextMeshPro - Text (UI) component to fit his parent. Here is the code.
static public Cube CreateCube(Transform parent, Vector2 anchors, Vector2 position)
{
Cube c = Instantiate(cubePrefab); // prefab has Cube component
var tr = (RectTransform)c.transform;
tr.SetParent(parent); // canvas or other 2d object
tr.anchorMin = tr.anchorMax = anchors;
tr.anchoredPosition = position;
var textObj = new GameObject("Text");
var textTr = textObj.AddComponent<RectTransform>();
textTr.SetParent(tr);
textTr.anchorMin = textTr.anchorMax = Vector2.one / 2; // anchors at the center of the parent
textTr.anchoredPosition = Vector2.zero;
// tr.sizeDelta is Vector2(60, 60) from prefab;
print(new Vector2(tr.sizeDelta.x, tr.sizeDelta.y * 2 / 3)); // Vector2(60, 40)
textTr.sizeDelta = new Vector2(tr.sizeDelta.x, tr.sizeDelta.y * 2 / 3); // width is the same as parent's width
// height is two thirds of parent's height
print(textTr.sizeDelta); // also Vector2(60, 40)
var text = textObj.AddComponent<TextMeshProUGUI>();
text.text = "Test";
text.fontSize = 14;
text.alignment = TextAlignmentOptions.Center;
return c;
}
Second print shows that sizeDelta has been set, but in the inspector and on the scene size is (200, 50) like without textTr.sizeDelta = ... line.
But if I add
private void Start()
{
((RectTransform)transform.GetChild(0)).sizeDelta = new Vector2(60, 40);
}
into the Cube class, it will work. Why doesn't it work when I create text?
Objects hierarchy:
-> Some 2d object
---> Object with Cube component
-----> Text object
Canvas hierarchy is calculated not immediately when you create and add new objects to it, and it often may lead to some unpredictable results. The reccomended workaround is to wait at least 1 frame after the object is created and only then apply changes for properties like sizeDelta. You can do this using Coroutine and skip 1 frame with yield return null. As an alternative for you specific use case, you can try to use RectTransform.SetSizeWithCurrentAnchors instead of directly setting sizeDelta, try this approach first I'd say.
textRt.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, 60);
textRt.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 40);

Onvif PTZControl troubles

I am using ONVIF protocol to control IP-cameras. Currently got stuck on zoom controlls with different cameras. I am using PTZBinding wsdl [https://www.onvif.org/onvif/ver20/ptz/wsdl/ptz.wsdl], tried ContinuousMove and RelativeMove fucntions, but it apperars to work in different ways on different cameras.
ContinuousMove function takes velocity and timeout, RelativeMove function takes vector and velocity.
I. e., first camera works perfect with ContinousMove, but with RelativeMove it always apply maximum/minimus zoom, and vice versa with second camera.
Didn't try AbsoluteMove because I can't find way to get current zoom value.
Can't find universal method to control zoom on multiple cameras, asking for your help.
Any advices will be helpful, I will provide source code if needed.
Solved this problem by getting current zoom value with GetStatus and then controlling using AbsoluteMove. There is a source code:
public void StartZoom (Direction direction)
{
if (zoomEnabled_)
{
var ptzStatus_ = ptzClient_.GetStatus(profile_.token);
moveVector_.PanTilt = ptzStatus_.Position.PanTilt;
moveVector_.Zoom = ptzStatus_.Position.Zoom;
var zoomSpace = options_.Spaces.AbsoluteZoomPositionSpace;
var minZoom = zoomSpace[0].XRange.Min;
var maxZoom = zoomSpace[0].XRange.Max;
moveVelocity_.Zoom.x = 0;
if (direction.HasFlag(Direction.ZoomIn))
{
if (ptzStatus_.Position.Zoom.x + 1 / 20F <= maxZoom)
{
moveVelocity_.Zoom.x = velocityZoomIn_;
moveVector_.Zoom.x += 1 / 20F;
}
else
{
moveVector_.Zoom.x = maxZoom;
}
}
else if (direction.HasFlag(Direction.ZoomOut))
{
if (ptzStatus_.Position.Zoom.x - 1 / 20F >= minZoom)
{
moveVelocity_.Zoom.x = velocityZoomOut_;
moveVector_.Zoom.x -= 1 / 20F;
}
else
{
moveVector_.Zoom.x = minZoom;
}
}
ptzClient_.AbsoluteMove(profile_.token, moveVector_, moveVelocity_);
}
}

HelixToolkit how to exclude elements from HitTest

I have a scene with a skybox and I would like to get the point the user clicked projected onto the skybox.
I'm using HelixViewport3D.FindNearestPoint(Point pt) to get the point, which works very well, except when there's anything between the click and the skybox. In this situation it returns the point projected onto the object in front of the skybok.
Is there any way to flag an element so it would be ignored in HitTests?
You can catch point on any Visual3D or Geometry3D
Give names to your Visual3D objects.
ModelVisual3D modelVisual3D = new ModelVisual3D();
modelVisual3D.SetName("ModelName");
You can use FindHits method with your HelixViewPort3D
Point3D point3D;
var hitList = yourHelixViewPort.ViewPort.FindHits(Point point);
foreach (var hit in hitList)
{
if (hit.Visual != null)
{
if (hit.Visual.GetName() == "ModelName")
{
point3D = hit.Position;
// You can use also hit.Mesh
// also hit.Model
// also hit.Visual
// also hit.Normal
}
}
}

Draw point where mouse clicked

I'm using HelixToolkit to see and interact with STL files. I need to draw or mark a point clicked by user on the window. I have the coordinates, I know where to draw that point, but I don't know how to draw it, can someone help me? I post some code to explain what I have right now:
private void vierport3d_MouseRightClick(object sender, MouseButtonEventArgs e)
{
Point mousePos = e.GetPosition(viewPort3d);
PointHitTestParameters hitParams = new PointHitTestParameters(mousePos);
VisualTreeHelper.HitTest(viewPort3d, null, ResultCallback, hitParams);
}
public HitTestResultBehavior ResultCallback(HitTestResult result)
{
RayHitTestResult rayResult = result as RayHitTestResult;
if (rayResult != null)
{
RayMeshGeometry3DHitTestResult rayMeshResult = rayResult as RayMeshGeometry3DHitTestResult;
//HERE I HAVE THE LOCATION TO DRAW
MessageBox.Show(rayMeshResult.PointHit.X + " " + rayMeshResult.PointHit.Y + " " + rayMeshResult.PointHit.Z);
if (rayMeshResult != null)
{
// I THINK I HAVE TO DRAW THE POINT HERE
}
}
return HitTestResultBehavior.Continue;
}
PD: I show the stl on a viewport3d.
We had same scenario in our project and used a sphere to visually indicate the point.
<ht:SphereVisual3D Radius="0.75" Fill="Red" Center="{Binding ContactPoint}" />
ContactPoint is a Point3D type.
This might help, but its probably not the most effecient.
Try the following:
This will create a 3D sphere that can be rendered at the given coordinates.
var sphereSize = 0.025;
/* keep these values low, the higher the values the more detailed the sphere which may impact your rendering perfomance.*/
var phi = 12;
var theta = 12;
MeshBuilder meshBuilder = new MeshBuilder();
Pass in your x,y,z to the first parameter. i.e. the click 3D location.
meshBuilder.AddSphere( new Point3D(x,y,z), sphereSize , theta, phi);
GeometryModel3D sphereModel = new GeometryModel3D(meshBuilder.ToMesh(),MaterialHelper.CreateMaterial(Brushes.Green,null,null,1,0));
Rendering the Point in your viewport
You will need a ModelVisual3D component as a child of the HelixViewport. ( This can be implemented in C# or in XAML) its up to you, ill show both ways.
C# version
NB: You need a reference to the helixviewport if its defined in xaml. Set the x:Name:"" to something appropriate. e.g x:Name="helixViewPort"
ModelVisual3D visualizer = new ModelVisual3D();
visualizer.Content = sphereModel;
helixViewPort.Children.Add(visualizer);
XAML version
I'll assume your xaml code has at least a helix view port so you'll have to add a ModelVisual3D child to the helix viewport if there's none.
<h:HelixViewport3D x:Name="HelixPlotViewPort" >
<h:DefaultLights/>
<ModelVisual3D x:Name="Visualizer">
</ModelVisual3D>
</h:HelixViewport3D>
//Then in C# add the following
Visualizer.Content = sphereModel;
That should do it, hope it helps, do inform us if you find a better solution. :)

Programmatically get the location of a block in AutoCAD with its ObjectId

I'm trying to write a method to change an existing block in an AutoCAD drawing. In this case, I want to change the length of a block by changing its scale factors. The method I've written will accomplish that by deleting the old block and creating a new one with the newly desired scale factors.
private static ObjectId _adjustCapPlate(ObjectId capPlateID, bool isHorizontal, Distance left = null, Distance right = null)
{
BlockReference capPlate = EntityMethods.GetBlockById(capPlateID); //Getting the block to be replaced
Scale3d oldScales = capPlate.ScaleFactors; //Getting the scales of the old block
Point3d originalLocation = capPlate.Location; // ToDo: Replace capPlate.Location with code that will return the coordinates of the insertion point of the block
EntityMethods.DeleteBlockFromDrawingWithId(capPlate.Id); //Deleting the old block
//Using specified splice plate length if method does not specify something else
if (left == null)
{
left = new Distance(SettingsController.SplicePlateLength / 2);
}
if (right == null)
{
right = new Distance(SettingsController.SplicePlateLength);
}
Distance newXScale, newYScale, newZScale;
Point3d newLocation;
if (isHorizontal) //If wall is oriented horizontally
{
newXScale = new Distance(DistanceType.Inch, oldScales.X - right.Inches);
newYScale = new Distance(DistanceType.Inch, oldScales.Y);
newLocation = new Point3d(originalLocation.X + left.Inches, originalLocation.Y, originalLocation.Z);
}
else
{
newXScale = new Distance(DistanceType.Inch, oldScales.X);
newYScale = new Distance(DistanceType.Inch, oldScales.Y - right.Inches);
newLocation = new Point3d(originalLocation.X, originalLocation.Y + left.Inches, originalLocation.Z);
}
newZScale = new Distance(DistanceType.Inch, oldScales.Z);
BlockReference newCapPlate = EntityMethods.InsertBlock("member", newLocation, "0", null, newXScale, newYScale, newZScale);
}
All I need to make this method to work is to replace capPlate.Location with something that will get an existing block's XYZ coordinates in the AutoCAD drawing. It seems ridiculous that there is no obvious way to get a block's coordinates programmatically.
I will not accept answers that change my approach. This has to be done by deleting the old block and replacing it by inserting a new block with new properties where the old block was.
Instead of using capPlate.Location, use capPlate.Position and you should get the desired behavior.

Categories