0
$\begingroup$

What I would like to achieve using python is,

Top view,

Z = 0.0

Y = 0.0

Vertex 0 = -1.0

vertex 1 = 1.0

Rotation on y axis (top view)

Point of rotation is either Vertex 0, mid-point or Vertex1

The angle is user specified and point of rotation also (just hard coded)

Line should remain same length when angle is adjusted

When the distance between the 2 vertices is adjusted (I.E. V0 = 0.5 and V1 = 1.5 the angle should remain the same)

In the screenshot I tried to make example

First line (example) is straight 0 degrees

Second line (example) is 45 degrees angle and V0 = -1.0 and V1 = 1

Third line (example) is also 45 degrees angle but v0 = -1 and V1 = 0.

I just want to draw 2 vertices including an edge (3 might also be good so the vertex in the middle can be used as point of rotation) and change the angle of the edge.

But when the distance between the 2 vertices becomes smaller or bigger the angle should remain the same.

Important is that the vertices always stays on the given X axis grid point. Y axis needs to be recalculated to get the specified angle.

enter image description here

old post!!

I would like to ask some help regarding mathutils and vectors (edges) At the moment I use this code to add 2 vertices (1 edge) But I would like to be able to adjust the angle of this line in such a way that no matter the distance between the 2 vertices the angle will always be fixed. I.E. 10 degrees or whatever I input as user. I search this forum and internet but could not find any similar to my question

import bpy
import bmesh
import mathutils
from mathutils import Vector
from bpy.types import Operator
from bpy_extras.object_utils import AddObjectHelper, object_data_add
from bpy.props import FloatVectorProperty    

verts = [(1, 1, 0), (-1, 1, 0)]

mesh = bpy.data.meshes.new("mesh")  # add a new mesh
obj = bpy.data.objects.new("MyObject", mesh)  # add a new object using the mesh

scene = bpy.context.scene
bpy.context.collection.objects.link(obj)  # put the object into the scene (link)
bpy.context.view_layer.objects.active = obj   # set as the active object in the scene
bpy.context.active_object.select_set(state=True)  # select object

mesh = bpy.context.object.data
bm = bmesh.new()

for v in verts:
    bm.verts.new(v)  # add a new vert

bm.edges.new(bm.verts)




# make the bmesh the object's mesh
bm.to_mesh(mesh)  
bm.free()  # always do this when finished

The code below wil just draw a straight line from 1 to -1 (x axis) I would like to get an angle like the picture

enter image description here

it is important that the vertices stay in same line (x axis) as seen on the screenshot. So rotating the line would not give the result I am after.

As seen in the screenshot V1 and V2 are still on the same grid line in the x direction

$\endgroup$
  • 1
    $\begingroup$ 10 degrees between what and what? $\endgroup$ – Robin Betts Dec 26 '19 at 11:26
  • $\begingroup$ 10 degrees between the first vertex and the second one. Then when an edge is drawn between the 2 vertices the line is under a 10 degree slope $\endgroup$ – cexoso Dec 26 '19 at 11:33
  • 1
    $\begingroup$ ..Sorry if I'm being dim.. but you can have 10 degrees between two edges .. even then, you would have to say which plane you would like the 3 vertices to lie in? $\endgroup$ – Robin Betts Dec 26 '19 at 11:49
  • $\begingroup$ x or y. It depends on how the vectors are drawn. the code example I gave is in the X plane. (top view) $\endgroup$ – cexoso Dec 26 '19 at 11:53
  • $\begingroup$ To clarify could you please whip out the grease pencil to demonstrate the angle? A locus of a cone one vert to other?. $\endgroup$ – batFINGER Dec 26 '19 at 13:00
1
$\begingroup$

As far as i understand you seek for this. one way to do this is using the matrix.Rotation the only thing i dont know and maybe @batFINGER or @Robin Betts can help in this is to extend the line to the asked X axis. As far as i get it @cexoso wants to "constrain" the vertices on the X axis. with other words the vertices are free to move on the Y axis and are fixed on the X axis respecting the given angle.

@batFINGER or @Robin Betts May i ask you to edit this code so that the line will be extended on both sides to the X axis. I'll try to explain using an square. Screenshot is below the code.

Please note that in this example the outside vextors are drawn on -1.0 and 1.0 X axis I added a third vector but it might not be needed.

import bpy
import bmesh
import mathutils
from mathutils import Vector
from bpy.types import Operator
from bpy_extras.object_utils import AddObjectHelper, object_data_add
from bpy.props import FloatVectorProperty    
from mathutils import Matrix
from math import asin, pi, degrees, radians


angle = 10
mesh = bpy.data.meshes.new("mesh")  # add a new mesh
obj = bpy.data.objects.new("MyObject", mesh)  # add a new object using the mesh

