Justin Israel

 




I had just received my brand new MacBook Pro a few days ago. Amazing machine. Probably the best laptop I have ever laid my hands upon. It was the early Feb 2011 model so I got a crazy good deal. But something caught my eye that I just had to investigate…

I’m a bit obsessive when it comes to small issues that I can’t resolve, and this is just still bothering me. I noticed that when I smooth scroll (trackpad or smooth wheel logitech mouse) on such content as webpages, or Mail, that there is a flicker in the display of text and context. It depends on the size and orientation of the content, whether it will flicker more or less. My eyes just couldn’t ignore it and I figured it couldn’t possibly be normal functionality. Thus, step one of my problem solving began: Google search.

My search turned up a number of similar complaints to both Macbook Pro and Air models, such as this discussion: https://discussions.apple.com/thread/2645516?start=0&tstart=0
It seemed the problem was not just limited to my specific early 2011 Macbook Pro. Suggestions ranged from resetting the PRAM (holding cmd+option+p+r for a couple of reboot cycles), to toggling the screen through display resolutions, to adjusting brightness of the display. Nothing seemed to make much of a difference to me.

Inversion (pixel-walk)

Googling also turned up a reference to this site, which offers various types of LCD tests: http://www.lagom.nl/lcd-test/inversion.php

According to this site, its normal to have a slight flicker in one box. And heavier flickering suggests voltage alignment issues in the LCD display. Using this test, I tried it on a number of Apple product configurations. Here is a collection of my findings:

DeviceDisplayScrollingNot Scrolling
Macbook Pro (early 2011)15" 1440×900Heavy multi-color flickering in all boxesAt least 2 boxes always lightly flickering
Mac Pro MacPro4,1 (2009)24" Cinema DisplayLight amount of flickering in all boxesNo flicker
Mac Pro MacPro5,1 (2010)27" LED Cinema DisplayNo flickerVery light flicker in box 7a
Inversion (pixel-walk) test results on LCD displays

Apple Support

After speaking to support over the phone, they suggested that I go into the store for more help. When I got to the store and started speaking with a tech at the Genius Bar, he had never heard of this issue before. But once I showed him an example on both news.google.com, and in my Apple Mail, he definitely acknowledged that its noticeable. He then went off into the back to research the issue a bit.
When the tech came back he said that he had found no outstanding information from Apple about this issue. A second tech even came and looked at the issue, and had no explanation for it.
I went around the store and checked web page scrolling on 13″ MacBook Airs, and 13″, 15″, and 17″ MacBook Pro models, with and without the higher resolution LCD display options. All models exhibited the same flicker during scrolling. My final recommendation from the stumped Apple tech was that it could be an issue with Lion and its rendering of fonts, or whatever, and that I could either return my laptop, or hold out for some kind of fix from Apple. Basically, no idea.

My question is… Am I being overly sensitive to this display flicker? I figure I can’t be the only one, as per the discussion lists of other users that notice the problem. Some of my friends with the same laptop said they have never really noticed until I pointed it out. I just wonder why such a fantastic laptop would exhibit this visual artifact, and whether it is something I should just accept as being normal?

Do you have this problem and is it noticeable? Post your feedback!

Are you affected at all by flicker when scrolling on a Macbook?

View Results

Loading ... Loading ...
 

A question came up in the Maya-Python mailing list that I thought was a really good topic, and should be reposted.

Someone asked how you can create maya UI objects and embed them within your main PyQt application. Specifically he wanted to create a modelPanel and embed it so that he would have a camera view within his own PyQt window.

Here is my example of how to achieve this…

from PyQt4 import QtCore, QtGui

import maya.cmds as cmds
import maya.OpenMayaUI as mui

import sip

global app


