X3D CAD/TransposeNurbsAxes

From Web3D.org
Jump to: navigation, search

The web page NURBS demo files demonstrates a disagreement among current X3D browsers that implement the NURBS Component as to how they interpret the standard to place the parametric axes (generally labelled the u-v axes) on rendered 2D surface. This discrepancy affects the way the browser handles:

  • the 'solid' attribute, which when set true will make the patch invisible from the 'inside'. The outward direction of the surface is determined by the 3-D orientation of the uv axes
  • the mapping of texture coordinates on the surface
  • the trimming of the surface by contours defined in uv-coordinates, for the NurbsTrimmedSurface node

This discrepancy amounts to interchanging the labelling of the parametic axes on the surface. It is possible to similarly interchange the parametric axes in an X3D model by

  1. interchanging the parameters uOrder with vOrder, uClosed with vClosed, etc.
  2. Transposing (rows with columns) the array of control points and weights before flattening to a list to encode in the Coordinate.point field.

The XSLT script below will perform this transformation on an XML-encoded X3D model containing NurbsTrimmedSurface or NurbsPatchSurface nodes.

An invocation of this script using the xsltproc XSLT engine from the command line would look like:

xsltproc transpose_nurbs_surface.xsl  model.x3d > transposed_model.x3d

XSLT script: transpose_nurbs_surface.xsl

<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- 
Transposes the u-v parametric coordinate axes on a NurbsPatchSurface or NurbsTrimmedSurface


For NurbsPatchSurface nodes and TrimmedNurbsSurface node the parameters which 
come in u-v pairs (ex uKnot, vKnot) have their values swapped

The attributes NurbsPatchSurface/@weight and NurbsPatchSurface/Coordinate/@point
are, in the input x3d data, lists of numbers which are the weight,control_points arrays listed in column-major order.
In the transformed data these lists are the same arrays flattened in row-major order.




Copyright 2012 Vincent Marchetti

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

-->
<xsl:stylesheet version="1.0" 
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:exsl="http://exslt.org/common"
                xmlns:str="http://exslt.org/strings"
                extension-element-prefixes="exsl"
                xmlns:ksh="http://kshell.com/ns/x3d">

<xsl:output method="xml" encoding="utf-8"/>


<xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>




<xsl:template match="NurbsPatchSurface/@uKnot|NurbsTrimmedSurface/@uKnot">
<xsl:if test="../@vKnot">
    <xsl:attribute name="{name()}">
    <xsl:value-of select="../@vKnot"/>
    </xsl:attribute>
</xsl:if>
</xsl:template>

<xsl:template match="NurbsPatchSurface/@vKnot|NurbsTrimmedSurface/@vKnot">
<xsl:if test="../@uKnot">
    <xsl:attribute name="{name()}">
    <xsl:value-of select="../@uKnot"/>
    </xsl:attribute>
</xsl:if>
</xsl:template>

<xsl:template match="NurbsPatchSurface/@uDimension|NurbsTrimmedSurface/@uDimension">
<xsl:if test="../@vDimension">
    <xsl:attribute name="{name()}">
    <xsl:value-of select="../@vDimension"/>
    </xsl:attribute>
</xsl:if>
</xsl:template>

<xsl:template match="NurbsPatchSurface/@vDimension|NurbsTrimmedSurface/@vDimension">
<xsl:if test="../@uDimension">
    <xsl:attribute name="{name()}">
    <xsl:value-of select="../@uDimension"/>
    </xsl:attribute>
</xsl:if>
</xsl:template>

<xsl:template match="NurbsPatchSurface/@uTessellation|NurbsTrimmedSurface/@uTessellation">
<xsl:if test="../@vTessellation">
    <xsl:attribute name="{name()}">
    <xsl:value-of select="../@vTessellation"/>
    </xsl:attribute>
</xsl:if>
</xsl:template>

<xsl:template match="NurbsPatchSurface/@vTessellation|NurbsTrimmedSurface/@vTessellation">
<xsl:if test="../@uTessellation">
    <xsl:attribute name="{name()}">
    <xsl:value-of select="../@uTessellation"/>
    </xsl:attribute>
</xsl:if>
</xsl:template>

<xsl:template match="NurbsPatchSurface/@uClosed|NurbsTrimmedSurface/@uClosed">
<xsl:if test="../@vClosed">
    <xsl:attribute name="{name()}">
    <xsl:value-of select="../@vClosed"/>
    </xsl:attribute>
</xsl:if>
</xsl:template>

<xsl:template match="NurbsPatchSurface/@vClosed|NurbsTrimmedSurface/@vClosed">
<xsl:if test="../@uClosed">
    <xsl:attribute name="{name()}">
    <xsl:value-of select="../@uClosed"/>
    </xsl:attribute>