scene = bpy.context.scene
bpy.context.collection.objects.link(obj)  # put the object into the scene (link)
bpy.context.view_layer.objects.active = obj   # set as the active object in the scene
bpy.context.active_object.select_set(state=True)  # select object

mesh = bpy.context.object.data
bm = bmesh.new()
test = [] 

V3 = bm.verts.new((1, 0, 0))
V2 = bm.verts.new((0, 0, 0))
V1 = bm.verts.new((-1, 0, 0))

bm.edges.new((V1, V2))
bm.edges.new((V2, V3))

bmesh.ops.rotate(   
    bm, verts=bm.verts, matrix=Matrix.Rotation(pi * angle / 180, 3, 'Z')) 

bm.to_mesh(mesh)  
bm.free()  # always do this when finished

Text continues below screenshot

enter image description here

Note 1 On any given angle the edge should connect to the square I dont know how to do this tbh. Im still learning lOL

Hope my answer contributes to this question

|improve this answer|||||
$\endgroup$
1
$\begingroup$

Projection onto plane

Going by result suggested by @DGRL I believe what you want to do is a vector projection. An example of this in blender is the shrinkwrap modifier.

For demonstration I've added an XZ plane, and projected the edge onto it along the Y axis. A driver slowly rotates the plane about z axis. It glitches at 90 degrees (infinity result as expected)

See that all the edges created from scripts below, project to the one result, when modifier display enabled, as the plane spins about z axis.

enter image description here

For projecting vector onto any plane simply define location and plane normal. In this case the location is (0, 0, 0) and the normal the Y-axis (0, 1, 0).

For demonstration can also build the projection matrix from rotate and scale. Script below scales each input edge to the length of the hypotenuse of triangle with angle and edge length length. (remember cos is adjacent over hypotenuse in a right angle triangle)

Have hard coded in the rotate as 'Z' axis to match the question. (View is immaterial, its project along Y axis, rotate about Z)

For any case, using the cross product of the edge vector v and the projection vector, Y axis, will give the axis rotation, a vector normal to the plane containing v and y axis.

>>> Vector((2, 0, 0)).cross(Vector((0, 1, 0))).normalized()
Vector((0.0, 0.0, 1.0))

axis of rotation as mentioned in my previous comment x or y. It depends on how the vectors are drawn. the code example I gave is in the X plane. (top view)

How would I know it's in top view from code? it simply makes one edge, .. the axis of rotation is Z (the being in top view part) and a line falls on infinite plane, not solely the x=0 plane.

Script Creates an edge for each rotation. from -75 to 75 degrees in 15 degree steps

enter image description here Result

import bpy
import bmesh
from mathutils import Vector, Matrix
from math import radians, cos


bm = bmesh.new()

    verts = ((-1, 0, 0),
             (1, 0, 0))

for deg in range(-75, 76, 5):
    angle = radians(deg)
    e = bm.edges.new(bm.verts.new(v) for v in verts)
    p = sum([v.co for v in e.verts], Vector()) / 2

    R = (Matrix.Translation(p) @

         Matrix.Rotation(angle, 4, 'Z') @
         Matrix.Diagonal((1 / cos(angle),) * 4) @
         Matrix.Translation(-p))

    bmesh.ops.transform(bm, 
            verts=e.verts,
            matrix=R,
            )

    '''
    # alternately 
    bmesh.ops.rotate(bm,
            verts=e.verts, 
            cent=p,
            matrix=Matrix.Rotation(angle, 3, 'Z'),
            ) 
    bmesh.ops.scale(bm,
            verts=e.verts,
            vec=(1 / cos(angle),) * 3,
            space=Matrix.Translation(-p)
            )
    '''
mesh = bpy.data.meshes.new("Edge")

bm.to_mesh(mesh) 
obj = bpy.data.objects.new("Edge", mesh)  
bpy.context.collection.objects.link(obj)  

or similarly using acosfor getting angle from scale, Rotated a set of the scaled (1x, 2x, 4x ...) input edges such that they are original if projected back to XZ plane along Y

for scale in (1, 2, 4, 8, 16, 32):

    e = bm.edges.new(bm.verts.new(v) for v in verts)
    p = sum([v.co for v in e.verts], Vector()) / 2

    angle = acos(1 / scale)
    print(scale, degrees(angle))
    R = (Matrix.Translation(p) @

         Matrix.Rotation(angle, 4, 'Z') @
         Matrix.Diagonal((scale,) * 4) @
         Matrix.Translation(-p))

    bmesh.ops.transform(bm, 
            verts=e.verts,
            matrix=R,
            )
|improve this answer|||||
$\endgroup$
  • $\begingroup$ Its a nice script but needs a few modifications to meet my needs I can use it though $\endgroup$ – cexoso Dec 27 '19 at 20:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.