class MyDialog(QtGui.QDialog):

    def __init__(self, parent, **kwargs):
        super(MyDialog, self).__init__(parent, **kwargs)
        
        self.setObjectName("MyWindow")
        self.resize(800, 600)
        self.setWindowTitle("PyQt ModelPanel Test")

        self.verticalLayout = QtGui.QVBoxLayout(self)
        # need to set a name so it can be referenced by maya node path
        self.verticalLayout.setObjectName("mainLayout")
        # First use SIP to unwrap the layout into a pointer
        # Then get the full path to the UI in maya as a string
        layout = mui.MQtUtil.fullName(long(sip.unwrapinstance(self.verticalLayout)))
        cmds.setParent(layout)

        self._cameraName = cmds.camera()[0]
        nodeName = cmds.modelPanel(cam=self._cameraName)
        # Find a pointer to the modelPanel that we just created
        ptr = mui.MQtUtil.findControl(nodeName)
        # Wrap the pointer into a python QObject
        self.modelPanel = sip.wrapinstance(long(ptr), QtCore.QObject)

        # add our QObject reference to the modelPanel to our layout
        self.verticalLayout.addWidget(self.modelPanel)

    def show(self):
        super(MyDialog, self).show()
        # maya can lag in how it repaints UI. Force it to repaint
        # when we show the window.
        self.modelPanel.repaint()
                    

def show():
    global app
    # use a shared instance of QApplication
    app = QtGui.QApplication.instance()

    # get a pointer to the maya main window
    ptr = mui.MQtUtil.mainWindow()
    # use sip to wrap the pointer into a QObject
    win = sip.wrapinstance(long(ptr), QtCore.QObject)
    d = MyDialog(win)
    d.show()

    return d

You need sip and the MQtUtil functions to convert between maya node paths and python Qbjects. Its the same idea as having to use those functions to get a reference to the maya MainWindow, in order to parent your dialog.

 

Second video in the python for maya series, just released through cmiVFX!

Python For Maya – Volume 2

If you watched the first video, you now have a good grasp on Python. Sweet. Let’s plow through some more involved concepts like python juggernauts!

With a working knowledge of the python scripting language, and the Maya Python commands API, we can continue to learn new ways to solve more challenging problems, create complete scripts, and build user interfaces around our tools. We also introduce the Maya Python API; a lower-level interface into Maya.

This video focuses more on breaking down full scripts, as opposed to typing out syntax. Its jam packaged with information and moves fast to deliver you as much brain food as possible. The first segment of the video transitions from beginning to intermediate level, with the majority of the video being intermediate, and finishing out by touching on advanced concepts. The included project files are abundant, complete, and full of helpful documentation so that you can take your time and learn about each piece of the tools.

If you check it out, leave me feedback!
http://cmivfx.com/tutorials/view/328/Python+For+Maya+Vol+02

First video can be found here

 

This is a follow up post to my previous one on Installing PyQt4 for Maya 2011

Recently while putting together my next video tutorial for Python for Maya, I came to a section where I wanted to demo PyQt4 in Maya2012. But I was concerned that viewers would have to go through the complicated steps of building PyQt4. I noticed that other people have made available precompiled PyQt installers for windows (here) but I could not find any for OSX or linux. So I decided to put together a build.

I created a new project on github called MyQt4
https://github.com/justinfx/MyQt4

Its a Makefile for completely downloading and building PyQt4 for maya, and generating a .pkg installer. Hopefully someone can contribute improvements since I dont have a ton of experience writing makefiles, and also that someone might create a linux version.

Here is a link to the latest pkg build:

Snow Leopard:  MyQt4.8.6-maya2012-x64-osx-10.6.pkg

Lion:  MyQt4.8.6-maya2012-x64-osx-10.7.pkg   (Thanks Chris!)

Here are builds other people have made:

 

Just released my first online video tutorial, through cmiVFX

Python Introduction Vol 01 – Maya

Amazing at Animation? Master of Modeling? Conquistador of Character Rigging?

But how is your Python?

This course brings the talented artist into the fold of the technical-side of Maya. Learn the basics of Python, and its place in your 3D workflow, with visual examples and real world problems. Get a kick-start on adding some automation into your life, and solving common problems in a fraction of the time. By the end of this video, you should have a deeper understanding of one of the languages Maya speaks under the hood, and how to start viewing your scenes in terms of glorious Python code!

Check it out: http://cmivfx.com/tutorials/view/320/Python+Introduction+Vol+01+-+Maya

If you check out this course, please leave me some feedback! I would love to hear your thoughts.
Stay tuned for more installments to come!

 

Preface