</xsl:if>
</xsl:template>

<xsl:template match="NurbsPatchSurface/@uOrder|NurbsTrimmedSurface/@uOrder">
<xsl:if test="../@vOrder">
    <xsl:attribute name="{name()}">
    <xsl:value-of select="../@vOrder"/>
    </xsl:attribute>
</xsl:if>
</xsl:template>

<xsl:template match="NurbsPatchSurface/@vOrder|NurbsTrimmedSurface/@vOrder">
<xsl:if test="../@uOrder">
    <xsl:attribute name="{name()}">
    <xsl:value-of select="../@uOrder"/>
    </xsl:attribute>
</xsl:if>
</xsl:template>



<xsl:template match="NurbsPatchSurface/@weight|NurbsTrimmedSurface/@weight">
<xsl:attribute name="{name()}">
    <xsl:variable name="transposed_weights">  
    <xsl:call-template name="ksh:transpose">
        <xsl:with-param name="NC" select="number(../@vDimension)"/>
        <xsl:with-param name="NR" select="number(../@uDimension)"/>
        <xsl:with-param name="elements" select="str:split(normalize-space(.))"/>
    </xsl:call-template>
    </xsl:variable>
    
    <xsl:for-each select="exsl:node-set($transposed_weights)/token">
        <xsl:value-of select="."/>
        <xsl:if test="last()>position()"><xsl:text> </xsl:text></xsl:if>
    </xsl:for-each>

</xsl:attribute>
</xsl:template>

<xsl:template match="NurbsPatchSurface/Coordinate/@point|NurbsTrimmedSurface/Coordinate/@point">
<xsl:attribute name="{name()}">
    <xsl:variable name="transposed_point">
    <xsl:call-template name="ksh:transpose">
        <xsl:with-param name="NC" select="number(../../@vDimension)"/>
        <xsl:with-param name="NR" select="number(../../@uDimension)"/>
        <xsl:with-param name="elements" select="str:split(.,',')"/>
    </xsl:call-template>
    </xsl:variable>
    
    <xsl:for-each select="exsl:node-set($transposed_point)/token">
        <xsl:value-of select="."/>
        <xsl:if test="last() > position()"><xsl:text>, </xsl:text></xsl:if>
    </xsl:for-each>
</xsl:attribute>
</xsl:template>


<!-- 
Templates for taking an array of nodes , NR rows and NC columns, output in
column-major order, and preparing a node set of those nodes listed in
row-major order
 -->
<xsl:template name="ksh:transpose">
    <xsl:param name="IR" select="1"/>
    <xsl:param name="NR"/>
    <xsl:param name="NC"/>
    <xsl:param name="elements"/>
    
    <!-- call the template which will list (in row major order
    the IR'th row -->
    <xsl:call-template name="ksh:transpose-row-out">
        <xsl:with-param name="IR" select="$IR"/>
        <xsl:with-param name="NC" select="$NC"/>
        <xsl:with-param name="NR" select="$NR"/>
        <xsl:with-param name="elements" select="$elements"/>
    </xsl:call-template>

    <!-- the recursion: call this template starting at the next row -->
    <xsl:if test="$NR > $IR">
        <xsl:call-template name="ksh:transpose">
            <xsl:with-param name="IR" select="$IR + 1"/>
            <xsl:with-param name="NC" select="$NC"/>
            <xsl:with-param name="NR" select="$NR"/>
            <xsl:with-param name="elements" select="$elements"/>
        </xsl:call-template>
    </xsl:if>
</xsl:template>
  
<xsl:template name="ksh:transpose-row-out">
    <xsl:param name="IC" select="1"/>
    <xsl:param name="IR"/>
    <xsl:param name="NR"/>
    <xsl:param name="NC"/>
    <xsl:param name="elements"/>
    
    <!-- send out the IC'th column of the IR'th row, using the 
    indexing appropriate for column major indexing of the underlying list -->
    <xsl:param name="index" select="($IC -1)*$NR + $IR"/>
    <xsl:copy-of select="$elements[$index]"/>
    
    <!-- and the recursion, send out the rest of the columns of this row,
    starting at IC+1 -->
    <xsl:if test="$NC > $IC">
        <xsl:call-template name="ksh:transpose-row-out">
            <xsl:with-param name="IC" select="$IC + 1"/>
            <xsl:with-param name="IR" select="$IR"/>
            <xsl:with-param name="NC" select="$NC"/>
            <xsl:with-param name="NR" select="$NR"/>
            <xsl:with-param name="elements" select="$elements"/>
        </xsl:call-template>
    </xsl:if>
</xsl:template>



</xsl:stylesheet>