ftp.nice.ch/pub/next/graphics/3d/geomview.1.4.1.s.tar.gz#/Geomview/src/bin/warp/common/warp.c

This is warp.c in view mode; [Download] [Up]

#include <string.h>
#include <sys/types.h>
#include <sys/time.h>
#include <stdio.h>
#include <math.h>
#include <limits.h>
#include "geom.h"
#include "bbox.h"
#include "hpoint3.h"
#include "streampool.h"
#include "transform.h"
#include "transobj.h"
#include "lisp.h"
#include "pointlist.h"
#include "pickfunc.h"
#include "ooglutil.h"
#include "warp.h"
#include "api.h"

#define FUDGE 1.e-6

static char msg[] = "warp.c";

/*
 * WarpPoint
 * A modification of the morphing algorithm found in
 * "Feature Based Image Metamorphosis"
 * by Thaddeus Beier and Shawn Neely
 * SIGGRAPH '92 Conference Proceedings
 */

/* scale is used to put the object roughly within the unit cube */
void WarpPoint(HPoint3 *old, HPoint3 *new, WarpZone *wz, int n_wz,
	       int step, int totsteps, float scale) {
  HPoint3 dsum, Di, tmp;
  double weightsum;
  double weight, dist;
  int i;
  
  if (totsteps <= 0) 
    if (step == totsteps) step = totsteps = 1;
    else return;

  HPt3From(&dsum, 0.0, 0.0, 0.0, 1.0);
  weightsum = 0;
  
  if (new == old) {
    HPt3Copy(old, &tmp);
    old = &tmp;
  }

  for (i = 0; i < n_wz; i++) {
    
    HPt3Transform(wz[i].T, old, new);
    Di.x = (new->x - old->x) * scale;
    Di.y = (new->y - old->y) * scale;
    Di.z = (new->z - old->z) * scale;
    Di.w = 0.0;
    
    wz[i].RestLocation.w = 1.0;
    dist = scale * HPt3Distance(old, &wz[i].RestLocation);

    /* This is the critical line */
    weight = wz[i].a * pow(M_E, -dist*dist*wz[i].b*wz[i].b);

    Di.x *= weight;
    Di.y *= weight;
    Di.z *= weight;
    HPt3Add(&dsum, &Di, &dsum);
    
    weightsum += weight;
  }
  
  if (weightsum > FUDGE) {
    dsum.x /= weightsum;
    dsum.y /= weightsum;
    dsum.z /= weightsum;
    dsum.w = 0.0;  

  } else HPt3From(&dsum, 0.0, 0.0, 0.0, 0.0);

  dsum.x = ((dsum.x / totsteps) * step) / scale;
  dsum.y = ((dsum.y / totsteps) * step) / scale;
  dsum.z = ((dsum.z / totsteps) * step) / scale;

  HPt3Add(old, &dsum, new);
  
}

void WarpPointN(HPoint3 *old, HPoint3 *new, int n, WarpZone *wz, int n_wz,
		int step, int totsteps, float scale)
{
  int i;
  
  for (i = 0; i < n; i++)
    WarpPoint(&old[i], &new[i], wz, n_wz, step, totsteps, scale);
  
}


/* That's it for the heavy-duty stuff that actually does the warping.
 * The rest is just stuff to control the widgets, deal with the pipes, 
 * etc */

static WarpZone *def;
static vvec warpZoneList;
static int nextName = 1;

static pickInterest = 0;

static Pool *standardIn, *standardOut;

/* 
 * Stuff to read things from Geomview 
 */
Geom *readTarget() {
  Geom *g;

  printf ("(echo { )");
  printf("(write geometry - %s bare)", uiGetTarget());
  printf("(echo \"}\\n\" )");
  fflush(stdout);

  g = GeomFLoad(stdin, NULL);
  if (g == NULL) uiError("Unable to find geometry", uiGetTarget(), "");
  return g;
}


float getScaleNormalization(Geom *g) {
  Geom *bbox;
  HPoint3 min, max;
  float tmp, scale;

  bbox = GeomBound(g, TM_IDENTITY);
  BBoxMinMax((BBox *)bbox, &min, &max);
  GeomDelete(bbox);
  scale = max.x - min.x;
  tmp = max.y - min.y;
  scale = (scale > tmp) ? scale : tmp;
  tmp = max.z - min.z;
  scale = (scale > tmp) ? scale : tmp;
  if (scale < FUDGE) {
    uiError("Object", "has size", "zero");
    return 1.0;
  }
  else return 1.0 / scale;
}
    