I’ve been programming in python for over 5 years now, and I love the language a lot. I would look for any opportunity to accomplish my coding tasks using python, as opposed to learning new languages. PyQt4 for user interfaces, Django for web design, etc… And when python just isn’t an option, I have done my share of php, javascript, objective-c and so on.
Recently I started thinking that I should probably expand my programming knowledge a bit to make myself more marketable. While python is pretty key in the world of Visual Effects pipelines… so is C++. Having previously focused on being a Compositor and not a programmer, I had always told myself that if I needed to learn C++, then I was going too far in that direction. But now that I have been working in pipeline development for over 2.5 years, it has become clear that I really should explore the world of compiled languages.

Grabbing some books on C+, I dove in. Yuk. Why is it so freaking boring? It really does suck trying to learn C++ after having been so spoiled with python for so long. Its like taking a Aborigine and trying to turn him into a snooty English gentleman. Hmm…Is that right? Well whatever. Its completely disorienting. So many things that I never had to think about, like type declarations, memory management, pointers… But I kept on reading and learning.

Then I came across a post on Google+, by someone that actually works at Google, mentioning a language being developed in-house. So I started reading about Go @ http://golang.org/

The Go Language

From the standpoint of a python programmer, Go feels like it sits right between C/C++ and python. You get the simpler syntax, but with the speed of a compiled language. Because Go has garbage collection, memory management isn’t a concern. I was never used to the code/compile/test/repeat pattern before, but it compiles so fast and is so easy to set up a project that it feels pretty natural. Go doesn’t require you to have header files and declare everything in advance, so banging out a simple program is quite fast and only slightly more overhead than writing a python script. Just have to add the step of compiling it. As far as the library, so far I have found everything pretty useful. And it seems that every time I search for a Go binding for something, I find one. I will go into more detail on that in a bit.

One thing that will make python programmers feel more at home is the type inference while creating a variable. While you can do something like this in Go:  

var myString string = “Foo”

you can also type the same thing like this:  

myString := “Foo”

:= operator lets you initialize a new variable and makes the compiler figure out what type it should be, based on the return type of the right hand side.

Pointers are still kind of strange for python programmers, but Go is a lot more flexible about using them. You don’t always have to explicitly dereference them like   (*myPointer).myFunction(). It just does it for you when you access member functions and attributes:  myPointer.myFunction().

Having no type inheritance is also a bit different as well. Instead of creating base abstract classes, and subclassing them, you only have structs… no classes. But you share functionality by using interfaces. An interface is just a definition of methods. If any object implements those methods, its consider that type of interface. This is something I have yet to really get into, since my current first project is more of a cmd program, rather than a pkg library. I’m sure I could be using interfaces already, but it hasn’t quite felt natural enough to incorporate as of yet.

A pretty crazy aspect of Go is its native support for concurrency using what they call “Goroutines”. The closest way I have been able to compare it to my experience in python is while using ZeroMQ for messaging. ZeroMQ promotes not only using its library for messaging, but also to replace issues with threads and locks. It has similar concepts in promoting concurrency. You divide your program up into its components and instead of sharing data structures between them, you communicate over channels (sockets in ZeroMQ). When you fire off a Goroutine, you aren’t waiting for it anymore. It can run and do its thing and you keep on going. You can then send data back and forth with channels, and even use them just for signaling, like saying “ok now exit”.

Actual Usage

I’ve been writing a message server so far in python, using the Tornado web server, along with some socket.io bindings called TornadIO, and also ZeroMQ for internal communication. So far its been working pretty well, but there are a lot of complex layers, with ZeroMQ sort of riding on top of Tornados ioloop. I decided to try and rewrite this server in Go. Turns out Go has a lot of built-in support for doing exactly what this python server was doing. The whole web-socket server functionality is part of the standard http library module. I quickly found Go bindings for socket.io and I was on my way.

It was quite fast to get the server to the point of doing global messages, but now I had to think about implementing the support for channel subscriptions. My first instinct was to go grab ZeroMQ and its bindings again, or to use Redis for the messaging, but then I was thinking “Shouldn’t I be able to do all this with Go’s concurrency?”. One thing that really helped me out was how fast everyone responds on the golang-nuts discussion group. It was quickly pointed out by more than one person that I should definitely be able to accomplish the internal message routing purely in Go. And they were right. I just set up a “dispatcher” function and run it in a loop as a goroutine, and then pass messages in and out of it. The dispatcher manages its data structure, and no other part of the code accesses it directly.

