sp-spiral.cpp revision fdc44da6a01ea7605271feed15df61005ddd0219
153bb1867986d6db392e2cfa711ad6231fce8abeJon A. Cruz#define __SP_SPIRAL_C__
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh/** \file
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * <sodipodi:spiral> implementation
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh */
9dc68827cbd515262ecb8d5ae8547d9e82c72e00Jon A. Cruz/*
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * Authors:
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * Mitsuru Oka <oka326@parkcity.ne.jp>
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * Lauris Kaplinski <lauris@kaplinski.com>
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh *
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * Copyright (C) 1999-2002 Lauris Kaplinski
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * Copyright (C) 2000-2001 Ximian, Inc.
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh *
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * Released under GNU GPL, read the file 'COPYING' for more information
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh */
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#include "config.h"
75b857d473541532819bd791105cb352c9a43214buliabyak
75b857d473541532819bd791105cb352c9a43214buliabyak
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#include "svg/svg.h"
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#include "attributes.h"
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#include "display/bezier-utils.h"
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#include "display/curve.h"
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#include <glibmm/i18n.h>
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh#include "xml/repr.h"
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#include "sp-spiral.h"
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshstatic void sp_spiral_class_init (SPSpiralClass *klass);
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshstatic void sp_spiral_init (SPSpiral *spiral);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshstatic void sp_spiral_build (SPObject * object, SPDocument * document, Inkscape::XML::Node * repr);
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshstatic Inkscape::XML::Node *sp_spiral_write (SPObject *object, Inkscape::XML::Node *repr, guint flags);
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshstatic void sp_spiral_set (SPObject *object, unsigned int key, const gchar *value);
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshstatic void sp_spiral_update (SPObject *object, SPCtx *ctx, guint flags);
c751b68e56ace7b9d649ee20b7f25ed1f65922e3Matthew Petroff
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshstatic gchar * sp_spiral_description (SPItem * item);
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshstatic void sp_spiral_snappoints(SPItem const *item, SnapPointsIter p);
b5b35fce2e3df933e5223ef6645d814eacf51cfamikloshstatic void sp_spiral_set_shape (SPShape *shape);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshstatic NR::Point sp_spiral_get_tangent (SPSpiral const *spiral, gdouble t);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshstatic SPShapeClass *parent_class;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh/**
dc4f69a188c203f2fdc65f22d0d57904a8c52dd7miklosh * Register SPSpiral class and return its type number.
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh */
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshGType
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshsp_spiral_get_type (void)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh{
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh static GType spiral_type = 0;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh if (!spiral_type) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh GTypeInfo spiral_info = {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh sizeof (SPSpiralClass),
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh NULL, /* base_init */
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh NULL, /* base_finalize */
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh (GClassInitFunc) sp_spiral_class_init,
c53f16f52840e8c0f2be9c1cc3af633c0ba1552emiklosh NULL, /* class_finalize */
c53f16f52840e8c0f2be9c1cc3af633c0ba1552emiklosh NULL, /* class_data */
c53f16f52840e8c0f2be9c1cc3af633c0ba1552emiklosh sizeof (SPSpiral),
c53f16f52840e8c0f2be9c1cc3af633c0ba1552emiklosh 16, /* n_preallocs */
c53f16f52840e8c0f2be9c1cc3af633c0ba1552emiklosh (GInstanceInitFunc) sp_spiral_init,
63e32b5fcb40d1d6ceffa7fa1f03e679cb694b23Jon A. Cruz NULL, /* value_table */
c53f16f52840e8c0f2be9c1cc3af633c0ba1552emiklosh };
c53f16f52840e8c0f2be9c1cc3af633c0ba1552emiklosh spiral_type = g_type_register_static (SP_TYPE_SHAPE, "SPSpiral", &spiral_info, (GTypeFlags)0);
c53f16f52840e8c0f2be9c1cc3af633c0ba1552emiklosh }
c53f16f52840e8c0f2be9c1cc3af633c0ba1552emiklosh return spiral_type;
c53f16f52840e8c0f2be9c1cc3af633c0ba1552emiklosh}
c53f16f52840e8c0f2be9c1cc3af633c0ba1552emiklosh
c53f16f52840e8c0f2be9c1cc3af633c0ba1552emiklosh/**
c53f16f52840e8c0f2be9c1cc3af633c0ba1552emiklosh * SPSpiral vtable initialization.
c53f16f52840e8c0f2be9c1cc3af633c0ba1552emiklosh */
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshstatic void
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshsp_spiral_class_init (SPSpiralClass *klass)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh{
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh GObjectClass * gobject_class;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh SPObjectClass * sp_object_class;
9dc68827cbd515262ecb8d5ae8547d9e82c72e00Jon A. Cruz SPItemClass * item_class;
9dc68827cbd515262ecb8d5ae8547d9e82c72e00Jon A. Cruz SPShapeClass *shape_class;
1cda9431ef400135f5e1bd899a94b921bdad0eafmiklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh gobject_class = (GObjectClass *) klass;
68664e00e2372534b4df2fdc5f54f836bafece18miklosh sp_object_class = (SPObjectClass *) klass;
dc4f69a188c203f2fdc65f22d0d57904a8c52dd7miklosh item_class = (SPItemClass *) klass;
9dc68827cbd515262ecb8d5ae8547d9e82c72e00Jon A. Cruz shape_class = (SPShapeClass *) klass;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
a4d12a5147f3d1d6b568a326e39ef5dca384248dmiklosh parent_class = (SPShapeClass *)g_type_class_ref (SP_TYPE_SHAPE);
1667116521643e2475184b048e0abb77a2aa9735miklosh
cb814cb0df20053ca3ef16ce55da474435daf045miklosh sp_object_class->build = sp_spiral_build;
cb814cb0df20053ca3ef16ce55da474435daf045miklosh sp_object_class->write = sp_spiral_write;
cb814cb0df20053ca3ef16ce55da474435daf045miklosh sp_object_class->set = sp_spiral_set;
cb814cb0df20053ca3ef16ce55da474435daf045miklosh sp_object_class->update = sp_spiral_update;
75b857d473541532819bd791105cb352c9a43214buliabyak
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh item_class->description = sp_spiral_description;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh item_class->snappoints = sp_spiral_snappoints;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
1cda9431ef400135f5e1bd899a94b921bdad0eafmiklosh shape_class->set_shape = sp_spiral_set_shape;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh}
68664e00e2372534b4df2fdc5f54f836bafece18miklosh
dc4f69a188c203f2fdc65f22d0d57904a8c52dd7miklosh/**
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh * Callback for SPSpiral object initialization.
cb814cb0df20053ca3ef16ce55da474435daf045miklosh */
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshstatic void
1667116521643e2475184b048e0abb77a2aa9735mikloshsp_spiral_init (SPSpiral * spiral)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh{
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh spiral->cx = 0.0;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh spiral->cy = 0.0;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh spiral->exp = 1.0;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh spiral->revo = 3.0;
1667116521643e2475184b048e0abb77a2aa9735miklosh spiral->rad = 1.0;
1667116521643e2475184b048e0abb77a2aa9735miklosh spiral->arg = 0.0;
1667116521643e2475184b048e0abb77a2aa9735miklosh spiral->t0 = 0.0;
f9ab06d037336cf8796b54c94a119f40eda79a46Kris}
f9ab06d037336cf8796b54c94a119f40eda79a46Kris
f9ab06d037336cf8796b54c94a119f40eda79a46Kris/**
f9ab06d037336cf8796b54c94a119f40eda79a46Kris * Virtual build: set spiral properties from corresponding repr.
f9ab06d037336cf8796b54c94a119f40eda79a46Kris */
1667116521643e2475184b048e0abb77a2aa9735mikloshstatic void
f9ab06d037336cf8796b54c94a119f40eda79a46Krissp_spiral_build (SPObject * object, SPDocument * document, Inkscape::XML::Node * repr)
f9ab06d037336cf8796b54c94a119f40eda79a46Kris{
1667116521643e2475184b048e0abb77a2aa9735miklosh if (((SPObjectClass *) parent_class)->build)
75b857d473541532819bd791105cb352c9a43214buliabyak ((SPObjectClass *) parent_class)->build (object, document, repr);
be17b6e6b80af045ef40c635ea563ebd987269b8tavmjong-free
be17b6e6b80af045ef40c635ea563ebd987269b8tavmjong-free sp_object_read_attr (object, "sodipodi:cx");
be17b6e6b80af045ef40c635ea563ebd987269b8tavmjong-free sp_object_read_attr (object, "sodipodi:cy");
be17b6e6b80af045ef40c635ea563ebd987269b8tavmjong-free sp_object_read_attr (object, "sodipodi:expansion");
be17b6e6b80af045ef40c635ea563ebd987269b8tavmjong-free sp_object_read_attr (object, "sodipodi:revolution");
75b857d473541532819bd791105cb352c9a43214buliabyak sp_object_read_attr (object, "sodipodi:radius");
75b857d473541532819bd791105cb352c9a43214buliabyak sp_object_read_attr (object, "sodipodi:argument");
c53f16f52840e8c0f2be9c1cc3af633c0ba1552emiklosh sp_object_read_attr (object, "sodipodi:t0");
c53f16f52840e8c0f2be9c1cc3af633c0ba1552emiklosh}
c53f16f52840e8c0f2be9c1cc3af633c0ba1552emiklosh
c53f16f52840e8c0f2be9c1cc3af633c0ba1552emiklosh/**
c53f16f52840e8c0f2be9c1cc3af633c0ba1552emiklosh * Virtual write: write spiral attributes to corresponding repr.
1667116521643e2475184b048e0abb77a2aa9735miklosh */
7ddc475794d846646261f6b12015d44ac361d070Kris De Gussemstatic Inkscape::XML::Node *
7ddc475794d846646261f6b12015d44ac361d070Kris De Gussemsp_spiral_write (SPObject *object, Inkscape::XML::Node *repr, guint flags)
7ddc475794d846646261f6b12015d44ac361d070Kris De Gussem{
1667116521643e2475184b048e0abb77a2aa9735miklosh SPSpiral *spiral = SP_SPIRAL (object);
1667116521643e2475184b048e0abb77a2aa9735miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh repr = sp_repr_new ("svg:path");
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh }
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh if (flags & SP_OBJECT_WRITE_EXT) {
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh /* Fixme: we may replace these attributes by
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * sodipodi:spiral="cx cy exp revo rad arg t0"
68664e00e2372534b4df2fdc5f54f836bafece18miklosh */
68664e00e2372534b4df2fdc5f54f836bafece18miklosh repr->setAttribute("sodipodi:type", "spiral");
68664e00e2372534b4df2fdc5f54f836bafece18miklosh sp_repr_set_svg_double(repr, "sodipodi:cx", spiral->cx);
68664e00e2372534b4df2fdc5f54f836bafece18miklosh sp_repr_set_svg_double(repr, "sodipodi:cy", spiral->cy);
68664e00e2372534b4df2fdc5f54f836bafece18miklosh sp_repr_set_svg_double(repr, "sodipodi:expansion", spiral->exp);
68664e00e2372534b4df2fdc5f54f836bafece18miklosh sp_repr_set_svg_double(repr, "sodipodi:revolution", spiral->revo);
68664e00e2372534b4df2fdc5f54f836bafece18miklosh sp_repr_set_svg_double(repr, "sodipodi:radius", spiral->rad);
68664e00e2372534b4df2fdc5f54f836bafece18miklosh sp_repr_set_svg_double(repr, "sodipodi:argument", spiral->arg);
68664e00e2372534b4df2fdc5f54f836bafece18miklosh sp_repr_set_svg_double(repr, "sodipodi:t0", spiral->t0);
68664e00e2372534b4df2fdc5f54f836bafece18miklosh }
c53f16f52840e8c0f2be9c1cc3af633c0ba1552emiklosh
c53f16f52840e8c0f2be9c1cc3af633c0ba1552emiklosh // make sure the curve is rebuilt with all up-to-date parameters
c53f16f52840e8c0f2be9c1cc3af633c0ba1552emiklosh sp_spiral_set_shape ((SPShape *) spiral);
c53f16f52840e8c0f2be9c1cc3af633c0ba1552emiklosh
c53f16f52840e8c0f2be9c1cc3af633c0ba1552emiklosh //Duplicate the path
c53f16f52840e8c0f2be9c1cc3af633c0ba1552emiklosh SPCurve *curve = ((SPShape *) spiral)->curve;
c53f16f52840e8c0f2be9c1cc3af633c0ba1552emiklosh //Nulls might be possible if this called iteratively
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh if ( !curve ) {
c53f16f52840e8c0f2be9c1cc3af633c0ba1552emiklosh //g_warning("sp_spiral_write(): No path to copy\n");
c53f16f52840e8c0f2be9c1cc3af633c0ba1552emiklosh return NULL;
c53f16f52840e8c0f2be9c1cc3af633c0ba1552emiklosh }
c53f16f52840e8c0f2be9c1cc3af633c0ba1552emiklosh NArtBpath *bpath = SP_CURVE_BPATH(curve);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh if ( !bpath ) {
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh //g_warning("sp_spiral_write(): No path to copy\n");
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh return NULL;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh }
c53f16f52840e8c0f2be9c1cc3af633c0ba1552emiklosh char *d = sp_svg_write_path ( bpath );
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh repr->setAttribute("d", d);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh g_free (d);
c53f16f52840e8c0f2be9c1cc3af633c0ba1552emiklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh if (((SPObjectClass *) (parent_class))->write)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh ((SPObjectClass *) (parent_class))->write (object, repr, flags | SP_SHAPE_WRITE_PATH);
1667116521643e2475184b048e0abb77a2aa9735miklosh
1667116521643e2475184b048e0abb77a2aa9735miklosh return repr;
1667116521643e2475184b048e0abb77a2aa9735miklosh}
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
1667116521643e2475184b048e0abb77a2aa9735miklosh/**
1667116521643e2475184b048e0abb77a2aa9735miklosh * Virtual set: change spiral object attribute.
1667116521643e2475184b048e0abb77a2aa9735miklosh */
1667116521643e2475184b048e0abb77a2aa9735mikloshstatic void
1667116521643e2475184b048e0abb77a2aa9735mikloshsp_spiral_set (SPObject *object, unsigned int key, const gchar *value)
1667116521643e2475184b048e0abb77a2aa9735miklosh{
1667116521643e2475184b048e0abb77a2aa9735miklosh SPSpiral *spiral;
1667116521643e2475184b048e0abb77a2aa9735miklosh SPShape *shape;
1667116521643e2475184b048e0abb77a2aa9735miklosh
1667116521643e2475184b048e0abb77a2aa9735miklosh spiral = SP_SPIRAL (object);
1667116521643e2475184b048e0abb77a2aa9735miklosh shape = SP_SHAPE (object);
1667116521643e2475184b048e0abb77a2aa9735miklosh
1667116521643e2475184b048e0abb77a2aa9735miklosh /// \todo fixme: we should really collect updates
1667116521643e2475184b048e0abb77a2aa9735miklosh switch (key) {
1667116521643e2475184b048e0abb77a2aa9735miklosh case SP_ATTR_SODIPODI_CX:
1667116521643e2475184b048e0abb77a2aa9735miklosh if (!sp_svg_length_read_computed_absolute (value, &spiral->cx)) {
1667116521643e2475184b048e0abb77a2aa9735miklosh spiral->cx = 0.0;
1667116521643e2475184b048e0abb77a2aa9735miklosh }
1667116521643e2475184b048e0abb77a2aa9735miklosh object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
1667116521643e2475184b048e0abb77a2aa9735miklosh break;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh case SP_ATTR_SODIPODI_CY:
c53f16f52840e8c0f2be9c1cc3af633c0ba1552emiklosh if (!sp_svg_length_read_computed_absolute (value, &spiral->cy)) {
68664e00e2372534b4df2fdc5f54f836bafece18miklosh spiral->cy = 0.0;
1cda9431ef400135f5e1bd899a94b921bdad0eafmiklosh }
68664e00e2372534b4df2fdc5f54f836bafece18miklosh object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
68664e00e2372534b4df2fdc5f54f836bafece18miklosh break;
68664e00e2372534b4df2fdc5f54f836bafece18miklosh case SP_ATTR_SODIPODI_EXPANSION:
68664e00e2372534b4df2fdc5f54f836bafece18miklosh if (value) {
68664e00e2372534b4df2fdc5f54f836bafece18miklosh /** \todo
68664e00e2372534b4df2fdc5f54f836bafece18miklosh * FIXME: check that value looks like a (finite)
68664e00e2372534b4df2fdc5f54f836bafece18miklosh * number. Create a routine that uses strtod, and
68664e00e2372534b4df2fdc5f54f836bafece18miklosh * accepts a default value (if strtod finds an error).
68664e00e2372534b4df2fdc5f54f836bafece18miklosh * N.B. atof/sscanf/strtod consider "nan" and "inf"
13c1b2b6c3fc75e657813da1865541253a2b1dceapenner * to be valid numbers.
1c03744513f6db77018fe703507fe04011c20b09Krzysztof Kosiński */
1c03744513f6db77018fe703507fe04011c20b09Krzysztof Kosiński spiral->exp = g_ascii_strtod (value, NULL);
1c03744513f6db77018fe703507fe04011c20b09Krzysztof Kosiński spiral->exp = CLAMP (spiral->exp, 0.0, 1000.0);
13c1b2b6c3fc75e657813da1865541253a2b1dceapenner } else {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh spiral->exp = 1.0;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh }
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh break;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh case SP_ATTR_SODIPODI_REVOLUTION:
1667116521643e2475184b048e0abb77a2aa9735miklosh if (value) {
c53f16f52840e8c0f2be9c1cc3af633c0ba1552emiklosh spiral->revo = g_ascii_strtod (value, NULL);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh spiral->revo = CLAMP (spiral->revo, 0.05, 1024.0);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh } else {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh spiral->revo = 3.0;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh }
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh break;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh case SP_ATTR_SODIPODI_RADIUS:
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh if (!sp_svg_length_read_computed_absolute (value, &spiral->rad)) {
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh spiral->rad = MAX (spiral->rad, 0.001);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh }
d9a7c806ee7f408ddb61ff4f233c9d96111ee2b5johanengelen object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh break;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh case SP_ATTR_SODIPODI_ARGUMENT:
fba63a357654d8b3e84c60007e40aa698cd45d19miklosh if (value) {
d9a7c806ee7f408ddb61ff4f233c9d96111ee2b5johanengelen spiral->arg = g_ascii_strtod (value, NULL);
d9a7c806ee7f408ddb61ff4f233c9d96111ee2b5johanengelen /** \todo
d9a7c806ee7f408ddb61ff4f233c9d96111ee2b5johanengelen * FIXME: We still need some bounds on arg, for
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh * numerical reasons. E.g., we don't want inf or NaN,
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * nor near-infinite numbers. I'm inclined to take
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * modulo 2*pi. If so, then change the knot editors,
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * which use atan2 - revo*2*pi, which typically
d27f5758e12c3107ee69e66702043931e0756f6bmiklosh * results in very negative arg.
d27f5758e12c3107ee69e66702043931e0756f6bmiklosh */
d27f5758e12c3107ee69e66702043931e0756f6bmiklosh } else {
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh spiral->arg = 0.0;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh }
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh break;
b5b35fce2e3df933e5223ef6645d814eacf51cfamiklosh case SP_ATTR_SODIPODI_T0:
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen if (value) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh spiral->t0 = g_ascii_strtod (value, NULL);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh spiral->t0 = CLAMP (spiral->t0, 0.0, 0.999);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh /** \todo
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * Have shared constants for the allowable bounds for
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * attributes. There was a bug here where we used -1.0
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh * as the minimum (which leads to NaN via, e.g.,
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh * pow(-1.0, 0.5); see sp_spiral_get_xy for
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh * requirements.
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh */
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh } else {
0c6b51649b501e4e378921d918c6a113ab8e2bceJohan B. C. Engelen spiral->t0 = 0.0;
0c6b51649b501e4e378921d918c6a113ab8e2bceJohan B. C. Engelen }
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh break;
0c6b51649b501e4e378921d918c6a113ab8e2bceJohan B. C. Engelen default:
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh if (((SPObjectClass *) parent_class)->set)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh ((SPObjectClass *) parent_class)->set (object, key, value);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh break;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh }
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh}
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh/**
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * Virtual update callback.
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh */
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshstatic void
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshsp_spiral_update (SPObject *object, SPCtx *ctx, guint flags)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh{
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh sp_shape_set_shape ((SPShape *) object);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh }
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh if (((SPObjectClass *) parent_class)->update)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh ((SPObjectClass *) parent_class)->update (object, ctx, flags);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh}
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh/**
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * Return textual description of spiral.
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh */
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshstatic gchar *
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshsp_spiral_description (SPItem * item)
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh{
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh // TRANSLATORS: since turn count isn't an integer, please adjust the
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh // string as needed to deal with an localized plural forms.
dc4f69a188c203f2fdc65f22d0d57904a8c52dd7miklosh return g_strdup_printf (_("<b>Spiral</b> with %3f turns"), SP_SPIRAL(item)->revo);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh}
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh/**
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * Fit beziers together to spiral and draw it.
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh *
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * \pre dstep \> 0.
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * \pre is_unit_vector(*hat1).
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * \post is_unit_vector(*hat2).
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh **/
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshstatic void
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshsp_spiral_fit_and_draw (SPSpiral const *spiral,
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh SPCurve *c,
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh double dstep,
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh NR::Point darray[],
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh NR::Point const &hat1,
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh NR::Point &hat2,
1ac9ddf51315777a429c1083a3fee7eba89f400dKrzysztof Kosiński double *t)
1ac9ddf51315777a429c1083a3fee7eba89f400dKrzysztof Kosiński{
1ac9ddf51315777a429c1083a3fee7eba89f400dKrzysztof Kosiński#define BEZIER_SIZE 4
1ac9ddf51315777a429c1083a3fee7eba89f400dKrzysztof Kosiński#define FITTING_MAX_BEZIERS 4
1ac9ddf51315777a429c1083a3fee7eba89f400dKrzysztof Kosiński#define BEZIER_LENGTH (BEZIER_SIZE * FITTING_MAX_BEZIERS)
1ac9ddf51315777a429c1083a3fee7eba89f400dKrzysztof Kosiński g_assert (dstep > 0);
1ac9ddf51315777a429c1083a3fee7eba89f400dKrzysztof Kosiński g_assert (is_unit_vector (hat1));
1ac9ddf51315777a429c1083a3fee7eba89f400dKrzysztof Kosiński
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh NR::Point bezier[BEZIER_LENGTH];
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh double d;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh int depth, i;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh for (d = *t, i = 0; i <= SAMPLE_SIZE; d += dstep, i++) {
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh darray[i] = sp_spiral_get_xy(spiral, d);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh /* Avoid useless adjacent dups. (Otherwise we can have all of darray filled with
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh the same value, which upsets chord_length_parameterize.) */
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh if ((i != 0)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh && (darray[i] == darray[i - 1])
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh && (d < 1.0)) {
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh i--;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh d += dstep;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh /** We mustn't increase dstep for subsequent values of
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * i: for large spiral.exp values, rate of growth
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * increases very rapidly.
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh */
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh /** \todo
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * Get the function itself to decide what value of d
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * to use next: ensure that we move at least 0.25 *
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * stroke width, for example. The derivative (as used
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * for get_tangent before normalization) would be
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * useful for estimating the appropriate d value. Or
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * perhaps just start with a small dstep and scale by
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * some small number until we move >= 0.25 *
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * stroke_width. Must revert to the original dstep
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * value for next iteration to avoid the problem
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * mentioned above.
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh */
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh }
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh }
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh double const next_t = d - 2 * dstep;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh /* == t + (SAMPLE_SIZE - 1) * dstep, in absence of dups. */
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh hat2 = -sp_spiral_get_tangent (spiral, next_t);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh /** \todo
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * We should use better algorithm to specify maximum error.
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh */
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh depth = sp_bezier_fit_cubic_full (bezier, NULL, darray, SAMPLE_SIZE,
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh hat1, hat2,
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh SPIRAL_TOLERANCE*SPIRAL_TOLERANCE,
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh FITTING_MAX_BEZIERS);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh g_assert(depth * BEZIER_SIZE <= gint(G_N_ELEMENTS(bezier)));
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#ifdef SPIRAL_DEBUG
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh if (*t == spiral->t0 || *t == 1.0)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh g_print ("[%s] depth=%d, dstep=%g, t0=%g, t=%g, arg=%g\n",
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh debug_state, depth, dstep, spiral->t0, *t, spiral->arg);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#endif
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh if (depth != -1) {
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh for (i = 0; i < 4*depth; i += 4) {
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh sp_curve_curveto (c,
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh bezier[i + 1],
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh bezier[i + 2],
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh bezier[i + 3]);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh }
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh } else {
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#ifdef SPIRAL_VERBOSE
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh g_print ("cant_fit_cubic: t=%g\n", *t);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh#endif
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh for (i = 1; i < SAMPLE_SIZE; i++)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh sp_curve_lineto (c, darray[i]);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh }
dc4f69a188c203f2fdc65f22d0d57904a8c52dd7miklosh *t = next_t;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh g_assert (is_unit_vector (hat2));
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh}
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshstatic void
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshsp_spiral_set_shape (SPShape *shape)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh{
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh NR::Point darray[SAMPLE_SIZE + 1];
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh double t;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh SPSpiral *spiral = SP_SPIRAL(shape);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh SP_OBJECT (spiral)->requestModified(SP_OBJECT_MODIFIED_FLAG);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh SPCurve *c = sp_curve_new ();
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#ifdef SPIRAL_VERBOSE
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh g_print ("cx=%g, cy=%g, exp=%g, revo=%g, rad=%g, arg=%g, t0=%g\n",
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh spiral->cx,
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh spiral->cy,
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh spiral->exp,
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh spiral->revo,
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh spiral->rad,
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh spiral->arg,
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh spiral->t0);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh#endif
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh /* Initial moveto. */
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh sp_curve_moveto(c, sp_spiral_get_xy(spiral, spiral->t0));
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh double const tstep = SAMPLE_STEP / spiral->revo;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh double const dstep = tstep / (SAMPLE_SIZE - 1);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh NR::Point hat1 = sp_spiral_get_tangent (spiral, spiral->t0);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh NR::Point hat2;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh for (t = spiral->t0; t < (1.0 - tstep);) {
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh sp_spiral_fit_and_draw (spiral, c, dstep, darray, hat1, hat2, &t);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh hat1 = -hat2;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh }
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh if ((1.0 - t) > SP_EPSILON)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh sp_spiral_fit_and_draw (spiral, c, (1.0 - t)/(SAMPLE_SIZE - 1.0),
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh darray, hat1, hat2, &t);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh sp_shape_set_curve_insync ((SPShape *) spiral, c, TRUE);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh sp_curve_unref (c);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh}
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh/**
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * Set spiral properties and update display.
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh */
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshvoid
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshsp_spiral_position_set (SPSpiral *spiral,
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh gdouble cx,
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh gdouble cy,
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh gdouble exp,
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh gdouble revo,
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh gdouble rad,
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh gdouble arg,
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh gdouble t0)
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh{
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh g_return_if_fail (spiral != NULL);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh g_return_if_fail (SP_IS_SPIRAL (spiral));
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
3686c32a570c3df738a09b34e85fc5d6bd50d020miklosh /** \todo
3686c32a570c3df738a09b34e85fc5d6bd50d020miklosh * Consider applying CLAMP or adding in-bounds assertions for
3686c32a570c3df738a09b34e85fc5d6bd50d020miklosh * some of these parameters.
3686c32a570c3df738a09b34e85fc5d6bd50d020miklosh */
3686c32a570c3df738a09b34e85fc5d6bd50d020miklosh spiral->cx = cx;
3686c32a570c3df738a09b34e85fc5d6bd50d020miklosh spiral->cy = cy;
3686c32a570c3df738a09b34e85fc5d6bd50d020miklosh spiral->exp = exp;
3686c32a570c3df738a09b34e85fc5d6bd50d020miklosh spiral->revo = revo;
3686c32a570c3df738a09b34e85fc5d6bd50d020miklosh spiral->rad = MAX (rad, 0.001);
3686c32a570c3df738a09b34e85fc5d6bd50d020miklosh spiral->arg = arg;
3686c32a570c3df738a09b34e85fc5d6bd50d020miklosh spiral->t0 = CLAMP(t0, 0.0, 0.999);
3686c32a570c3df738a09b34e85fc5d6bd50d020miklosh
3686c32a570c3df738a09b34e85fc5d6bd50d020miklosh ((SPObject *)spiral)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
3686c32a570c3df738a09b34e85fc5d6bd50d020miklosh}
d27f5758e12c3107ee69e66702043931e0756f6bmiklosh
3686c32a570c3df738a09b34e85fc5d6bd50d020miklosh/**
3686c32a570c3df738a09b34e85fc5d6bd50d020miklosh * Virtual snappoints callback.
3686c32a570c3df738a09b34e85fc5d6bd50d020miklosh */
3686c32a570c3df738a09b34e85fc5d6bd50d020mikloshstatic void sp_spiral_snappoints(SPItem const *item, SnapPointsIter p)
3686c32a570c3df738a09b34e85fc5d6bd50d020miklosh{
3686c32a570c3df738a09b34e85fc5d6bd50d020miklosh if (((SPItemClass *) parent_class)->snappoints) {
3686c32a570c3df738a09b34e85fc5d6bd50d020miklosh ((SPItemClass *) parent_class)->snappoints (item, p);
3686c32a570c3df738a09b34e85fc5d6bd50d020miklosh }
3686c32a570c3df738a09b34e85fc5d6bd50d020miklosh}
3686c32a570c3df738a09b34e85fc5d6bd50d020miklosh
3686c32a570c3df738a09b34e85fc5d6bd50d020miklosh/**
3686c32a570c3df738a09b34e85fc5d6bd50d020miklosh * Return one of the points on the spiral.
3686c32a570c3df738a09b34e85fc5d6bd50d020miklosh *
3686c32a570c3df738a09b34e85fc5d6bd50d020miklosh * \param t specifies how far along the spiral.
3686c32a570c3df738a09b34e85fc5d6bd50d020miklosh * \pre \a t in [0.0, 2.03]. (It doesn't make sense for t to be much more
3686c32a570c3df738a09b34e85fc5d6bd50d020miklosh * than 1.0, though some callers go slightly beyond 1.0 for curve-fitting
3686c32a570c3df738a09b34e85fc5d6bd50d020miklosh * purposes.)
3686c32a570c3df738a09b34e85fc5d6bd50d020miklosh */
3686c32a570c3df738a09b34e85fc5d6bd50d020mikloshNR::Point sp_spiral_get_xy (SPSpiral const *spiral, gdouble t)
1db439af43130c9695dbbb661e893d56006bb072miklosh{
1db439af43130c9695dbbb661e893d56006bb072miklosh g_assert (spiral != NULL);
1db439af43130c9695dbbb661e893d56006bb072miklosh g_assert (SP_IS_SPIRAL(spiral));
1db439af43130c9695dbbb661e893d56006bb072miklosh g_assert (spiral->exp >= 0.0);
1db439af43130c9695dbbb661e893d56006bb072miklosh /* Otherwise we get NaN for t==0. */
1db439af43130c9695dbbb661e893d56006bb072miklosh g_assert (spiral->exp <= 1000.0);
1db439af43130c9695dbbb661e893d56006bb072miklosh /* Anything much more results in infinities. Even allowing 1000 is somewhat overkill. */
1db439af43130c9695dbbb661e893d56006bb072miklosh g_assert (t >= 0.0);
1db439af43130c9695dbbb661e893d56006bb072miklosh /* Any callers passing -ve t will have a bug for non-integral values of exp. */
1db439af43130c9695dbbb661e893d56006bb072miklosh
1db439af43130c9695dbbb661e893d56006bb072miklosh double const rad = spiral->rad * pow(t, (double) spiral->exp);
1db439af43130c9695dbbb661e893d56006bb072miklosh double const arg = 2.0 * M_PI * spiral->revo * t + spiral->arg;
1db439af43130c9695dbbb661e893d56006bb072miklosh
1db439af43130c9695dbbb661e893d56006bb072miklosh return NR::Point(rad * cos (arg) + spiral->cx,
1db439af43130c9695dbbb661e893d56006bb072miklosh rad * sin (arg) + spiral->cy);
1db439af43130c9695dbbb661e893d56006bb072miklosh}
1db439af43130c9695dbbb661e893d56006bb072miklosh
1db439af43130c9695dbbb661e893d56006bb072miklosh
1db439af43130c9695dbbb661e893d56006bb072miklosh/**
1db439af43130c9695dbbb661e893d56006bb072miklosh * Returns the derivative of sp_spiral_get_xy with respect to t,
1db439af43130c9695dbbb661e893d56006bb072miklosh * scaled to a unit vector.
3686c32a570c3df738a09b34e85fc5d6bd50d020miklosh *
3686c32a570c3df738a09b34e85fc5d6bd50d020miklosh * \pre spiral != 0.
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh * \pre 0 \<= t.
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh * \pre p != NULL.
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh * \post is_unit_vector(*p).
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh */
3711b3e25395437ee0a09dbbb2a76d999c4ef322mikloshstatic NR::Point
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshsp_spiral_get_tangent (SPSpiral const *spiral, gdouble t)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh{
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh NR::Point ret(1.0, 0.0);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh g_return_val_if_fail (( ( spiral != NULL )
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh && SP_IS_SPIRAL(spiral) ),
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh ret);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh g_assert (t >= 0.0);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh g_assert (spiral->exp >= 0.0);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh /* See above for comments on these assertions. */
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh double const t_scaled = 2.0 * M_PI * spiral->revo * t;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh double const arg = t_scaled + spiral->arg;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh double const s = sin (arg);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh double const c = cos (arg);
920fbbf5386a5d3b1d0a1304cb71eb48112abe0dmiklosh
920fbbf5386a5d3b1d0a1304cb71eb48112abe0dmiklosh if (spiral->exp == 0.0) {
920fbbf5386a5d3b1d0a1304cb71eb48112abe0dmiklosh ret = NR::Point(-s, c);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh } else if (t_scaled == 0.0) {
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh ret = NR::Point(c, s);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh } else {
07b7f1aaaf1087716e784a50cf574a059f7018d3Jon A. Cruz NR::Point unrotated(spiral->exp, t_scaled);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh double const s_len = L2 (unrotated);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh g_assert (s_len != 0);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh /** \todo
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * Check that this isn't being too hopeful of the hypot
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * function. E.g. test with numbers around 2**-1070
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * (denormalized numbers), preferably on a few different
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh * platforms. However, njh says that the usual implementation
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh * does handle both very big and very small numbers.
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh */
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh unrotated /= s_len;
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh /* ret = spiral->exp * (c, s) + t_scaled * (-s, c);
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen alternatively ret = (spiral->exp, t_scaled) * (( c, s),
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh (-s, c)).*/
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh ret = NR::Point(dot(unrotated, NR::Point(c, -s)),
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh dot(unrotated, NR::Point(s, c)));
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh /* ret should already be approximately normalized: the
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh matrix ((c, -s), (s, c)) is orthogonal (it just
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh rotates by arg), and unrotated has been normalized,
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh so ret is already of unit length other than numerical
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh error in the above matrix multiplication. */
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh /** \todo
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * I haven't checked how important it is for ret to be very
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * near unit length; we could get rid of the below.
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh */
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh ret.normalize();
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh /* Proof that ret length is non-zero: see above. (Should be near 1.) */
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh }
f9325af537ca5517eb50ef95f432a3204616f6b3apenner
1c03744513f6db77018fe703507fe04011c20b09Krzysztof Kosiński g_assert (is_unit_vector (ret));
1c03744513f6db77018fe703507fe04011c20b09Krzysztof Kosiński return ret;
1c03744513f6db77018fe703507fe04011c20b09Krzysztof Kosiński}
1c03744513f6db77018fe703507fe04011c20b09Krzysztof Kosiński
1c03744513f6db77018fe703507fe04011c20b09Krzysztof Kosiński/**
1c03744513f6db77018fe703507fe04011c20b09Krzysztof Kosiński * Compute rad and/or arg for point on spiral.
1c03744513f6db77018fe703507fe04011c20b09Krzysztof Kosiński */
1c03744513f6db77018fe703507fe04011c20b09Krzysztof Kosińskivoid
f9325af537ca5517eb50ef95f432a3204616f6b3apennersp_spiral_get_polar (SPSpiral const *spiral, gdouble t, gdouble *rad, gdouble *arg)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh{
dd6d813ffad339352c39dc0645a792bdd9d8315cmiklosh g_return_if_fail (spiral != NULL);
dd6d813ffad339352c39dc0645a792bdd9d8315cmiklosh g_return_if_fail (SP_IS_SPIRAL(spiral));
dd6d813ffad339352c39dc0645a792bdd9d8315cmiklosh
dd6d813ffad339352c39dc0645a792bdd9d8315cmiklosh if (rad)
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh *rad = spiral->rad * pow(t, (double) spiral->exp);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh if (arg)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh *arg = 2.0 * M_PI * spiral->revo * t + spiral->arg;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh}
6d9c1b595cc989472a475874007760800d11e6b8pjrm
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh/**
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh * Return true if spiral has properties that make it invalid.
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh */
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshbool
7a7fa095a483e8b652af9f00e5169f62c84f09b9mikloshsp_spiral_is_invalid (SPSpiral const *spiral)
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh{
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh gdouble rad;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh sp_spiral_get_polar (spiral, 0.0, &rad, NULL);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh if (rad < 0.0 || rad > SP_HUGE) {
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh g_print ("rad(t=0)=%g\n", rad);
f8d5f6dc1f2af1841ce5df731eb121151810e43dKris return TRUE;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh }
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh sp_spiral_get_polar (spiral, 1.0, &rad, NULL);
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh if (rad < 0.0 || rad > SP_HUGE) {
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh g_print ("rad(t=1)=%g\n", rad);
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh return TRUE;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh }
3711b3e25395437ee0a09dbbb2a76d999c4ef322miklosh return FALSE;
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh}
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh/*
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh Local Variables:
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh mode:c++
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh c-file-style:"stroustrup"
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh indent-tabs-mode:nil
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh fill-column:99
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh End:
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh*/
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
7a7fa095a483e8b652af9f00e5169f62c84f09b9miklosh