/* 
 * Widget stuff 
 */

/* Only WidgetCreatAt() includes the progn stuff */
void WidgetCreateAt(float x, float y, float z) {
  WidgetName newname;

  apiFreeze();
  printf("(progn ");
  WIDGET_NAME(newname, nextName++);
  WidgetCreateAtNamed(x, y, z, newname);
  printf(")");
  fflush(stdout);
  apiThaw();
}

void WidgetCreateAtNamed(float x, float y, float z,
			 const char *name) {
  WarpZone *newzone;

  newzone = VVINDEX(warpZoneList, WarpZone, VVCOUNT(warpZoneList)++);
  def = VVINDEX(warpZoneList, WarpZone, 0);
  HPt3From(&newzone->RestLocation, x, y, z, 1.0);
  newzone->a = def->a;
  newzone->b = def->b;
  			
  printf("(progn ");
  printf("(geometry %s { INST unit { ", name);
  printf("INST unit : WarpWidgetGeom transform : WarpScaleT ");
  printf(" } transform : %sLocationT } )", name);
  printf("(normalization %s none)", name);
  WidgetLocation(name, x, y, z);
  printf(")");		/* progn */

  fflush(stdout);
  
  uiAddWidget(name, VVCOUNT(warpZoneList) - 1);
  uiSetSelectedWidget(VVCOUNT(warpZoneList) - 1);
}

void WidgetDelete(const char *name) {
  printf("(delete %s)", name);
  fflush(stdout);
}

void WidgetLocation(const char *name, float x, float y, float z) {
  Transform T;

  printf("(progn ");
  TmTranslate(T, x, y, z);
  printf("(read transform { define %sLocationT ", name);
  fputtransform(stdout, 1, &T[0][0], 0);
  printf(" } ) ");
  printf("(xform-set %s { ", name);
  fputtransform(stdout, 1, &TM_IDENTITY[0][0], 0);
  printf(" } ) ");
  printf(")");		/* End of (progn) */
  fflush(stdout);

}

void WidgetGetTransform(const char *name, Transform T) {

  printf("(echo \"{  \")");
  printf("(write transform - %s world)", name);
  printf("(echo \"}\\n\" )");
  fflush(stdout);

  if (!TransStreamIn(standardIn, NULL, T)) {
    uiError("Unable to find transform for", name, "");
    TmIdentity(T);
  }

}

void WidgetSetTransform(const char *name, Transform T) {
  printf("(xform-set %s ", name);
  TransStreamOut(standardOut, NULL, T);
  printf(")");
  fflush(stdout);
}

void WidgetDeleteAll() {
  int i;

  printf("(progn "); 
  for (i = VVCOUNT(warpZoneList) - 1; i > 0; i--) {
    WidgetDelete(uiGetWidgetName(i));
    uiDeleteWidget(i);
  }
  printf(")"); 
  fflush(stdout);
  
  VVCOUNT(warpZoneList) = 1;
  vvtrim(&warpZoneList);
  def = VVINDEX(warpZoneList, WarpZone, 0);
  uiSetSelectedWidget(0); 
}

/* Call-backs for lisp functions */

#define PICK_YES	"(interest (pick self))"
#define PICK_NO		"(uninterest (pick self))"

DEFPICKFUNC("(pick COORDSYS GEOMID G V E F P VI EI FI)",
	    coordsys,
	    id,
	    point, pn,
	    vertex, vn,
	    edge, en,
	    face, fn, 10,
	    ppath, ppn, 50,
	    vi,
	    ei, ein,
	    fi,
	    {
	      if (pn != 4) return Lt;
	      
	      if (uiGetCreateOnPick() && uiWidgetIndex(id) == -1) {
		if (point.w > FUDGE)
		  WidgetCreateAt(point.x / point.w, point.y / point.w,
				 point.z / point.w);
		else /* I'm not really sure that this is right */
		  WidgetCreateAt(point.x, point.y, point.z);
	      } 
	      return Lt;
	    })

LDEFINE(ack, LVOID, "(ack)")
{
  LDECLARE(("ack", LBEGIN,
	    LEND));
  if (!pickInterest) {
    printf(PICK_YES);
    fflush(stdout);
    pickInterest = 1;
  } 
  uiThaw();
  return Lt;
}

/* These routines will be sent from other external modules, or the user
 * via the (emodule-transmit) command. */