So far, this Go server is turning out great, and I’m excited by the fact that its compiled and faster. I don’t have to distribute my source code now :-)

Performance

* Disclaimer: These numbers are just comparisons between what I built in python vs Go. I think the point is to reflect what I naturally came up with on my first pass at using Go, vs applying years of python experience. I’m dead sure my Go code isn’t written as efficiently as someone with more experience, which I think makes it even more interesting of a comparison.

Go comes with built in testing and benchmarking functionality. What I built was a client test that connects to a running server and rapid fires messages. It times how long it takes for a 150 byte message to be sent out, flow through the server, and come back to that client as been delivered. The gotest utility that is used to run the test code will run the test, and if it ran too fast to calculate timings, it will repeat the test over and over with larger iterations. When I first got my Go server working to where a client would send a message and it would just get broadcasted right back out to everyone, I ran a benchmark. Here are the results of my tests…

TESTTIME PER MSGNET RESULT
Python (Tornado, …)835314 ns/op-
Go (barebones messaging)107091 ns/op7.8x faster
Go (1-to-1 python port)159823 ns/op5.2x faster
Go (weekly.12-01-2011)76230 ns/op11x faster

 

The second Go test was after I finished implementing the same 1-to-1 feature set of the python version. I had thought the numbers would be dramatically impacted after adding the overhead of all the internal message routing, but the Go server still came out almost 5x faster than the python version. And this is from my first attempt at writing a Go program! I bet once I get a lot more solid with the language I can optimize this code a lot more.

Conclusion

I’m pretty hooked on the language. I feel its the perfect option for a python programmer that wants some speed increases and simple concurrency, without having to learn something as intense as C++. Go is supposed to get faster and faster as they improve things like the goroutines, channels, and the garbage collector, so its a great time to jump in and start learning. Its really helped me understand more formal concepts that will probably make learning C++ even easier once I decide to go back to learning it :-)

 

 

Update: Installing pyqt4 for maya2012

Personally, when trying to run PyQt from within Maya 2009/2010 using the pumpThread method, I never had much luck. The best I ever got was the ability to bring up a dialog but not without locking up the UI, even though the pumpThread tool is meant to address that.

Anyways, when I found out Maya 2011 was rewritten based on Qt for the UI, I was really stoked. I saw the example video of being able to design a ui file in Designer, and just directly open it in a maya script, and all I could think about was designing Qt GUIs so much more easily now. Turns out that Maya 2011 didn’t actually ship with PyQt included for licensing reasons I’m sure. But it included documentation on how one could go about building PyQt for maya. Unfortunately I had tons of issues that caused maya to just crash when importing PyQt.

What I finally figured out was a mish-mash of information from the maya documention, and different forums and user groups. So I decided to make this easier on anyone having the same problems as I did, and just collect that information into one place. This process is for OSX. I’m sure most of it is probably still relevant to linux or win, except for the last parts with ‘install_name_tool’. You would just need to make sure to find the right Qt/PyQt/SIP packages for your OS.

Building PyQt4 for Maya 2011 on OSX

Update for Maya 2012

While Maya uses newer versions, it seems the versions from the 2011 install still work. But here they are anyways incase you want the newer version for 2012:

Make sure you have downloaded and installed the latest XCode from Apple. Its also included on your OSX installation disc.

Qt: Maya has a specific version of Qt built into it. This is Qt 4.5.3.
  1. Download:  qt-mac-opensource-src-4.5.3.tar.gz
  2. Extract:
    tar zxvf qt-mac-opensource-src-4.5.3.tar.gz
    cd qt-mac-opensource-src-4.5.3
  3. Build and install:
    ./configure -cocoa -arch x86_64 -debug-and-release -no-phonon -no-phonon-backend -no-qt3support -no-webkit -nomake docs -nomake examples -nomake demos -nomake translations -no-rpath -no-framework
    make -j 8
    sudo make install
