diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2817,6 +2817,7 @@ 'PhabricatorCoreCreateTransaction' => 'applications/transactions/xaction/PhabricatorCoreCreateTransaction.php', 'PhabricatorCoreTransactionType' => 'applications/transactions/xaction/PhabricatorCoreTransactionType.php', 'PhabricatorCoreVoidTransaction' => 'applications/transactions/xaction/PhabricatorCoreVoidTransaction.php', + 'PhabricatorCosChartFunction' => 'applications/fact/chart/PhabricatorCosChartFunction.php', 'PhabricatorCountFact' => 'applications/fact/fact/PhabricatorCountFact.php', 'PhabricatorCountdown' => 'applications/countdown/storage/PhabricatorCountdown.php', 'PhabricatorCountdownApplication' => 'applications/countdown/application/PhabricatorCountdownApplication.php', @@ -4475,6 +4476,7 @@ 'PhabricatorSSHWorkflow' => 'infrastructure/ssh/PhabricatorSSHWorkflow.php', 'PhabricatorSavedQuery' => 'applications/search/storage/PhabricatorSavedQuery.php', 'PhabricatorSavedQueryQuery' => 'applications/search/query/PhabricatorSavedQueryQuery.php', + 'PhabricatorScaleChartFunction' => 'applications/fact/chart/PhabricatorScaleChartFunction.php', 'PhabricatorScheduleTaskTriggerAction' => 'infrastructure/daemon/workers/action/PhabricatorScheduleTaskTriggerAction.php', 'PhabricatorScopedEnv' => 'infrastructure/env/PhabricatorScopedEnv.php', 'PhabricatorSearchAbstractDocument' => 'applications/search/index/PhabricatorSearchAbstractDocument.php', @@ -4564,6 +4566,7 @@ 'PhabricatorSetupIssue' => 'applications/config/issue/PhabricatorSetupIssue.php', 'PhabricatorSetupIssueUIExample' => 'applications/uiexample/examples/PhabricatorSetupIssueUIExample.php', 'PhabricatorSetupIssueView' => 'applications/config/view/PhabricatorSetupIssueView.php', + 'PhabricatorShiftChartFunction' => 'applications/fact/chart/PhabricatorShiftChartFunction.php', 'PhabricatorShortSite' => 'aphront/site/PhabricatorShortSite.php', 'PhabricatorShowFiletreeSetting' => 'applications/settings/setting/PhabricatorShowFiletreeSetting.php', 'PhabricatorSimpleEditType' => 'applications/transactions/edittype/PhabricatorSimpleEditType.php', @@ -8811,6 +8814,7 @@ 'PhabricatorCoreCreateTransaction' => 'PhabricatorCoreTransactionType', 'PhabricatorCoreTransactionType' => 'PhabricatorModularTransactionType', 'PhabricatorCoreVoidTransaction' => 'PhabricatorModularTransactionType', + 'PhabricatorCosChartFunction' => 'PhabricatorChartFunction', 'PhabricatorCountFact' => 'PhabricatorFact', 'PhabricatorCountdown' => array( 'PhabricatorCountdownDAO', @@ -10775,6 +10779,7 @@ 'PhabricatorPolicyInterface', ), 'PhabricatorSavedQueryQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', + 'PhabricatorScaleChartFunction' => 'PhabricatorChartFunction', 'PhabricatorScheduleTaskTriggerAction' => 'PhabricatorTriggerAction', 'PhabricatorScopedEnv' => 'Phobject', 'PhabricatorSearchAbstractDocument' => 'Phobject', @@ -10864,6 +10869,7 @@ 'PhabricatorSetupIssue' => 'Phobject', 'PhabricatorSetupIssueUIExample' => 'PhabricatorUIExample', 'PhabricatorSetupIssueView' => 'AphrontView', + 'PhabricatorShiftChartFunction' => 'PhabricatorChartFunction', 'PhabricatorShortSite' => 'PhabricatorSite', 'PhabricatorShowFiletreeSetting' => 'PhabricatorSelectSetting', 'PhabricatorSimpleEditType' => 'PhabricatorEditType', diff --git a/src/applications/fact/chart/PhabricatorChartFunction.php b/src/applications/fact/chart/PhabricatorChartFunction.php --- a/src/applications/fact/chart/PhabricatorChartFunction.php +++ b/src/applications/fact/chart/PhabricatorChartFunction.php @@ -5,9 +5,9 @@ private $xAxis; private $yAxis; - private $limit; private $argumentParser; + private $sourceFunction; final public function getFunctionKey() { return $this->getPhobjectClassConstant('FUNCTIONKEY', 32); @@ -44,6 +44,12 @@ $parser->setHaveAllArguments(true); $parser->parseArguments(); + $source_argument = $parser->getSourceFunctionArgument(); + if ($source_argument) { + $source_function = $this->getArgument($source_argument->getName()); + $this->setSourceFunction($source_function); + } + return $this; } @@ -71,6 +77,15 @@ return; } + protected function setSourceFunction(PhabricatorChartFunction $source) { + $this->sourceFunction = $source; + return $this; + } + + protected function getSourceFunction() { + return $this->sourceFunction; + } + final public function setXAxis(PhabricatorChartAxis $x_axis) { $this->xAxis = $x_axis; return $this; @@ -89,6 +104,67 @@ return $this->yAxis; } + protected function canEvaluateFunction() { + return false; + } + + protected function evaluateFunction($x) { + throw new PhutilMethodNotImplementedException(); + } + + public function hasDomain() { + if ($this->canEvaluateFunction()) { + return false; + } + + throw new PhutilMethodNotImplementedException(); + } + + public function getDatapoints(PhabricatorChartDataQuery $query) { + if ($this->canEvaluateFunction()) { + $points = $this->newSourceDatapoints($query); + foreach ($points as $key => $point) { + $y = $point['y']; + $y = $this->evaluateFunction($y); + $points[$key]['y'] = $y; + } + + return $points; + } + + return $this->newDatapoints($query); + } + + protected function newDatapoints(PhabricatorChartDataQuery $query) { + throw new PhutilMethodNotImplementedException(); + } + + protected function newSourceDatapoints(PhabricatorChartDataQuery $query) { + $source = $this->getSourceFunction(); + if ($source) { + return $source->getDatapoints($query); + } + + return $this->newDefaultDatapoints($query); + } + + protected function newDefaultDatapoints(PhabricatorChartDataQuery $query) { + $x_min = $query->getMinimumValue(); + $x_max = $query->getMaximumValue(); + $limit = $query->getLimit(); + + $points = array(); + $steps = $this->newLinearSteps($x_min, $x_max, $limit); + foreach ($steps as $step) { + $points[] = array( + 'x' => $step, + 'y' => $step, + ); + } + + return $points; + } + protected function newLinearSteps($src, $dst, $count) { $count = (int)$count; $src = (int)$src; diff --git a/src/applications/fact/chart/PhabricatorChartFunctionArgument.php b/src/applications/fact/chart/PhabricatorChartFunctionArgument.php --- a/src/applications/fact/chart/PhabricatorChartFunctionArgument.php +++ b/src/applications/fact/chart/PhabricatorChartFunctionArgument.php @@ -5,6 +5,7 @@ private $name; private $type; + private $isSourceFunction; public function setName($name) { $this->name = $name; @@ -39,6 +40,15 @@ return $this->type; } + public function setIsSourceFunction($is_source_function) { + $this->isSourceFunction = $is_source_function; + return $this; + } + + public function getIsSourceFunction() { + return $this->isSourceFunction; + } + public function newValue($value) { switch ($this->getType()) { case 'fact-key': diff --git a/src/applications/fact/chart/PhabricatorChartFunctionArgumentParser.php b/src/applications/fact/chart/PhabricatorChartFunctionArgumentParser.php --- a/src/applications/fact/chart/PhabricatorChartFunctionArgumentParser.php +++ b/src/applications/fact/chart/PhabricatorChartFunctionArgumentParser.php @@ -151,4 +151,43 @@ implode(', ', $argument_list)); } + public function getSourceFunctionArgument() { + $required_type = 'function'; + + $sources = array(); + foreach ($this->argumentMap as $key => $argument) { + if (!$argument->getIsSourceFunction()) { + continue; + } + + if ($argument->getType() !== $required_type) { + throw new Exception( + pht( + 'Function "%s" defines an argument "%s" which is marked as a '. + 'source function, but the type of this argument is not "%s".', + $this->getFunctionArgumentSignature(), + $argument->getName(), + $required_type)); + } + + $sources[$key] = $argument; + } + + if (!$sources) { + return null; + } + + if (count($sources) > 1) { + throw new Exception( + pht( + 'Function "%s" defines more than one argument as a source '. + 'function (arguments: %s). Functions must have zero or one '. + 'source function.', + $this->getFunctionArgumentSignature(), + implode(', ', array_keys($sources)))); + } + + return head($sources); + } + } diff --git a/src/applications/fact/chart/PhabricatorConstantChartFunction.php b/src/applications/fact/chart/PhabricatorConstantChartFunction.php --- a/src/applications/fact/chart/PhabricatorConstantChartFunction.php +++ b/src/applications/fact/chart/PhabricatorConstantChartFunction.php @@ -5,8 +5,6 @@ const FUNCTIONKEY = 'constant'; - private $value; - protected function newArguments() { return array( $this->newArgument() @@ -15,26 +13,12 @@ ); } - public function getDatapoints(PhabricatorChartDataQuery $query) { - $x_min = $query->getMinimumValue(); - $x_max = $query->getMaximumValue(); - - $value = $this->getArgument('n'); - - $points = array(); - $steps = $this->newLinearSteps($x_min, $x_max, 2); - foreach ($steps as $step) { - $points[] = array( - 'x' => $step, - 'y' => $value, - ); - } - - return $points; + protected function canEvaluateFunction() { + return true; } - public function hasDomain() { - return false; + protected function evaluateFunction($x) { + return $this->getArgument('n'); } } diff --git a/src/applications/fact/chart/PhabricatorCosChartFunction.php b/src/applications/fact/chart/PhabricatorCosChartFunction.php new file mode 100644 --- /dev/null +++ b/src/applications/fact/chart/PhabricatorCosChartFunction.php @@ -0,0 +1,25 @@ +newArgument() + ->setName('x') + ->setType('function') + ->setIsSourceFunction(true), + ); + } + + protected function canEvaluateFunction() { + return true; + } + + protected function evaluateFunction($x) { + return cos(deg2rad($x)); + } + +} diff --git a/src/applications/fact/chart/PhabricatorScaleChartFunction.php b/src/applications/fact/chart/PhabricatorScaleChartFunction.php new file mode 100644 --- /dev/null +++ b/src/applications/fact/chart/PhabricatorScaleChartFunction.php @@ -0,0 +1,28 @@ +newArgument() + ->setName('x') + ->setType('function') + ->setIsSourceFunction(true), + $this->newArgument() + ->setName('scale') + ->setType('number'), + ); + } + + protected function canEvaluateFunction() { + return true; + } + + protected function evaluateFunction($x) { + return $x * $this->getArgument('scale'); + } + +} diff --git a/src/applications/fact/chart/PhabricatorShiftChartFunction.php b/src/applications/fact/chart/PhabricatorShiftChartFunction.php new file mode 100644 --- /dev/null +++ b/src/applications/fact/chart/PhabricatorShiftChartFunction.php @@ -0,0 +1,28 @@ +newArgument() + ->setName('x') + ->setType('function') + ->setIsSourceFunction(true), + $this->newArgument() + ->setName('shift') + ->setType('number'), + ); + } + + protected function canEvaluateFunction() { + return true; + } + + protected function evaluateFunction($x) { + return $x * $this->getArgument('shift'); + } + +} diff --git a/src/applications/fact/chart/PhabricatorSinChartFunction.php b/src/applications/fact/chart/PhabricatorSinChartFunction.php --- a/src/applications/fact/chart/PhabricatorSinChartFunction.php +++ b/src/applications/fact/chart/PhabricatorSinChartFunction.php @@ -9,26 +9,17 @@ return array( $this->newArgument() ->setName('x') - ->setType('function'), + ->setType('function') + ->setIsSourceFunction(true), ); } - protected function assignArguments(array $arguments) { - $this->argument = $arguments[0]; + protected function canEvaluateFunction() { + return true; } - public function getDatapoints(PhabricatorChartDataQuery $query) { - $points = $this->getArgument('x')->getDatapoints($query); - - foreach ($points as $key => $point) { - $points[$key]['y'] = sin(deg2rad($points[$key]['y'])); - } - - return $points; - } - - public function hasDomain() { - return false; + protected function evaluateFunction($x) { + return sin(deg2rad($x)); } } diff --git a/src/applications/fact/chart/PhabricatorXChartFunction.php b/src/applications/fact/chart/PhabricatorXChartFunction.php --- a/src/applications/fact/chart/PhabricatorXChartFunction.php +++ b/src/applications/fact/chart/PhabricatorXChartFunction.php @@ -9,25 +9,12 @@ return array(); } - public function getDatapoints(PhabricatorChartDataQuery $query) { - $x_min = $query->getMinimumValue(); - $x_max = $query->getMaximumValue(); - $limit = $query->getLimit(); - - $points = array(); - $steps = $this->newLinearSteps($x_min, $x_max, $limit); - foreach ($steps as $step) { - $points[] = array( - 'x' => $step, - 'y' => $step, - ); - } - - return $points; + protected function canEvaluateFunction() { + return true; } - public function hasDomain() { - return false; + protected function evaluateFunction($x) { + return $x; } } diff --git a/src/applications/fact/controller/PhabricatorFactChartController.php b/src/applications/fact/controller/PhabricatorFactChartController.php --- a/src/applications/fact/controller/PhabricatorFactChartController.php +++ b/src/applications/fact/controller/PhabricatorFactChartController.php @@ -29,6 +29,27 @@ $functions[] = id(new PhabricatorSinChartFunction()) ->setArguments(array($x_function)); + $cos_function = id(new PhabricatorCosChartFunction()) + ->setArguments(array($x_function)); + + $functions[] = id(new PhabricatorShiftChartFunction()) + ->setArguments( + array( + array( + 'scale', + array( + 'cos', + array( + 'scale', + array('x'), + 0.001, + ), + ), + 10, + ), + 200, + )); + list($domain_min, $domain_max) = $this->getDomain($functions); $axis = id(new PhabricatorChartAxis()) @@ -83,6 +104,9 @@ 'yMax' => $y_max, ); + // TODO: Move this back up, it's just down here for now to make + // debugging easier so the main page throws a more visible exception when + // something goes wrong. if ($is_chart_mode) { return $this->newChartResponse(); }