LDEFINE(create_widget, LVOID, "(create-widget (x y z [w]))\n\
	Creates a widget at named location.")
{
  HPoint3 point;
  int pn = 4;

  LDECLARE(("create-widget", LBEGIN,
	    LHOLD, LARRAY, LFLOAT, &point, &pn,
	    LEND));
  if (pn == 3) point.w = 1.0;
  else if (pn != 4) return Lnil;
  WidgetCreateAt(point.x / point.w, point.y / point.w, 
		 point.z / point.w);
  return Lt;
}

LDEFINE(create_widget_grid, LVOID, 
	"(create-widget-grid [(xdimn ydimn zdimn)])\n\
	Creates a grid of widgets.  If xdimn, ydimn, and zdimn are not \n\
	specified, they are assumed to be the default or the values which \n\
	have been specified on the user interface panel.  If they are \n\
	present, they will replace the values in the user interface panels.")
{
  int dim[3], dimn = 3;
  LDECLARE(("create-widget-grid", LBEGIN,
	    LOPTIONAL,
	    LHOLD, LARRAY, LINT, dim, &dimn,
	    LEND));
  if (dimn != 3) {
    dim[0] = uiGetGridX();
    dim[1] = uiGetGridY();
    dim[2] = uiGetGridZ();
  } else {
    uiSetGridX(dim[0]);
    uiSetGridY(dim[1]);
    uiSetGridZ(dim[2]);
  }
  apiPosition();
  return Lt;
}

LDEFINE(delete_widget, LVOID,
	"(delete-widget [name])\n\
	Deletes widgets.  If name is present, deletes only that widget. \n\
	If name is not specified, deletes all widgets.")
{
  int index;
  char *name = NULL;
  LDECLARE(("delete-widget", LBEGIN,
	    LOPTIONAL, 
	    LSTRING, &name,
	    LEND));
  if (name != NULL) {
    index = uiWidgetIndex(name);
    if (index == -1) return Lnil;
    uiSetSelectedWidget(index);
    apiDelete();
  }
  else apiDeleteAll();
  return Lt;
}
  

LDEFINE(preview, LVOID, 
	"(preview [name])\n\
	Same as pressing the preview button.  name is the object to warp.\n\
	If name is omitted the default object or the object specified on \n\
	the user interface panel will be used.  If name is specified, it \n\
	will replace the value on the user interface panel.")
{
  char *name = NULL;
  LDECLARE(("preview", LBEGIN,
	   LOPTIONAL,
	   LSTRING, &name,
	   LEND));
  if (name != NULL) uiSetTarget(name);
  apiPreview();
  return Lt;
}

LDEFINE(warp, LVOID, "(warp [name])\n\
	Same as pressing the warp button.name is the object to warp.\n\
        If name is omitted the default object or the object specified on \n\
        the user interface panel will be used.  If name is specified, it \n\
        will replace the value on the user interface panel.")
{
  char *name = NULL;
  LDECLARE(("warp", LBEGIN,
	   LOPTIONAL,
	   LSTRING, &name,
	   LEND));
  if (name != NULL) uiSetTarget(name);
  apiWarp();
  return Lt;
}
  


LDEFINE(exit, LVOID, "(exit)")
{ 
  LDECLARE(("exit", LBEGIN,
	    LEND));
  apiExit();
  return Lt;
}

/* Things implemented in common code to be called by machine-specific
 * code */

void apiFreeze() {
  if (pickInterest) {
    printf(PICK_NO);
    fflush(stdout);
    pickInterest = 0;
  } 
  uiFreeze();
}

void apiThaw() {
  printf("(echo \"(ack)\\n\")");
  fflush(stdout); 
}

void apiTargetChanged() {
  apiUpdateScale();
}

Lake *lake;
LObject *lit, *val;