SIP: The maya docs recommend sip version 4.10
  1. Download this specific SIP:   sip-4.10.tar.gz
  2. Extract:
    tar zxvf sip-4.10.tar.gz
    cd sip-4.10
  3. Build and install:
    /Applications/Autodesk/maya2011/Maya.app/Contents/bin/mayapy configure.py –arch=x86_64
    make -j 8
    sudo make install
PyQt4: The maya docs suggest PyQt 4.7
  1. Download this specific PyQt: PyQt-mac-gpl-4.7.3.tar.gz
  2. Extract:
    tar zxvf PyQt-mac-gpl-4.7.3.tar.gz
    cd PyQt-mac-gpl-4.7.3
  3. Set up some environment variables before building:
    export QTDIR=/usr/local/Trolltech/Qt-4.5.3
    export PATH=/usr/local/Trolltech/Qt-4.5.3/bin:$PATH
    export QMAKESPEC=/usr/local/Trolltech/Qt-4.5.3/mkspecs/macx-g++
    export DYLD_LIBRARY_PATH=/usr/local/Trolltech/Qt-4.5.3/lib
  4. Build and install:
    /Applications/Autodesk/maya2011/Maya.app/Contents/bin/mayapy configure.py LIBDIR_QT=/usr/local/Trolltech/Qt‐4.5.3/lib INCDIR_QT=/usr/local/Trolltech/Qt‐4.5.3/include MOC=/usr/local/Trolltech/Qt‐4.5.3/bin/moc -w –no-designer-plugin
    make -j 8
    sudo make install
  5. PyQt4 will now be installed into Maya’s python site-packages, BUT will be linked against the wrong Qt binaries. The maya docs have an annoying multi step set of commands but they don’t copy/paste nicely, so here is a for-loop you can use:
    for mod in Core Gui Svg OpenGL Xml; do sudo find /Applications/Autodesk/maya2011/Maya.app/Contents/Frameworks/
    Python.framework/Versions/Current/lib/python2.6/site-packages/PyQt4 -name "*so" -exec install_name_tool -change libQt${mod}.4.dylib @executable_path/Qt${mod} {} ;; done

At this point you should be able to start up Maya and import and run PyQt from the script editor. You no longer need the pumpThread. Here is a test code snippet that I borrowed from here (the original had typos in it that I corrected)

import sip
import maya.OpenMayaUI as mui
from PyQt4.QtCore import *
from PyQt4.QtGui import *

def getMayaWindow():
ptr = mui.MQtUtil.mainWindow()
return sip.wrapinstance(long(ptr), QObject)

class Form(QDialog):
def __init__(self, parent=None):
super(Form, self).__init__(parent)
self.setWindowTitle(‘Test Dialog’)
self.setObjectName(‘mainUI’)
self.mainLayout = QVBoxLayout(self)
self.myButton = QPushButton(‘myButton’)
self.mainLayout.addWidget(self.myButton)

global app
global form
app = qApp
form = Form(getMayaWindow())
form.show()

It doesn’t seem like you even need the install of Qt 4.5.3 that we did at this point since we changed the links, unless you use another Qt module besides QtCore, QtGui, QtSvg, QtXml, QtOpenGL (such as QtNetwork), but this could be solved by copying over the missing libs to where Maya is expecting them. Example for copying over QtNetwork:

sudo cp /usr/local/Trolltech/Qt-4.5.3/lib/libQtNetwork.* /Applications/Autodesk/maya2011/Maya.app/Contents/MacOS
sudo install_name_tool -change libQtCore.4.dylib @executable_path/QtCore /Applications/Autodesk/maya2011/Maya.app/Contents/MacOS/libQtNetwork.4.dylib

If you happen to have a mixed library environment like me, with more than one python lib location for code, and you see any funny errors while importing a module, just make sure that mayas python site-package is always in the front of the sys.path:

sys.path.insert(0,‘/Applications/Autodesk/maya2011/Maya.app/Contents/Frameworks/Python.framework/Versions/Current/lib/python2.6/site-packages’)

And there you have it. PyQt4 now installed in Maya 2011 under OSX.


References:

 
AtomSplitter

