Timeline: finish ZoomWindow implementation and boundrary tests
This commit is contained in:
parent
e436023ef9
commit
52d3231226
6 changed files with 220 additions and 35 deletions
|
|
@ -64,17 +64,12 @@ typedef unsigned int uint;
|
|||
|
||||
#include "lib/format-cout.hpp"
|
||||
#include "lib/test/test-helper.hpp"
|
||||
#include "lib/test/diagnostic-output.hpp"
|
||||
#include "lib/util.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
using std::string;
|
||||
|
||||
#define SHOW_TYPE(_TY_) \
|
||||
cout << "typeof( " << STRINGIFY(_TY_) << " )= " << lib::meta::typeStr<_TY_>() <<endl;
|
||||
#define SHOW_EXPR(_XX_) \
|
||||
cout << "Probe " << STRINGIFY(_XX_) << " ? = " << _XX_ <<endl;
|
||||
|
||||
|
||||
template<class TAR>
|
||||
|
|
|
|||
68
src/lib/test/diagnostic-output.hpp
Normal file
68
src/lib/test/diagnostic-output.hpp
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
DIAGNOSTIC-OUTPUT.hpp - some helpers for investigation and value output
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2022, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/** @file diagnostic-output.hpp
|
||||
** Helpers typically used while writing tests.
|
||||
** In the finished test, we rather want values be asserted explicitly in the
|
||||
** test code, but while building the test, it is often necessary to see the types
|
||||
** and the actual values of intermediary results, to get a clue where matters go south.
|
||||
**
|
||||
** @see test-helper.hpp
|
||||
**
|
||||
*/
|
||||
|
||||
|
||||
#ifndef LIB_TEST_DIAGNOSTIC_OUTPUT_H
|
||||
#define LIB_TEST_DIAGNOSTIC_OUTPUT_H
|
||||
|
||||
|
||||
#include "lib/format-cout.hpp"
|
||||
#include "lib/test/test-helper.hpp"
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <string>
|
||||
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace lib {
|
||||
namespace test{
|
||||
|
||||
}} // namespace lib::test
|
||||
|
||||
|
||||
|
||||
|
||||
/* === test helper macros === */
|
||||
|
||||
/**
|
||||
* Macro to print types and expressions to STDOUT,
|
||||
* using Lumiera's string conversion framework
|
||||
*/
|
||||
#define SHOW_TYPE(_TY_) \
|
||||
cout << "typeof( " << STRINGIFY(_TY_) << " )= " << lib::test::showType<_TY_>() <<endl;
|
||||
#define SHOW_EXPR(_XX_) \
|
||||
cout << "#--◆--# " << STRINGIFY(_XX_) << " ? = " << _XX_ <<endl;
|
||||
|
||||
|
||||
#endif /*LIB_TEST_DIAGNOSTIC_OUTPUT_H*/
|
||||
|
|
@ -93,6 +93,8 @@ namespace time {
|
|||
|
||||
const Offset Offset::ZERO (Time::ZERO);
|
||||
|
||||
const FSecs FSEC_MAX{std::numeric_limits<int64_t>::max() / lib::time::TimeValue::SCALE};
|
||||
|
||||
Literal DIAGNOSTIC_FORMAT{"%s%01d:%02d:%02d.%03d"};
|
||||
|
||||
|
||||
|
|
@ -339,9 +341,16 @@ lumiera_tmpbuf_print_time (gavl_time_t time)
|
|||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
/// @todo this utility function could be factored out into a `FSecs` or `RSec` class ///////////////////////TICKET #1262
|
||||
gavl_time_t
|
||||
lumiera_rational_to_time (FSecs const& fractionalSeconds)
|
||||
{
|
||||
// avoid numeric wrap from values not representable as 64bit µ-ticks
|
||||
if (abs(fractionalSeconds) > lib::time::FSEC_MAX)
|
||||
return (fractionalSeconds < 0? -1:+1)
|
||||
* std::numeric_limits<int64_t>::max();
|
||||
|
||||
return gavl_time_t(util::reQuant (fractionalSeconds.numerator()
|
||||
,fractionalSeconds.denominator()
|
||||
,lib::time::TimeValue::SCALE
|
||||
|
|
|
|||
|
|
@ -50,6 +50,9 @@
|
|||
** - the overall TimeSpan of the timeline, defining a start and end time
|
||||
** - the visible interval („window“), likewise modelled as time::TimeSpan
|
||||
** - the scale defined as pixels per second
|
||||
** @todo as of 12/2022 it rather seems the more general navigation should be abstracted
|
||||
** at a higher level, leaving ZoomWindow mostly focused on time scale handling.
|
||||
**
|
||||
**
|
||||
** # Interactions
|
||||
**
|
||||
|
|
@ -199,7 +202,10 @@ namespace model {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
|
||||
|
||||
|
||||
/******************************************************//**
|
||||
* A component to ensure uniform handling of zoom scale
|
||||
* and visible interval on the timeline. Changes through
|
||||
* the mutator functions are validated and harmonised to
|
||||
|
|
@ -237,7 +243,7 @@ namespace model {
|
|||
}
|
||||
|
||||
ZoomWindow (TimeSpan timeline =TimeSpan{Time::ZERO, DEFAULT_CANVAS})
|
||||
: ZoomWindow{0, timeline} //see ensureConsistent()
|
||||
: ZoomWindow{0, timeline} //see establishMetric()
|
||||
{ }
|
||||
|
||||
TimeSpan
|
||||
|
|
@ -499,7 +505,7 @@ namespace model {
|
|||
* is thus specific to the ZoomWindow implementation. To sanitise, the denominator
|
||||
* is reduced logarithmically (bit-shift) sufficiently and then used as new quantiser,
|
||||
* thus ensuring that both denominator (=quantiser) and numerator are below limit.
|
||||
* @warning the rational number must not be to large overall; this heuristic will fail
|
||||
* @warning the rational number must not be too large overall; this heuristic will fail
|
||||
* on fractions with very large numerator and small denominator — however, for
|
||||
* the ZoomWindow, this case is not relevant, since the zoom factor is limited,
|
||||
* and other usages of rational numbers can be range checked explicitly.
|
||||
|
|
|
|||
|
|
@ -25,18 +25,51 @@
|
|||
** The timeline uses the abstraction of an »Zoom Window«
|
||||
** to define the scrolling and temporal scaling behaviour uniformly.
|
||||
** This unit test verifies this abstracted behaviour against the spec.
|
||||
**
|
||||
** # Fractional Seconds
|
||||
**
|
||||
** A defining trait of the ZoomWindow implementation — as it stands 12/2022 — is the use
|
||||
** of integer fractions for most scale and time interval calculations. The typical media
|
||||
** handling operations often rely on denomination into a divisor defined scale — be it
|
||||
** seconds divided by frame count (25fps), or be it audio samples like 1/96000 sec.
|
||||
** And for presentation in the UI, these uneven fractions need to be broken down into
|
||||
** a fixed pixel count, while the zoom factor can vary over several orders of magnitude.
|
||||
** Integer fractions are a technically brilliant solution to cope with this challenge,
|
||||
** without rounding discrepancies and accumulation of errors.
|
||||
**
|
||||
** However, there is a catch: The way fractional arithmetics are handled leads to lots
|
||||
** of multiplications, with the tendency to build up very large irreducible numbers, both in
|
||||
** numerator and denominator. In worst case, numeric wrap-around can happen even at seemingly
|
||||
** innocuous places. In an attempt to maintain the benefits of integer fraction arithmetics,
|
||||
** for ZoomWindow a set of »coping strategies« was developed, to detect and control the cases
|
||||
** when numbers „go south“. This approach is based on the observation that almost all
|
||||
** everyday time calculations happen within a rather limited domain, while the extended
|
||||
** time domain of years and centuries rather serves as a theoretical headroom. Thus it
|
||||
** seems reasonable to benefit from integer fractions within this everyday range, under
|
||||
** the condition that computations can be kept from derailing totally, when entering
|
||||
** the extended domain.
|
||||
**
|
||||
** To this end, we use the trick of introducing a minute numeric error, by re-quantising
|
||||
** huge numbers into a scale with a smaller denominator. We introduce the notion of »toxic«
|
||||
** numbers, which are defined by figures above 2^40 — irrespective if in numerator or in
|
||||
** denominator. This rather arbitrary choice is based on the observation that most
|
||||
** computation paths require to multiply with Time::SCALE (the µ-tick scale of 10^6),
|
||||
** which together with 2^40 just fits into the value range of int64_t. Thus, into all
|
||||
** crucial computation paths, a function `detox()` is wired, which remains inactive for
|
||||
** regular values, but automatically _sanitises extreme values._ Together with the
|
||||
** safety headroom built into the limits of the Lumiera lib::time::Time domain,
|
||||
** this allows to handle all valid time points and represent even the largest
|
||||
** possible lib::time::Duration::MAX.
|
||||
**
|
||||
** A major part of this test is dedicated to covering those hypothetical corner cases
|
||||
** and to ensure the defined behaviour can be maintained even under extreme conditions.
|
||||
**
|
||||
*/
|
||||
|
||||
|
||||
#include "lib/test/run.hpp"
|
||||
#include "lib/test/test-helper.hpp"
|
||||
#include "stage/model/zoom-window.hpp"
|
||||
#include "lib/format-cout.hpp"//////////////TODO
|
||||
#define SHOW_TYPE(_TY_) \
|
||||
cout << "typeof( " << STRINGIFY(_TY_) << " )= " << lib::meta::typeStr<_TY_>() <<endl;
|
||||
#define SHOW_EXPR(_XX_) \
|
||||
cout << "#--◆--# " << STRINGIFY(_XX_) << " ? = " << _XX_ <<endl;
|
||||
//////////////////////////////////////////////////////////////////////////////TODO
|
||||
|
||||
|
||||
namespace stage{
|
||||
|
|
@ -61,6 +94,7 @@ namespace test {
|
|||
* - setting a visible position
|
||||
* - nudging the position
|
||||
* - nudging the scale factor
|
||||
* @remark the `safeguard_*` tests focus on the boundary cases.
|
||||
* @see zoom-window.hpp
|
||||
*/
|
||||
class ZoomWindow_test : public Test
|
||||
|
|
@ -121,7 +155,7 @@ namespace test {
|
|||
|
||||
|
||||
/** @test verify the possible variations for initial setup of the zoom window
|
||||
* - can be defined either the canvas duration,
|
||||
* - can be defined either with the canvas duration,
|
||||
* or an explicit extension given in pixels, or both
|
||||
* - after construction, visible window always covers whole canvas
|
||||
* - window extension, when given, defines the initial metric
|
||||
|
|
@ -156,7 +190,7 @@ namespace test {
|
|||
}
|
||||
|
||||
|
||||
/** @test verify defining and retaining of the effective extension in pixels
|
||||
/** @test verify defining and retaining the effective extension in pixels
|
||||
* - changes to the extension are applied by adjusting the visible window
|
||||
* - visible window's start position is maintained
|
||||
* - unless the resulting window would exceed the overall canvas,
|
||||
|
|
@ -525,7 +559,7 @@ namespace test {
|
|||
ZoomWindow win{1};
|
||||
win.setVisibleDuration(Duration{FSecs(1,25)});
|
||||
win.setOverallRange(TimeSpan(_t(10), _t(0))); // set an "reversed" overall time range
|
||||
CHECK (win.overallSpan() == TimeSpan(_t(0), _t(10))); // range has been oriented forward
|
||||
CHECK (win.overallSpan() == TimeSpan(_t(0), _t(10))); // range has been re-oriented forward
|
||||
CHECK (win.visible().duration() == Time(40,0));
|
||||
CHECK (win.px_per_sec() == 25);
|
||||
CHECK (win.pxWidth() == 1);
|
||||
|
|
@ -552,7 +586,7 @@ namespace test {
|
|||
CHECK (2_r/3 < poison and poison < 1); // looks innocuous...
|
||||
CHECK (poison + Time::SCALE < 0); // simple calculations fail due to numeric overflow
|
||||
CHECK (poison * Time::SCALE < 0);
|
||||
CHECK (-6 == rational_cast<gavl_time_t>(poison * Time::SCALE)); // naive conversion to µ-ticks would leads to overflow
|
||||
CHECK (-6 == rational_cast<gavl_time_t>(poison * Time::SCALE)); // naive conversion to µ-ticks would lead to overflow
|
||||
CHECK (671453 == _raw(Time(FSecs(poison)))); // however the actual conversion routine is safeguarded
|
||||
CHECK (671453.812f == rational_cast<float>(poison)*Time::SCALE);
|
||||
|
||||
|
|
@ -598,6 +632,7 @@ namespace test {
|
|||
|
||||
Rat poison{_raw(Time::MAX)-101010101010101010, _raw(Time::MAX)+23};
|
||||
CHECK (0 < poison and poison < 1);
|
||||
|
||||
/*--Test-1-----------*/
|
||||
win.setMetric (poison); // inject an evil new value for the metric
|
||||
CHECK (win.visible() == win.overallSpan()); // however, nothing happens
|
||||
|
|
@ -922,14 +957,45 @@ namespace test {
|
|||
*/
|
||||
void
|
||||
safeguard_verySmall()
|
||||
{
|
||||
// SHOW_EXPR(win.overallSpan());
|
||||
// SHOW_EXPR(_raw(win.overallSpan().duration()));
|
||||
// SHOW_EXPR(_raw(win.visible().duration()));
|
||||
// SHOW_EXPR(_raw(win.visible().start()));
|
||||
// SHOW_EXPR(_raw(win.visible().end()));
|
||||
// SHOW_EXPR(win.px_per_sec());
|
||||
// SHOW_EXPR(win.pxWidth());
|
||||
{ // for setup, request a window crossing time domain bounds
|
||||
ZoomWindow win{ 1, TimeSpan{Time::MAX - TimeValue(23), Duration::MAX}};
|
||||
CHECK (win.overallSpan().duration() == Duration::MAX); // we get a canvas with the requested extension Duration::MAX
|
||||
CHECK (win.overallSpan().end() == Time::MAX); // but shifted into domain to fit
|
||||
CHECK (win.visible().duration() == LIM_HAZARD * 1000); // the visible window however is limited to be smaller
|
||||
CHECK (win.visible().start()+win.visible().end() == Time::ZERO); // and (since this is a zoom-in) it is centred at origin
|
||||
CHECK (win.px_per_sec() == 1_r/(LIM_HAZARD*1000)*Time::SCALE); // Zoom metric is likewise limited, to keep the numbers manageable
|
||||
CHECK (win.px_per_sec() == 125_r/137438953472);
|
||||
CHECK (win.pxWidth() == 1);
|
||||
|
||||
win.nudgeVisiblePos (+1); // can work with this tiny window as expected
|
||||
CHECK (win.visible().start() == Time::ZERO);
|
||||
CHECK (win.visible().end() == LIM_HAZARD*1000);
|
||||
CHECK (win.px_per_sec() == 125_r/137438953472);
|
||||
CHECK (win.pxWidth() == 1);
|
||||
|
||||
win.nudgeMetric (-1); // can not zoom out further
|
||||
CHECK (win.px_per_sec() == 125_r/137438953472);
|
||||
win.nudgeMetric (+1); // but can zoom in
|
||||
CHECK (win.px_per_sec() == 125_r/68719476736);
|
||||
CHECK (win.visible().start() == TimeValue(274877908523000));
|
||||
CHECK (win.visible().end() == TimeValue(824633722411000));
|
||||
CHECK (win.visible().duration() == LIM_HAZARD * 1000 / 2);
|
||||
CHECK (win.pxWidth() == 1);
|
||||
|
||||
win.setVisiblePos (Time{Time::MAX - TimeValue(23)});
|
||||
CHECK (win.visible().end() == Time::MAX);
|
||||
CHECK (win.visible().duration() == LIM_HAZARD * 1000 / 2);
|
||||
CHECK (win.px_per_sec() == 2_r/(LIM_HAZARD*1000)*Time::SCALE);
|
||||
CHECK (win.pxWidth() == 1);
|
||||
|
||||
win.setVisibleRange (TimeSpan{Time::MAX - TimeValue(23) // request a window exceeding domain,
|
||||
,FSecs{LIM_HAZARD, 1001}}); // but with a zoom slightly above minimal-zoom
|
||||
CHECK (win.visible().end() == Time::MAX); // Resulting window is shifted into domain
|
||||
CHECK (win.visible().duration() == Duration(FSecs{LIM_HAZARD, 1001})); // and has the requested extension
|
||||
CHECK (win.visible().duration() == TimeValue(1098413214561438));
|
||||
CHECK ( FSecs(LIM_HAZARD, 1000) > FSecs(LIM_HAZARD, 1001)); // which is indeed smaller than the maximum duration
|
||||
CHECK (win.px_per_sec() == 2003_r/2199023255552);
|
||||
CHECK (win.pxWidth() == 1);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -38219,6 +38219,7 @@
|
|||
</node>
|
||||
</node>
|
||||
<node CREATED="1666965004641" ID="ID_748757781" MODIFIED="1666965813264" TEXT="Interaction Procedure">
|
||||
<linktarget COLOR="#955977" DESTINATION="ID_748757781" ENDARROW="Default" ENDINCLINATION="-318;688;" ID="Arrow_ID_1882192055" SOURCE="ID_867107887" STARTARROW="None" STARTINCLINATION="-49;-276;"/>
|
||||
<node CREATED="1666965028506" ID="ID_1718237033" MODIFIED="1666965700555" TEXT="mutators">
|
||||
<icon BUILTIN="full-1"/>
|
||||
<node CREATED="1666965070532" ID="ID_466715389" MODIFIED="1666965073074" TEXT="set metric"/>
|
||||
|
|
@ -38469,9 +38470,34 @@
|
|||
</body>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
<node CREATED="1671324422426" ID="ID_1192793970" MODIFIED="1671324572988">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
Zoom-Metrik verwendet <b>integer-Brüche</b>
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<icon BUILTIN="forward"/>
|
||||
<node COLOR="#435e98" CREATED="1671324446051" ID="ID_1112836860" MODIFIED="1671324565631" TEXT="kann damit auch unebene Teilungen der Zeitskala sauber handhaben">
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1666966669487" ID="ID_1477573565" MODIFIED="1667093086439" TEXT="testgetrieben entwickelt">
|
||||
<icon BUILTIN="pencil"/>
|
||||
<node COLOR="#435e98" CREATED="1671324465573" ID="ID_186471302" MODIFIED="1671324563688" TEXT="erfordert Sicherheitsmaßnahmen für sehr große Zahlen">
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1671324522229" ID="ID_1590349109" MODIFIED="1671324559550" TEXT="Gefahr von numeric-wrap">
|
||||
<icon BUILTIN="clanbomber"/>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1671324535163" ID="ID_298282383" MODIFIED="1671324555746" TEXT="Lösung: re-Quantisierung (minimalen Fehler einführen)">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1666966669487" FOLDED="true" ID="ID_1477573565" MODIFIED="1671324271010" TEXT="testgetrieben entwickelt">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node COLOR="#338800" CREATED="1667093097050" ID="ID_1662709264" MODIFIED="1667093103756" TEXT="verify_simpleUsage">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node CREATED="1667093106907" ID="ID_1255657275" MODIFIED="1667093118644" TEXT="nudge Zoom-Faktor"/>
|
||||
|
|
@ -39562,8 +39588,8 @@
|
|||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1668180004015" ID="ID_1355163433" MODIFIED="1671235929237" TEXT="extreme Grenzfälle abtesten">
|
||||
<icon BUILTIN="pencil"/>
|
||||
<node COLOR="#338800" CREATED="1668180004015" ID="ID_1355163433" MODIFIED="1671324161416" TEXT="extreme Grenzfälle abtesten">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node COLOR="#338800" CREATED="1668212345981" ID="ID_1473341053" MODIFIED="1668212365788" TEXT="leer konstruiert ⟹ default-canvas">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
|
|
@ -41466,8 +41492,8 @@
|
|||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1668180054302" ID="ID_1604190635" MODIFIED="1668180136078" TEXT="1 Pixel">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node COLOR="#338800" CREATED="1668180054302" ID="ID_1604190635" MODIFIED="1671324160042" TEXT="1 Pixel">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node CREATED="1670719662361" ID="ID_1680322812" MODIFIED="1670719685207" TEXT="maximal gefährlich für die Metrik-Berechnung">
|
||||
<node COLOR="#990000" CREATED="1670719706106" ID="ID_266339038" MODIFIED="1670889773325" TEXT="Newton-Optimierung möglicherweise ⟘">
|
||||
<arrowlink COLOR="#7d5c62" DESTINATION="ID_1040949384" ENDARROW="Default" ENDINCLINATION="-541;29;" ID="Arrow_ID_1071041256" STARTARROW="None" STARTINCLINATION="1170;80;"/>
|
||||
|
|
@ -41477,16 +41503,27 @@
|
|||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1671294771824" ID="ID_115927706" MODIFIED="1671294789194" TEXT="numeric-wrap entdeckt">
|
||||
<node COLOR="#435e98" CREATED="1671294771824" ID="ID_115927706" MODIFIED="1671322048434" TEXT="numeric-wrap entdeckt">
|
||||
<icon BUILTIN="broken-line"/>
|
||||
<node CREATED="1671295216805" ID="ID_987376979" MODIFIED="1671295239222" TEXT="In der conversion FSecs ⟼ µ-tick"/>
|
||||
<node CREATED="1671295240330" ID="ID_1742063347" MODIFIED="1671295249934" TEXT="bei übergroßen Werten">
|
||||
<icon BUILTIN="info"/>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1671322050278" ID="ID_1702542630" MODIFIED="1671322066067" TEXT="Limit in den Konverter">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1670719689323" ID="ID_794436463" MODIFIED="1670719702317" TEXT="mit möglichst großen Zeiten kombinieren"/>
|
||||
<node CREATED="1671294759109" ID="ID_531574116" MODIFIED="1671294770708" TEXT="ähnliche Operationen wie bereits für die giftigen Faktoren"/>
|
||||
<node COLOR="#338800" CREATED="1671294759109" ID="ID_531574116" MODIFIED="1671324166659" TEXT="ähnliche Operationen wie bereits für die giftigen Faktoren">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1671324210414" ID="ID_1973605620" MODIFIED="1671324233682" TEXT="sehr groß skalieren und Limit LIM_HAZARD*1000">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1671324243418" ID="ID_596210372" MODIFIED="1671324255033" TEXT="Scroll / Zoom-Nudge">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1668264626100" ID="ID_45091906" MODIFIED="1671235896011" TEXT="giftige Brüche">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
|
|
@ -42830,6 +42867,10 @@
|
|||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1671324283797" ID="ID_867107887" MODIFIED="1671324383048" TEXT="gemäß Requirement-Konzept integrieren">
|
||||
<arrowlink COLOR="#955977" DESTINATION="ID_748757781" ENDARROW="Default" ENDINCLINATION="-318;688;" ID="Arrow_ID_1882192055" STARTARROW="None" STARTINCLINATION="-49;-276;"/>
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1667488193842" ID="ID_1347640673" MODIFIED="1667488208549" TEXT="Invarianten">
|
||||
<font BOLD="true" NAME="SansSerif" SIZE="14"/>
|
||||
|
|
|
|||
Loading…
Reference in a new issue