void apiInit() {

  uiSetTarget(DEF_OBJECT);

  uiSetStrengthBounds(MIN_A, MAX_A);
  uiSetSmoothnessBounds(MIN_B, MAX_B);

  uiSetSteps(DEF_INT_STEPS);
  uiSetStart(0);
  uiSetEnd(DEF_INT_STEPS);

  uiSetPrefix(DEF_PREFIX);
  uiSetPath(DEF_PATH);

  uiSetWidgetGeom(DEF_WIDGET_GEOM);
  uiSetWidgetSize(DEF_WIDGET_SIZE);

  uiSetGridX(DEF_GRIDX);
  uiSetGridY(DEF_GRIDY);
  uiSetGridZ(DEF_GRIDZ);

  VVINIT(warpZoneList, WarpZone, 8);
  def = VVINDEX(warpZoneList, WarpZone, 0);
  HPt3From(&def->RestLocation, 0.0, 0.0, 0.0, 1.0);
  def->a = DEF_A;
  def->b = DEF_B;
  nextName = 1;
  VVCOUNT(warpZoneList) = 1;
  uiAddWidget("default", 0);
  uiSetSelectedWidget(0);

  standardIn = PoolStreamTemp(NULL, stdin, 0, NULL);
  standardOut = PoolStreamTemp(NULL, stdout, 1, NULL);

  apiUpdateGeom();
  apiUpdateScale();

  LInit();
  lake = LakeDefine(stdin, stdout, NULL);
  printf(PICK_YES);
  fflush(stdout);
  pickInterest = 1;
  LDefun("pick", Lpick, Hpick);
  LDefun("ack", Lack, Hack);

  LDefun("create-widget", Lcreate_widget, Hcreate_widget);
  LDefun("create-widget-grid", Lcreate_widget_grid, Hcreate_widget_grid);
  LDefun("delete-widget", Ldelete_widget, Hdelete_widget);
  LDefun("preview", Lpreview, Hpreview);
  LDefun("warp", Lwarp, Hwarp);
  LDefun("exit", Lexit, Hexit);
  
  pointlist_init();

}

int apiCheckPipes() {
  if (async_fnextc(stdin, 0) != NODATA) {
    apiDoPipes();
    return 1;
  } else {
    static struct timeval tenth = { 0, 100000 };
    select(0, NULL, NULL, NULL, &tenth);
    return 0;
  }
}

void apiDoPipes() {
  lit = LSexpr(lake);
  val = LEval(lit);
  LFree(lit);
  LFree(val);
}

int apiVerifyUI() {
  int bad = 0;

  if (!strlen(uiGetTarget())) {
    uiSetTarget(DEF_OBJECT);
    bad = 1;
  }
  if (uiGetSelectedWidget() < 0 || 
      uiGetSelectedWidget() > VVCOUNT(warpZoneList) - 1) {
    uiSetSelectedWidget(0);
    bad = 1;
  }
  if (uiGetSteps() < 0) {
    uiSetSteps(DEF_INT_STEPS);
    bad = 1;
  }
  if (uiGetStart() < 0) {
    uiSetStart(0);
    bad = 1;
  }
  if (uiGetEnd() != uiGetStart() + uiGetSteps()) { 
    uiSetEnd(uiGetStart() + uiGetSteps());
    bad = 1;
  }
  if (!strlen(uiGetPrefix())) {
    uiSetPrefix(DEF_PREFIX);
    bad = 1;
  }
  if (!strlen(uiGetPath())) {
    uiSetPath(DEF_PATH);
    bad = 1;
  }
  if (!strlen(uiGetWidgetGeom())) {
    uiSetWidgetGeom(DEF_WIDGET_GEOM);
    bad = 1;
  }
  if (uiGetWidgetSize() < 0.0) {
    uiSetWidgetSize(DEF_WIDGET_SIZE);
    bad = 1;
  }
  if (uiGetGridX() < 1) {
    uiSetGridX(DEF_GRIDX);
    bad = 1;
  } 
  if (uiGetGridY() < 1) {
    uiSetGridY(DEF_GRIDY);
    bad = 1;
  }
  if (uiGetGridZ() < 1) {
    uiSetGridZ(DEF_GRIDZ);
    bad = 1;
  }

  return bad;
}

void apiPreview() {
  int n_wz;
  Geom *g;
  int i;
  HPoint3 *points;
  int n_points;

  apiFreeze();
  apiVerifyUI();
  
  n_wz = VVCOUNT(warpZoneList) - 1;
  for (i = 1; i < VVCOUNT(warpZoneList); i++)
    WidgetGetTransform(uiGetWidgetName(i), 
		       VVINDEX(warpZoneList, WarpZone, i)->T);
  
  if (i == 1) {
    apiThaw();
    return;
  }

  g = readTarget();
  if (g == NULL) {
    apiThaw();
    return;
  }

  points = PointList_get(g, TM_IDENTITY, POINTLIST_SELF);
  n_points = PointList_length(g);
  WarpPointN(points, points, n_points, VVINDEX(warpZoneList, WarpZone, 1),
	     n_wz, 1, 1, getScaleNormalization(g));
  PointList_set(g, POINTLIST_SELF, points);

  printf("(geometry preview { ");
  GeomFSave(g, stdout, NULL);
  printf(" } ) ");
  fflush(stdout);
  
  GeomDelete(g);
  OOGLFree(points);
  
  apiThaw();
  
}