We finally decided to release the source code for AtomSplitter, since we were getting some feedback that the application builds weren’t 100% working on every possible system.

Project site:
http://redmine.justinfx.com/projects/atomsplitter

The current version is 1.6.2,  which adds the cmivfx chatroom to a tab, and some minor bug fixes. More info:
http://www.cmivfx.com/productpages/product.aspx?name=AtomSplitter_1.6

Update #1:

I have posted the AtomSplitter project on github now: https://github.com/justinfx/AtomSplitter

 

 
AtomSplitter

 

AtomSplitter has been updated to v1.6, available through cmivfx.com

AtomSplitter 1.6 – cmiVFX.com

Updates:

  • Exports Terragen .tgd scene file format

See the original post.

COMMAND LINE HELP

OSX:
AtomSplitter.app/Contents/MacOS/AtomSplitter <chan file> [out file]

Linux:
AtomSplitter <chan file> [out file]

Windows:
AtomSplitter.exe <chan file> [out file]

=========================================

HELP:
>>> AtomSplitter -h

Usage: AtomSplitter <chan file> [out file]

Options:
-h, --help
        show this help message and exit
-o OBJ, --obj=OBJ
        Optional nuke-exported pointCloud .obj file
-f FPS, --fps=FPS
        Set FPS rate (default 24)
-x WIDTH, --width=WIDTH
        Set frame width (default 2048)
-y HEIGHT, --height=HEIGHT
        Set frame height (default 1556)
--filmwidth=FILMWIDTH
        Set film aperature width in mm (default 24.576)
--filmheight=FILMHEIGHT
        Set film aperature height in mm (default 18.672)
-s SCALE, --scale=SCALE
        Scale the translation values by this amount
-F FORMAT, --format=FORMAT
        Output format (fbx, action, terragen)
 
curvesExample-white

This is a story about my journey in solving a problem at work involving curves. The solution seemed really simple at first, but because of a stupid Maya issue, this turned into me having to rewrite the tool 3 times before I discovered a surprising solution.

The Problem:

Because we rely heavily on the process of importing Illustrator .ai files into Maya as nurbsCurves, we constantly have to deal with curves that intersect themselves. This is a result of the curves having been hand drawn originally in the art department, and they posed issues during rendering after being planar-ized.

We needed a way to detect self-intersecting nurbsCurves, so that we could fix them before working with them as nurbsSurfaces.

Loop Curves (self-intersection)

Getting started. I hate you, Maya:

The obvious place for me to start was to use the existing curveIntersect command. This command will tell you if two curves intersect, and at what points. Although it requires that you tell it 2 individual curves to test, I figured I could just split the single curve into parts (using detach curve), and pair off all the parts to test them. And here is where I ran into something that just pissed me off. While it works great to tell you where the intersection is… when it DOESN’T find an intersection, it prints a “Warning: Could not find curve-curve intersection” line. Printing to the script editor in maya is SUPER expensive as I have found. Everything slows down. This was the initial reason I decided not to use this method. I later found out the RIGHT hack way to deal with this is to wrap the entire process in scriptEditorInfo commands to suppress warnings being printed to the Script Editor and re-enable after. This method does prevent the amazing slowness, but still has the unacceptable flood of warnings once suppression is turned back off again. So even with this fix, I still found this approach to be too slow. For a single complex curve object that might have, say, 500 spans… that would be 124750 unique combinations that would have to be tested with the curveIntersect command. In a test of just looping that many times and calling this command, the process took 54 seconds! No way could this work, with a scene that could easily have 2000+ nurbsCurves!

Great. Now I get to investigate my own intersection detection from scratch.

Approach #2 – Brute Force

I’m a film school graduate… not a computer science major. Never thought I would have to use the kind of math I TRIED to implement for a first solution. Solving the cubic function of every curve segment to find the intersections? Meh. Too hard.

Next I thought of an idea to take a curve and sample N-amount of points from start to end parameter. So lets say 1000 samples. I would get 1000 points on the curve, then compare each point to the other non-neighboring points to see if they are within a distance tolerance of each other. If they are, I consider this a point in the curve where it looped and self-intersected. This method did work. It was still not super fast, but no where NEAR as slow as using the intersect command. I built in some dynamic scaling of the sampling and tolerance to accommodate for curves of different size and complexity.  Even though I had to test 499500 point combinations per nurbsCurve for the 1000 point samples, this was only math and not a repeat call to curveIntersect. Ultimately, I was still not where I needed to be in terms of usability. A complex curve could still be a few seconds to test. Still too slow.

