I am tryin to draw a genealogy tree. In my tree, I store information about ex partners. So the Panel (Region) for person shoud looking like this
Z * * * Z * * * Z * * * X --- Y
Where Z represent exPartner, X represent Persion and Y represent current Wife / Husband
And now I would like to draw line to connect current relation with children. And ec relations with children. (graphicaly there will be a line beetwen Z and *)
But when the person is in the other region LayoutX and LayoutX property return relative value.
How Can I do that, and how dynamicaly resize this Panel ? I would like that Z should be allways at middle of line Horizontal line connecting children.
Given any node, you can get its bounds in the coordinate system of any other node in the same scene graph with:
Node nodeOfInterest = ... ;
Node anotherNode = ... ;
// ...
Bounds boundsInScene = nodeOfInterest.localToScene(nodeOfInterest.getBoundsInLocal());
Bounds boundRelativeToAnotherNode = anotherNode.sceneToLocal(boundsInScene);
So assuming you have some kind of pane which is a common ancestor to two nodes you want to connect, you can do this:
Pane commonAncestor = ... ;
Node n1 = ... ;
Node n2 = ... ;
Bounds n1InCommonAncestor = getRelativeBounds(n1, commonAncestor);
Bounds n2InCommonAncestor = getRelativeBounds(n2, commonAncestor);
Point2D n1Center = getCenter(n1InCommonAncestor);
Point2D n2Center = getCenter(n2InCommonAncestor);
Line connector = new Line(n1Center.getX(), n1Center.getY(), n2Center.getX(), n2Center.getY());
commonAncestor.getChildren().add(connector);
// ...
private Bounds getRelativeBounds(Node node, Node relativeTo) {
Bounds nodeBoundsInScene = node.localToScene(node.getBoundsInLocal());
return relativeTo.sceneToLocal(nodeBoundsInScene);
}
private Point2D getCenter(Bounds b) {
return new Point2D(b.getMinX() + b.getWidth() / 2, b.getMinY() + b.getHeight() / 2);
}
Related
grid = new Group();
for (y = 0; y < isoSteps; y++) {
for (x = 0; x < isoSteps; x++) {
p = new Point(x * stepSize, y * stepSize);
grid.addChild(p);
}
}
The code above gives me a " TypeError: item._remove is not a function" error.
You can add a point to a path and that path to a group. Groups are made up of paperjs items. Point is not an item, just a basic type.
Paperjs items contain a remove function and point does not. Thats probably why youre getting that TypeError.
How to translate a vector from an offset vector.
I have a line with 2 points attached one on the line start and one on the end vector of the line.
I want to translate the line and the points base on the DragConstrols position and not base on the line position I am just updating the line geometry vertices and the points position base on the line start and end vectors.
Keap in mind the points are not childs of the line, all objects are childs of the Scene
The line stores the points inside the userData of the line.
My goal is to translate all this 3 objects from the drag offset Vector3
Here is the screenshots of the line with the points.
start_vector = (-140, 60, 0)
end_vector = (-70, 100, 0)
I know the line start vector before the user drags the line v1 = (-140, 60, 0) and I
know the line end vector before the drag v2 = (-70, 100, 0)
Now when the user drags the line with the DragConstrols I dont let the dragControls to change the line position I get the dragConstrol vector3 and I want to translate the line vetrices base on that vector.
The DragControls allways start from (0, 0, 0) so I try to add the line vectors (v1, v2) with the position but I get this result.
Can someone show me how to set the line start and end vector from the dragControls position.
Or how to translate vectors from offset vectors.
Thank you.
My code so far.
Keep in mind the line is a Line2 (fat line) three/examples/jsm/lines/Line2
class NewLine extends Line2 {
private _start = new Vector3();
private _end = new Vector3();
/** The viewport width */
public viewportWidth: number;
/** The viewport height */
public viewportHeight: number;
/** The line3 */
public line3 = new Line3();
public get start() { return this._start; }
public set start(v: Vector3) {
this._start = v;
this.line3.set(v, this._end);
this.geometry['setPositions']([
v.x,
v.y,
v.z,
this._end.x,
this._end.y,
this._end.z
]);
this.geometry['verticesNeedUpdate'] = true; // maybe i don't need that
this.geometry.computeBoundingSphere();
this.computeLineDistances();
}
public get end() { return this._end; }
public set end(v: Vector3) {
this._end = v;
this.line3.set(this._start, v);
this.geometry['setPositions']([
this._start.x,
this._start.y,
this._start.z,
v.x,
v.y,
v.z,
]);
this.geometry['verticesNeedUpdate'] = true; // maybe i don't need that
this.geometry.computeBoundingSphere();
this.computeLineDistances();
}
constructor( start: Vector3, end: Vector3 ) {
super();
// create geometry
const geometry = new LineGeometry();
geometry.setPositions([
start.x, start.y, start.z,
end.x, end.y, end.z
]);
this.geometry = geometry;
// create material
const material = new LineMaterial({
color: 0x000000,
linewidth:5,
depthTest: false, // new
vertexColors: false,
});
this.material = material;
this.computeLineDistances();
this.scale.set( 1, 1, 1 );
this.line3.set(start, end);
}
}
/**
* Translates a line base on DragControls position.
* line: The NewLine
* position: The new Vector3 position from dragControls
*/
translateLineFromVector(line: NewLine, position: Vector3) {
const v1 = line.start;
const v2 = line.end;
const offset_start = line.start.clone().add( position ).sub(line.position);
const offset_end = line.end.clone().add( position ).sub(line.position);
line.start.copy( offset_start );
line.end.copy( offset_end );
}
Standard approach for dragging:
On mouse down(X,Y):
If Clicked at object and not Dragging:
Dragging = True
X0 = X
Y0 = Y
//important - remember object coordinates at this moment!
V1_0 = V1
V2_0 = V2
On mouse moving (X,Y) :
If Dragging:
//Draw object at coordinates shifted by (X - X0), (Y - Y0)
//from initial position, not from the current one
V1.X = V1_0.X + X - X0
V1.Y = V1_0.Y + Y - Y0
same for V2
DrawObject(V1,V2)
On Mouse Up(X,Y):
If Dragging:
Fragging = False
Fix positions V1,V2 if needed
I am trying to figure out where a bunch of line-segments clip into a window around them. I saw the Liang–Barsky algorithm, but that seems to assume the segments already clip the edges of the window, which these do not.
Say I have a window from (0,0) to (26,16), and the following segments:
(7,6) - (16,3)
(10,6) - (19,6)
(13,10) - (21,3)
(16,12) - (19,14)
Illustration:
I imagine I need to extend the segments to a certain X or Y point, till they hit the edge of the window, but I don't know how.
How would I find the points where these segments (converted to lines?) clip into the edge of the window? I will be implementing this in C#, but this is pretty language-agnostic.
If you have two line segments P and Q with points
P0 - P1
Q0 - Q1
The line equations are
P = P0 + t(P1 - P0)
Q = Q0 + r(Q1 - Q0)
then to find out where they intersect after extension you need to solve the following equation for t and r
P0 + t(P1 - P0) = Q0 + r(Q1 - Q0)
The following code can do this. ( Extracted from my own code base )
public static (double t, double r )? SolveIntersect(this Segment2D P, Segment2D Q)
{
// a-d are the entries of a 2x2 matrix
var a = P.P1.X - P.P0.X;
var b = -Q.P1.X + Q.P0.X;
var c = P.P1.Y - P.P0.Y;
var d = -Q.P1.Y + Q.P0.Y;
var det = a*d - b*c;
if (Math.Abs( det ) < Utility.ZERO_TOLERANCE)
return null;
var x = Q.P0.X - P.P0.X;
var y = Q.P0.Y - P.P0.Y;
var t = 1/det*(d*x - b*y);
var r = 1/det*(-c*x + a*y);
return (t, r);
}
If null is returned from the function then it means the lines are parallel and cannot intersect. If a result is returned then you can do.
var result = SolveIntersect( P, Q );
if (result != null)
{
var ( t, r) = result.Value;
var p = P.P0 + t * (P.P1 - P.P0);
var q = Q.P0 + t * (Q.P1 - Q.P0);
// p and q are the same point of course
}
The extended line segments will generally intersect more than one box edge but only one of those intersections will be inside the box. You can check this easily.
bool IsInBox(Point corner0, Point corner1, Point test) =>
(test.X > corner0.X && test.X < corner1.X && test.Y > corner0.Y && test.Y < corner1.Y ;
That should give you all you need to extend you lines to the edge of your box.
I managed to figure this out.
I can extend my lines to the edge of the box by first finding the equations of my lines, then solving for the X and Y of each of the sides to get their corresponding point. This requires passing the max and min Y and the max and min X into the following functions, returning 4 values. If the point is outside the bounds of the box, it can be ignored.
My code is in C#, and is making extension methods for EMGU's LineSegment2D. This is a .NET wrapper for OpenCv.
My Code:
public static float GetYIntersection(this LineSegment2D line, float x)
{
Point p1 = line.P1;
Point p2 = line.P2;
float dx = p2.X - p1.X;
if(dx == 0)
{
return float.NaN;
}
float m = (p2.Y - p1.Y) / dx; //Slope
float b = p1.Y - (m * p1.X); //Y-Intercept
return m * x + b;
}
public static float GetXIntersection(this LineSegment2D line, float y)
{
Point p1 = line.P1;
Point p2 = line.P2;
float dx = p2.X - p1.X;
if (dx == 0)
{
return float.NaN;
}
float m = (p2.Y - p1.Y) / dx; //Slope
float b = p1.Y - (m * p1.X); //Y-Intercept
return (y - b) / m;
}
I can then take these points, check if they are in the bounds of the box, discard the ones that are not, remove duplicate points (line goes directly into corner). This will leave me with one x and one y value, which I can then pair to the corresponding min or max Y or X values I passed into the functions to make 2 points. I can then make my new segment with the two points.
Wiki description of Liang-Barsky algorithm is not bad, but code is flaw.
Note: this algorithm intended to throw out lines without intersection as soon as possible. If most of lines intersect the rectangle, then approach from your answer might be rather effective, otherwise L-B algorithm wins.
This page describes approach in details and contains concise effective code:
// Liang-Barsky function by Daniel White # http://www.skytopia.com/project/articles/compsci/clipping.html
// This function inputs 8 numbers, and outputs 4 new numbers (plus a boolean value to say whether the clipped line is drawn at all).
//
bool LiangBarsky (double edgeLeft, double edgeRight, double edgeBottom, double edgeTop, // Define the x/y clipping values for the border.
double x0src, double y0src, double x1src, double y1src, // Define the start and end points of the line.
double &x0clip, double &y0clip, double &x1clip, double &y1clip) // The output values, so declare these outside.
{
double t0 = 0.0; double t1 = 1.0;
double xdelta = x1src-x0src;
double ydelta = y1src-y0src;
double p,q,r;
for(int edge=0; edge<4; edge++) { // Traverse through left, right, bottom, top edges.
if (edge==0) { p = -xdelta; q = -(edgeLeft-x0src); }
if (edge==1) { p = xdelta; q = (edgeRight-x0src); }
if (edge==2) { p = -ydelta; q = -(edgeBottom-y0src);}
if (edge==3) { p = ydelta; q = (edgeTop-y0src); }
if(p==0 && q<0) return false; // Don't draw line at all. (parallel line outside)
r = q/p;
if(p<0) {
if(r>t1) return false; // Don't draw line at all.
else if(r>t0) t0=r; // Line is clipped!
} else if(p>0) {
if(r<t0) return false; // Don't draw line at all.
else if(r<t1) t1=r; // Line is clipped!
}
}
x0clip = x0src + t0*xdelta;
y0clip = y0src + t0*ydelta;
x1clip = x0src + t1*xdelta;
y1clip = y0src + t1*ydelta;
return true; // (clipped) line is drawn
}
In this answer to my recent question, there is some code that draws a graph, but I can't manage to edit it into something that accepts any list of points as a parameter.
I'd like the Drawing method to accept these parameters:
List of Vector2, Point or VertexPositionColor, I can work with whichever.
Offset for the whole graph
These optional requirements would be appreciated:
Color that may override VertexPositionColor's color and apply to all points.
Size of the graph, so it can be shrunk or expanded, either as Vector2 as multiplier, or Point as target size. Maybe even combine this with offset in Rectangle.
And if it's possible, I'd like to have it all in a class, so graphs can be used separately from each other, each with its own Effect.world matrix, etc.
Here is that code (by Niko Drašković):
Matrix worldMatrix;
Matrix viewMatrix;
Matrix projectionMatrix;
BasicEffect basicEffect;
VertexPositionColor[] pointList;
short[] lineListIndices;
protected override void Initialize()
{
int n = 300;
//GeneratePoints generates a random graph, implementation irrelevant
pointList = new VertexPositionColor[n];
for (int i = 0; i < n; i++)
pointList[i] = new VertexPositionColor() { Position = new Vector3(i, (float)(Math.Sin((i / 15.0)) * height / 2.0 + height / 2.0 + minY), 0), Color = Color.Blue };
//links the points into a list
lineListIndices = new short[(n * 2) - 2];
for (int i = 0; i < n - 1; i++)
{
lineListIndices[i * 2] = (short)(i);
lineListIndices[(i * 2) + 1] = (short)(i + 1);
}
worldMatrix = Matrix.Identity;
viewMatrix = Matrix.CreateLookAt(new Vector3(0.0f, 0.0f, 1.0f), Vector3.Zero, Vector3.Up);
projectionMatrix = Matrix.CreateOrthographicOffCenter(0, (float)GraphicsDevice.Viewport.Width, (float)GraphicsDevice.Viewport.Height, 0, 1.0f, 1000.0f);
basicEffect = new BasicEffect(graphics.GraphicsDevice);
basicEffect.World = worldMatrix;
basicEffect.View = viewMatrix;
basicEffect.Projection = projectionMatrix;
basicEffect.VertexColorEnabled = true; //important for color
base.Initialize();
}
And the drawing method:
foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes)
{
pass.Apply();
GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionColor>(
PrimitiveType.LineList,
pointList,
0,
pointList.Length,
lineListIndices,
0,
pointList.Length - 1
);
}
The Graph class that does the requested can be found here.About 200 lines of code seemed too much to paste here.
The Graph is drawn by passing a list of floats (optionally with colors) to its Draw(..) method.
Graph properties are:
Vector2 Position - the bottom left corner of the graph
Point Size - the width (.X) and height (.Y) of the graph. Horizontally, values will be distributed to exactly fit the width. Vertically, all values will be scaled with Size.Y / MaxValue.
float MaxValue - the value which will be at the top of the graph. All off the chart values (greater than MaxValue) will be set to this value.
GraphType Type - with possible values GraphType.Line and GraphType.Fill, determines if the graph will be drawn line only, or bottom filled.
The graph is drawn with a line list / triangle strip.
I have a program that I'm making with others and I ran into a problem. I'm working on adding in polygon models into our scene in an XNA window. I have that part complete. I also have bounding spheres(I know I tagged as bounding-box but there is no bounding sphere tag) drawing around each polygon. My problem is when I move the polygons around the 3D space the bounding spheres move twice as much as the polygons. I imagine its something within my polygon matrices that I use to create the bounding sphere that makes it move twice as much but that is only speculation.
So just to clarify I'll give you an example of my problem. If I hold down D to move a polygon along the X axis. (model.position.X--;) The polygon moves as expected to but the bounding sphere around the polygon moves twice as much. Thanks for the help guys!
Here is how I draw the models and the bounding spheres:
public void Draw(Matrix view, Matrix projection, bool drawBoundingSphere)
{
Matrix translateMatrix = Matrix.CreateTranslation(position);
Matrix worldMatrix = translateMatrix * Matrix.CreateScale(scaleRatio);
foreach (ModelMesh mesh in model.Meshes)
{
foreach (BasicEffect effect in mesh.Effects)
{
effect.World = worldMatrix * modelAbsoluteBoneTransforms[mesh.ParentBone.Index];
effect.View = view;
effect.Projection = projection;
effect.EnableDefaultLighting();
effect.PreferPerPixelLighting = true;
}
mesh.Draw();
if (drawBoundingSphere)
{
// the mesh's BoundingSphere is stored relative to the mesh itself.
// (Mesh space). We want to get this BoundingSphere in terms of world
// coordinates. To do this, we calculate a matrix that will transform
// from coordinates from mesh space into world space....
Matrix world = modelAbsoluteBoneTransforms[mesh.ParentBone.Index] * worldMatrix;
// ... and then transform the BoundingSphere using that matrix.
BoundingSphere sphere = BoundingSphereRenderer.TransformBoundingSphere(mesh.BoundingSphere, world);
// now draw the sphere with our renderer
BoundingSphereRenderer.Draw(sphere, view, projection);
}
}
And here is the BoundingSphereRenderer Code:
private static VertexBuffer vertexBuffer;
private static BasicEffect effect;
private static int lineCount;
public static void Initialize(GraphicsDevice graphicsDevice, int sphereResolution)
{
// create our effect
effect = new BasicEffect(graphicsDevice);
effect.LightingEnabled = false;
effect.VertexColorEnabled = true;
// calculate the number of lines to draw for all circles
lineCount = (sphereResolution + 1) * 3;
// we need two vertices per line, so we can allocate our vertices
VertexPositionColor[] vertices = new VertexPositionColor[lineCount * 2];
// compute our step around each circle
float step = MathHelper.TwoPi / sphereResolution;
// used to track the index into our vertex array
int index = 0;
//create the loop on the XY plane first
for (float angle = 0f; angle < MathHelper.TwoPi; angle += step)
{
vertices[index++] = new VertexPositionColor(new Vector3((float)Math.Cos(angle), (float)Math.Sin(angle), 0f), Color.Blue);
vertices[index++] = new VertexPositionColor(new Vector3((float)Math.Cos(angle + step), (float)Math.Sin(angle + step), 0f), Color.Blue);
}
//next on the XZ plane
for (float angle = 0f; angle < MathHelper.TwoPi; angle += step)
{
vertices[index++] = new VertexPositionColor(new Vector3((float)Math.Cos(angle), 0f, (float)Math.Sin(angle)), Color.Red);
vertices[index++] = new VertexPositionColor(new Vector3((float)Math.Cos(angle + step), 0f, (float)Math.Sin(angle + step)), Color.Red);
}
//finally on the YZ plane
for (float angle = 0f; angle < MathHelper.TwoPi; angle += step)
{
vertices[index++] = new VertexPositionColor(new Vector3(0f, (float)Math.Cos(angle), (float)Math.Sin(angle)), Color.Green);
vertices[index++] = new VertexPositionColor(new Vector3(0f, (float)Math.Cos(angle + step), (float)Math.Sin(angle + step)), Color.Green);
}
// now we create the vertex buffer and put the vertices in it
vertexBuffer = new VertexBuffer(graphicsDevice, typeof(VertexPositionColor), vertices.Length, BufferUsage.WriteOnly);
vertexBuffer.SetData(vertices);
}
public static void Draw(this BoundingSphere sphere, Matrix view, Matrix projection)
{
if (effect == null)
throw new InvalidOperationException("You must call Initialize before you can render any spheres.");
// set the vertex buffer
effect.GraphicsDevice.SetVertexBuffer(vertexBuffer);
// update our effect matrices
effect.World = Matrix.CreateScale(sphere.Radius) * Matrix.CreateTranslation(sphere.Center);
effect.View = view;
effect.Projection = projection;
// draw the primitives with our effect
effect.CurrentTechnique.Passes[0].Apply();
effect.GraphicsDevice.DrawPrimitives(PrimitiveType.LineList, 0, lineCount);
}
public static BoundingSphere TransformBoundingSphere(BoundingSphere sphere, Matrix transform)
{
BoundingSphere transformedSphere;
// the transform can contain different scales on the x, y, and z components.
// this has the effect of stretching and squishing our bounding sphere along
// different axes. Obviously, this is no good: a bounding sphere has to be a
// SPHERE. so, the transformed sphere's radius must be the maximum of the
// scaled x, y, and z radii.
// to calculate how the transform matrix will affect the x, y, and z
// components of the sphere, we'll create a vector3 with x y and z equal
// to the sphere's radius...
Vector3 scale3 = new Vector3(sphere.Radius, sphere.Radius, sphere.Radius);
// then transform that vector using the transform matrix. we use
// TransformNormal because we don't want to take translation into account.
scale3 = Vector3.TransformNormal(scale3, transform);
// scale3 contains the x, y, and z radii of a squished and stretched sphere.
// we'll set the finished sphere's radius to the maximum of the x y and z
// radii, creating a sphere that is large enough to contain the original
// squished sphere.
transformedSphere.Radius = Math.Max(scale3.X, Math.Max(scale3.Y, scale3.Z));
// transforming the center of the sphere is much easier. we can just use
// Vector3.Transform to transform the center vector. notice that we're using
// Transform instead of TransformNormal because in this case we DO want to
// take translation into account.
transformedSphere.Center = Vector3.Transform(sphere.Center, transform);
return transformedSphere;
}