void apiWarp() {
  WarpZone *wz;
  int n_wz;
  int i;
  Geom *g, *dest;
  HPoint3 *points, *newpoints;
  int n_points;
  int firststep, step, totsteps;
  char buf[512];
  float scale;
  
  apiFreeze();
  
  apiVerifyUI();
  
  n_wz = VVCOUNT(warpZoneList) - 1;
  for (i = 1; i < VVCOUNT(warpZoneList); i++) {
    wz = VVINDEX(warpZoneList, WarpZone, i);
    WidgetGetTransform(uiGetWidgetName(i), wz->T);
    HPt3Transform(wz->T, &wz->RestLocation, &wz->TransLocation);
  }
  
  if (i == 1) {
    apiThaw();
    return;
  }
  
  g = readTarget();
  if (g == NULL) {
    apiThaw();
    return;
  }
  scale = getScaleNormalization(g);
  
  dest = GeomCopy(g);
  
  if (uiGetIntToGV() || uiGetIntToFiles()) {
    firststep = uiGetStart();
    totsteps = uiGetEnd();
    if (uiGetAutoUpdate()) {
      uiSetStart(uiGetStart() + uiGetSteps() + 1);
      uiSetEnd(uiGetEnd() + uiGetSteps() + 1);
    }
  }
  else {
    totsteps = 1;
    firststep = 0;
  }
  
  points = PointList_get(g, TM_IDENTITY, POINTLIST_SELF);
  n_points = PointList_length(g);
  newpoints = OOGLNewNE(HPoint3, n_points, msg);
  bcopy(points, newpoints, n_points * sizeof(HPoint3));
  
  for (step = 1; step <= totsteps; step++) {
    WarpPointN(points, newpoints, n_points,
	       VVINDEX(warpZoneList, WarpZone, 1), n_wz, step, totsteps, 
	       scale);
    PointList_set(dest, POINTLIST_SELF, newpoints);
    if (uiGetIntToFiles()) {
      sprintf(buf, "%s%s%04d", uiGetPath(), uiGetPrefix(), 
	      firststep + step);
      if (GeomSave(dest, buf) == NULL) 
	uiError("Unable to save file", buf, "");
    }
    if (uiGetIntToGV()) {
      if (uiGetRetain()) printf("(new-geometry ");
      else printf("(geometry ");
      printf(" %s { ", uiGetTarget());
      GeomFSave(dest, stdout, NULL);
      printf(" } ) ");
      fflush(stdout);
    }
  }
  
  printf("(progn ");
  
  printf("(geometry %s { ", uiGetTarget());
  GeomFSave(dest, stdout, NULL);
  printf(" } ) ");
  fflush(stdout);
  
  wz = VVINDEX(warpZoneList, WarpZone, 1);
  for (i = 0; i < n_wz; i++) {
    WarpPoint(&wz[i].RestLocation, &wz[i].TransLocation,
	      wz, n_wz, 1, 1, scale);
    WidgetLocation(uiGetWidgetName(i+1),
		   wz[i].TransLocation.x, wz[i].TransLocation.y,
		   wz[i].TransLocation.z);
    WidgetSetTransform(uiGetWidgetName(i+1), TM_IDENTITY);
  }
  for (i = 0; i < n_wz; i++)
    HPt3Copy(&wz[i].TransLocation, &wz[i].RestLocation);
  
  printf(")");		/* End of (progn) */

  fflush(stdout);
  
  GeomDelete(g);
  GeomDelete(dest);
  OOGLFree(points);
  OOGLFree(newpoints);
  
  apiThaw();
  
}

void apiEdit() {
  int i;
  WarpZone *wz;

  i = uiGetSelectedWidget();
  wz = VVINDEX(warpZoneList, WarpZone, i);
  uiSetStrength(wz->a);
  uiSetSmoothness(wz->b);
  uiSetPoint((Point3 *)(&(wz->RestLocation)));
  uiEdit(uiGetWidgetName(i));
}

void apiEditSet() {
  int i;
  WarpZone *wz;

  i = uiGetSelectedWidget();
  wz = VVINDEX(warpZoneList, WarpZone, i);
  wz->a = uiGetStrength();
  wz->b = uiGetSmoothness();
  uiGetPoint((Point3 *)(&(wz->RestLocation)));
  WidgetLocation(uiGetWidgetName(i), wz->RestLocation.x, wz->RestLocation.y,
		 wz->RestLocation.z);
}

