3
$\begingroup$

I realize this has been asked before, so I apologize for the "repeat". I saw this answer, and it mostly solved my issue. However, I can't figure out how to generate my specific equation. This is my equation:

enter image description here

This works from the range -1 <= x <= 1. For the really big equation, the "x" should actually be z, and the "h" should be x (I had to use those to graph it on Desmos). In any case, I tried inputting the first half of the equation with the Blender Add Mesh: Extra Objects > Math Function > Z Math Surface by typing this:

(sqrt(-16*fabs(x)+40*sqrt(fabs(x))*y-16*pow(x,2)-25*pow(y,2)+16)+15*x)/15

The result is an error evaluating the expression. Do I have to set the range somehow? Would it be better to use XYZ Math Surface? (and if so, how?). Or am I just doing something wrong? Thank you in advance.

$\endgroup$
  • 1
    $\begingroup$ Hi Lee. Not sure what you mean by 'two asterisks each'. Can you edit the question and put 4 spaces before the line with the code. This will disable the formatting so that you can enter it as-is. $\endgroup$ – Rich Sedman May 24 '17 at 6:30
  • $\begingroup$ @RichSedman, Got it. I didn't know about adding four spaces. Thanks for the tip, the proper change has been made. $\endgroup$ – Lee Fulf May 26 '17 at 15:28
4
$\begingroup$

As @TLousky quite rightly points out, the problem is due to the evaluation of the equation throwing an exception due to the sqrt(...) function attempting to find the square root of a negative number (which doesn't have a 'real' answer).

To avoid the Z Equation from hitting this problem you can use a conditional assignment.

In Python you can assign a variable based on whether a certain condition is met as follows :

myvar = <value> if <condition> else <othervalue>

Where value is the value to assign if the condition is True and othervalue is the value to assign if the condition is False. The same style of conditional assignment can be used in Blender for the Z Equation.

In your example, the failure occurs because the contents of the initial sqrt(...) function evaluates below zero, so the condition to identify 'valid' values should be :

(-16*fabs(x)+40 * sqrt(fabs(x)) * y - 16 * pow(x,2) - 25 * pow(y,2) + 16)>=0.0 

The value to assign if this condition is True would be :

( sqrt(-16*fabs(x)+40 * sqrt(fabs(x)) * y - 16 * pow(x,2) - 25 * pow(y,2) + 16)  + 15 * x)/15

And the value to assign if the condition is False would be :

(0 + 15 * x)/15

(ie, for values < 0 assume that sqrt(...) is zero)

Putting this all together, the Z Equation should be :

( sqrt(-16*fabs(x)+40 * sqrt(fabs(x)) * y - 16 * pow(x,2) - 25 * pow(y,2) + 16)  + 15 * x)/15 if (-16*fabs(x)+40 * sqrt(fabs(x)) * y - 16 * pow(x,2) - 25 * pow(y,2) + 16)>=0.0 else (0 + 15 * x)/15

And this should produce the following result :

result

$\endgroup$
  • 1
    $\begingroup$ Nice workaround! Quick note though: (0+15*x)/15 equals simply x, so would make the conditional simpler :) $\endgroup$ – TLousky May 25 '17 at 12:48
  • 1
    $\begingroup$ Well, that looks about exactly what I'm looking for. I didn't know you could use conditionals in the equation. That opens so many possibilities! Thanks for the answer! $\endgroup$ – Lee Fulf May 26 '17 at 15:33
  • $\begingroup$ Would it be possible to, instead of having the conditional be that the squared function is greater than 0, that x is less than or equal to 1 and greater than or equal to -1? -1<=x<=1 I'm not sure if that would change the results, but it was how I initially set the function to work. Is that possible by saying [function] if x >= -1 AND x <= 1 else [alt function]? As it stands, I'm not sure why it's slanted like that. I thought it should be flat, perpendicular to the x-axis. $\endgroup$ – Lee Fulf May 26 '17 at 19:09
  • $\begingroup$ You can use conditionals such as that, but equally you can set the range in the X Size and Y Size fields in the tool panel on the left. Setting X Size to 2 would naturally make it range from -1 to 1. Perhaps one of the terms in the equation is incorrect? $\endgroup$ – Rich Sedman May 26 '17 at 19:16
  • $\begingroup$ Is it supposed to be a heart shape? If you remove the last bit you get "( sqrt(-16*fabs(x)+40 * sqrt(fabs(x)) * y - 16 * pow(x,2) - 25 * pow(y,2) + 16))/15 if (-16*fabs(x)+40 * sqrt(fabs(x)) * y - 16 * pow(x,2) - 25 * pow(y,2) + 16)>=0.0 else (0)" which does produce a heart shape on a flat plane. $\endgroup$ – Rich Sedman May 26 '17 at 19:21
6
$\begingroup$

enter image description here

The problem that prevents your equation from working with the Add Mesh: Extra Objects > Math Function > Z Math , is that you're using a sqrt function which is undefined for negative values.

When the math function operator tries to evaluate different values for X and Y, it raises an exception the first time that evaluation fails due to an attempt to sqrt a negative value.

Another option is to write a script of your own to generate the mesh only where the equation provides valid results. I wrote a quick script to do that below. It's incomplete as it only calculates vertices coordinates (result in the image above), and does not generate edges and faces, but it's a start:

import bpy, bmesh
import numpy     as np
from   math      import sqrt
from   itertools import product

# Define X and Y values lower bound, upper bound and number of points
lb, ub, n = ( -30, 30, 1000 )

# Calculate all the possible X-Y combinations and convert to a numpy array
xy = np.array( list( product( np.linspace( lb, ub, n ), np.linspace( lb, ub, n ) ) ) )

# Split X and Y to different arrays and initialize Z array
xs = xy[:,0]
ys = xy[:,1]
zs = []

# Iterate over every x and y value, and try to calculate the z.
# Append result if successful, and nan if not
for x, y in zip( xs, ys ):
    try:
        zs.append(
            ( 
                sqrt( 
                    -16 * abs(x) + 40 * sqrt( abs(x) ) * y - 16 * pow( x, 2 ) - 25 * pow( y, 2 ) + 16 
                ) + 15 * x 
            ) / 15 
        )
    except:
        zs.append( np.nan )

# Drop nan values from all axes
mask = np.logical_not(np.isnan(zs))
xs = xs[ mask ]
ys = ys[ mask ]
zs = np.array( zs )[ mask ]

# Generate mesh
bm = bmesh.new()

# Add vertices
for x, y, z in zip( xs, ys, zs ):
    bm.verts.new( (x,y,z) )

m = bpy.data.meshes.new( "equation" )

bm.to_mesh( m )

o = bpy.data.objects.new( "equation", m )
bpy.context.scene.objects.link( o )

This script uses external, non built-in libraries (namely numpy). You can achieve the same result without numpy, but it makes some of the calculations quicker and easier.

$\endgroup$
  • 1
    $\begingroup$ Ah. I was hoping it would only generate for valid values and simply ignore invalids. Guess it's not the TI-89 I'm used to XD. I'm sadly not familiar with using straight up text in Blender, but I shall endeavor to try. Thanks for your answer, it is most helpful! $\endgroup$ – Lee Fulf May 26 '17 at 15:33

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.