Approach #2.5 – Brute Force mixed with a fast little pre-check

Amongst the tons of search results I waded through on google, I found a really cool little trick someone came up with: http://forums.cgsociety.org/archive/index.php/t-517347.html

He suggested a cool hack where you planar the surface, then convert it to polygon with certain options, and then count the number of resulting faces that were produced. Theoretically there should only be 1 face if the curve never intersected itself. If it did, those loops should also have faces.

Well then, why couldn’t I just use this method as my detection algorithm? While it was pretty darn fast since no iteration is involved, I discovered a few limited cases where I was getting False Negatives: The method said there was only one face (no intersection) on a curve that actually did have a self intersection. So I decided to use this as a pre-check to save time. If it DID say there was self-intersection, great! Log it. Otherwise, slow test it with the brute force method.

Approach #3 – Math that I never learned

At this point I had accepted the solution for detecting self-intersection, and moved on to writing a maya UI for the tool. It would detect loops and optionally even report back the best estimate to the point where it actually occurred. But once I started writing another bigger tool that would use this script, I just knew it could be done faster with the power of math and without the poop of maya script command calls.

Bezier Clipping Method

I discovered a paper written about a method called “Bezier Clipping” : Curve intersection using Bezier Clipping

For this method to be used on a single curve, one would first need to split the curve into its segments, in my case producing a bunch of cubic curves. With each pair of combinations, a bounding box check would be performed. If the bounding boxes do not overlap, you are done. No intersection would have occurred. If the bounding boxes did overlap, then you would start drilling down the check by clipping away sections of the curves outside the bounding box overlap. You can discover the intersection pretty fast by either drilling down until the segments are small enough to represent the single point, or the segments are now small enough to be considered linear and a you can use the slopes to find the intersection.

I was really stoked by this idea. The paper made it look really straight forward. I ended up writing a really cool custom wrapper around a nurbsCurve object that could represent a sub-section of the curve, and could easily give me things like the length and parameter range. It had a split() method that could split the curve at a given length or parameter and give me back two sub-curves, of which each could further be split. This class was integrated into the Bezier Clipping method and I finally got it all working. The initial tests were amazingly fast, but I ended up finding that the process became quite slow on large numbers of complex curves. I attribute this to my implementation of the bezier clipping, not the method itself. I didn’t take the time to actually generate proper sub-segments based on the bounding box overlap, and instead just did a simple 50/50 split of each curve, and tested the new combinations. I think I was just so bummed about the performance results that I didn’t feel like making the improvements to the process would make enough of a difference. What I had now was ultimately slower than my brute force method when tested against an entire scene of curves. So I decided to just go back to that.

Approach #4 – The unexpected trick

Whilst working on the larger project meant to make use of the loop detection script, I discovered something amongst the curve commands. offsetCurve includes the function of clipping away loops while creating the new offset curve object. My idea was to create an offsetCurve with the minimal changes, at a zero distance, so the curves would be right on top of each other. Then I could just compare the area/length/etc attributes of the curves to see if there was a change. This was really fast and really accurate, down to the smallest loops. Right now I’m comparing the area and length values rounded to a certain tolerance. This method was amazingly fast, and a fraction of the code.

The only minor downside was I found that because of the rounding I needed to do to compare the values, every so often a couple curves would be found as self-intersecting when they were not. Thats much better than MISSING curves during the detection. So what I did was combine the earlier face-counting solution with this one, to fail over between the two. The combination was really fast and accurate, without 100,000 warnings showing up in the stupid script editor.

All in all

I’m really surprised how many versions of this tool I had to write, to ultimately end up at a really simple one. I never would have thought to look at this offsetCurve command for a solution. Had to dig into some math I never thought I would use, also. So THATS what those stupid polynomial equations were for?


© 2011 Justin Israel | justinfx.comSuffusion theme by Sayontan Sinha