void apiEditSetAll() {
  int i;
  float a, b;
  a = uiGetStrength();
  b = uiGetSmoothness();
  for (i = 0; i < VVCOUNT(warpZoneList); i++) {
    VVINDEX(warpZoneList, WarpZone, i)->a = a;
    VVINDEX(warpZoneList, WarpZone, i)->b = b;
  }
}

void apiEditLocation() {
  int i;
  WarpZone *wz;

  i = uiGetSelectedWidget();
  wz = VVINDEX(warpZoneList, WarpZone, i);
  WidgetGetTransform(uiGetWidgetName(i), wz->T);
  HPt3Transform(wz->T, &wz->RestLocation, &wz->TransLocation);
  uiSetPoint((Point3 *)(&(wz->TransLocation)));
}

void apiDelete() {
  int i, j;

  i = uiGetSelectedWidget();
  if (i < 1) return;
  WidgetDelete(uiGetWidgetName(i));

  for (j = i+1; j < VVCOUNT(warpZoneList); j++)
    bcopy(VVINDEX(warpZoneList, WarpZone, j),
	  VVINDEX(warpZoneList, WarpZone, j-1), sizeof(WarpZone));
  VVCOUNT(warpZoneList) = VVCOUNT(warpZoneList) - 1;
  uiDeleteWidget(i);

  if (i < VVCOUNT(warpZoneList)) uiSetSelectedWidget(i);
  else uiSetSelectedWidget(i-1);

  vvtrim(&warpZoneList);
  def = VVINDEX(warpZoneList, WarpZone, 0);
}

void apiDeleteAll() {
  apiFreeze(); 
  WidgetDeleteAll();
  apiThaw();
}

void apiPosition() {
  int x, y, z, i, j, k;
  Geom *g;
  Geom *bbox;
  HPoint3 min, max;
  float stepx, stepy, stepz;
  WidgetName name;
  
  apiVerifyUI();
  WidgetDeleteAll();

  g = readTarget();
  if (g == NULL) return;
  bbox = GeomBound(g, TM_IDENTITY);  
  BBoxMinMax((BBox *)bbox, &min, &max);

  x = uiGetGridX();
  y = uiGetGridY();
  z = uiGetGridZ();
  
  if (max.x - min.x < FUDGE) {
    uiSetGridX(1);
    x = 1;
  }
  if (max.y - min.y < FUDGE) {
    uiSetGridY(1);
    y = 1;
  }
  if (max.z - min.z < FUDGE) {
    uiSetGridZ(1);
    z = 1;
  }
  stepx = (max.x - min.x) / (x > 1 ? x-1 : 1);
  stepy = (max.y - min.y) / (y > 1 ? y-1 : 1);
  stepz = (max.z - min.z) / (z > 1 ? z-1 : 1);

  printf("(progn ");
  
  for (i = 0; i < x; i++)
    for (j = 0; j < y; j++)
      for (k = 0; k < z; k++) {
	WIDGET_GRIDNAME(name, i, j, k);
	WidgetCreateAtNamed(min.x + stepx * i, min.y + stepy * j,
			    min.z + stepz * k, name);
	
      }
  
  /* Close off progn() statement */
  printf(")");
  fflush(stdout);
  
  GeomDelete(bbox);
  GeomDelete(g);

}

void apiUpdateGeom() {
  printf("(read geometry { define WarpWidgetGeom < %s })",
	 uiGetWidgetGeom());
  fflush(stdout);
}

void apiUpdateScale() {
  Transform T;
  float scale;
  Geom *g, *bbox;
  HPoint3 min, max;
  float tmp;

  if (uiGetRelativeSize()) {
    g = readTarget();
    if (g == NULL) scale = 1.0;
    else {
      bbox = GeomBound(g, TM_IDENTITY);
      BBoxMinMax((BBox *)bbox, &min, &max);

      HPt3Normalize(&min, &min);
      HPt3Normalize(&max, &max);
      scale = max.x - min.x;
      tmp = max.y - min.y;
      if (tmp > scale) scale = tmp;
      tmp = max.z - min.z;
      if (tmp > scale) scale = tmp;
    }
    scale *= uiGetWidgetSize();
  } else scale = uiGetWidgetSize();

  TmScale(T, scale, scale, scale);

  printf("(read transform { define WarpScaleT ");
  TransStreamOut(standardOut, NULL, T); 
  printf("})");
  fflush(stdout);
}

void apiExit() {
  apiDeleteAll();
  exit(0);
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.